A directive for Angular framework to provide unlimited bidirectional virtual scrolling over limited viewport. Built on top of vscroll native virtual scrolling engine. Demo is available at dhilt.github.io/ngx-ui-scroll.
can donate? go here 👉
make open-source world better
Scrolling large datasets may cause performance issues. Many DOM elements, many data-bindings, many event listeners... The common way to improve the performance is to render only a small portion of the dataset visible to a user. Other dataset elements that are not visible to a user are virtualized with upward and downward empty padding elements which should provide a consistent viewport with consistent scrollbar parameters.
The ngx-ui-scroll library provides the *uiScroll structural directive that works like *ngFor and renders a templated element once per item from a collection. By requesting the external Datasource (the implementation of which is a developer responsibility) the *uiScroll directive fetches necessary portion of the dataset and renders corresponded elements until the visible part of the viewport is filled out. It starts to retrieve new data to render new elements again if a user scrolls to the edge of visible element list. It dynamically destroys elements as they become invisible and recreates them if they become visible again.

The *uiScroll directive is a part of UiScrollModule which is available via npm –
npm install ngx-ui-scroll vscroll
The UiScrollModule has to be imported in the App/feature module where it is going to be used.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UiScrollModule } from 'ngx-ui-scroll';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, UiScrollModule],
bootstrap: [AppComponent]
})
export class AppModule {}
Standalone component usage (Angular 17+):
import { Component } from '@angular/core';
import { UiScrollModule } from 'ngx-ui-scroll';
@Component({
selector: 'app-my-component',
standalone: true,
imports: [UiScrollModule],
template: `
`
})
export class MyComponent {}
Basic usage template may look like
<b>{{item.text}}</b>
where the viewport is a scrollable area of finite height:
.viewport {
height: 300px;
overflow-y: auto;
}
If the height of the viewport is not constrained, it will pull the entire content of the datasource and no scrollbar will appear.
*uiScroll acts like *ngFor in its simplest form, where the datasource is an object of special type (IDatasource), which implements the get method used by the *uiScroll directive to access data by index and count parameters. The directive calls the Datasource.get method each time the user scrolls the list of visible elements to the edge.
import { IDatasource } from 'ngx-ui-scroll';
@Component({ ... })
export class AppComponent {
datasource: IDatasource = {
get: (index, count, success) => {
const data = [];
for (let i = index; i <= index + count - 1; i++) {
data.push({ text: 'item #' + i });
}
success(data);
}
};
}
Datasource.get method must provide an array of count data-items started from index position. If there are no items within given range [index; index + count - 1], an empty array has to be passed. Empty result (or result whose length is less than count) is being treated as the edge of the dataset (eof/bof), and no further requests for preceding/following items will be issued.
Datasource.get has 3 signatures: callback based, Promise based and Observable based. So, if we want some remote API to be a source of our data, basically it may look like
datasource: IDatasource = {
get: (index, count) =>
this.http.get(`${myApiUrl}?index=${index}&count=${count}`)
};
More details could be found on the Datasource demo page.
Datasource implementation along with get method property may include settings object property:
datasource: IDatasource = {
get: ...,
settings: {
minIndex: 0,
startIndex: 0,
...
}
};
Settings are being applied during the Scroller initialization and have an impact on how the Scroller behaves. Below is the list of available settings with descriptions, defaults, types and demos.
| Name | Type | Default | Description |
|---|---|---|---|
| bufferSize | number, |
integer | 5 | Fixes minimal size of the pack of the datasource items to be requested per single Datasource.get call. Can't be less than 1. | | padding | number,
float | 0.5 | Determines the viewport outlets containing real but not visible items. The value is relative to the viewport's size. For example, 0.25 means that there will be as many items at a moment as needed to fill out 100% of the visible part of the viewport, + 25% of the viewport size in the backward direction and + 25% in the forward direction. The value can't be less than 0.01. | | startIndex | number,
integer | 1 | Specifies item index to be requested/rendered first. Can be any, but the real datasource boundaries should be taken into account. | | minIndex | number,
integer | -Infinity | Fixes absolute minimal index of the dataset. The datasource left boundary. | | maxIndex | number,
integer | +Infinity | Fixes absolute maximal index of the dataset. The datasource right boundary. | | infinite | boolean | false | Enables "infinite" mode, when items rendered once are never removed. | | horizontal | boolean | false | Enables "horizontal" mode, when the viewport's orientation is horizontal. | | sizeStrategy | string enum, 'average' | 'frequent' | 'constant' | 'average' | Defines how the default item size is calculated. If item has never been rendered, its size is assumed to be the default size: an average or most frequent among all items that have been rendered before, or constant. This has an impact on the process of virtualization. | | windowViewport | boolean | false | Enables "entire window scrollable" mode, when the entire window becomes the scrollable viewport. |
The Scroller has API to assess its parameters and provide some manipulations at runtime. This API is available via special Adapter object. The datasource needs to be instantiated via operator "new" for the Adapter object to be added to it:
import { Datasource } from 'ngx-ui-scroll';
...
datasource = new Datasource({
get: ... ,
settings: { ... }
});
Then this.datasource.adapter.packageInfo, this.datasource.adapter.reload() and other Adapter expressions become available. For better typing, it is recommended to specify the Datasource Item type as follows:
import { Datasource } from 'ngx-ui-scroll';
...
datasource = new Datasource<MyItem>({
get: ... ,
settings: { ... }
});
MyItem should reflect the structure of items that the Datasource will deal with. It is "unknown" by default, and if not set, for example, this.datasource.adapter.firstVisible.data.id expression will produce typescript error: Object is of type 'unknown'. There are some Adapter props and methods dealing with MyItem, and if used, MyItem should be specified.
Below is the list of read-only properties of the Adapter API with descriptions and links to demos.
| Name | Type | Description
$ claude mcp add ngx-ui-scroll \
-- python -m otcore.mcp_server <graph>