A framework-independent virtual scrolling engine for JavaScript and TypeScript.
Virtual scrolling is a technique for displaying large lists efficiently. Instead of rendering every item at once, it keeps a small set of items in the DOM — those in and around the visible area — and updates that set as the user scrolls. This reduces DOM size and rendering work while preserving a familiar scrolling experience.
VScroll provides a framework-independent core engine for virtual scrolling. An application can use it directly or through a platform-specific wrapper called a consumer. The diagram shows how the engine reaches the end user when a consumer is used.
The minimal browser demo demonstrates direct use of VScroll without a separate consumer.
Existing consumers and integration examples include:
- ngx-ui-scroll — an Angular virtual scrolling directive.
- vscroll-native — a virtual scrolling module for native JavaScript applications.
- Vue integration sample — an example of using VScroll in Vue.
Load the library in a browser and access its exports through VScroll:
<script src="https://cdn.jsdelivr.net/npm/vscroll"></script>
<script>
new VScroll.Workflow(...);
</script>For reproducible deployments, pin the CDN URL to a package version.
npm install vscrollImport the library in the application's build:
import * as VScroll from 'vscroll';
new VScroll.Workflow(...);A vscroll consumer is responsible for the integration: supplying data when requested by the engine and rendering the current buffer in the DOM. The engine manages scrolling, determines which items are needed, and updates the buffer; the consumer defines how data is retrieved and displayed. This integration is configured when creating Workflow, the main entry point to the engine.
The Workflow class, exported by vscroll, is where the integration is configured. Instantiating it starts the engine. See the Workflow reference for the requirements behind its constructor parameters.
const workflow = new VScroll.Workflow({ consumer, element, datasource, run, Routines });| Parameter | Purpose |
|---|---|
consumer |
Static integration metadata (name and version), used in diagnostics. |
element |
The mounted DOM element containing the rendered list, not the scrollable viewport. |
datasource |
The object that supplies data and scrolling settings, described below. See also Datasource. |
run(items) |
The callback that keeps the rendered list in sync with the complete current buffer, including offscreen items. See Rendering. |
Routines |
Optional subclass of VScroll.Routines for customizing DOM operations and render scheduling. See Custom Routines. |
Every Workflow requires a datasource object to supply items on request and, optionally, configure scrolling. Its data and configuration fields are { get, settings, devSettings }. See the Datasource reference for the full contract, supported signatures, and implementation examples.
-
getis the required data retrieval function, called with a starting index and item count. It can be synchronous or asynchronous. A minimal callback example providing a synchronous, infinite data stream:const get = (index, count, callback) => callback(Array.from({ length: count }, (_, i) => `Item ${index + i}`));
-
settingsis an optional object for configuring scrolling. The table below summarizes its options and defaults. See Configuration for types, constraints and examples.Setting Default Purpose startIndex1Initial item index, clamped to the configured bounds. minIndex-InfinityInclusive lower dataset index bound. maxIndexInfinityInclusive upper dataset index bound. padding0.5Extra buffered area on each side, in viewport sizes. bufferSize5Minimum fetch batch target, not a limit on buffered items. itemSizeNaNInitial item-size estimate in pixels; measured automatically when omitted. sizeStrategy'average'Estimate unknown item sizes using 'average','frequent'or'constant'.viewportElementnullCustom viewport element or element factory; defaults to the content element's parent. Experimental. windowViewportfalseUse the browser window as the viewport. horizontalfalseScroll horizontally instead of vertically. inversefalseAlign short content to the bottom or right without reversing item order. Experimental. infinitefalseKeep loaded items instead of clipping them automatically. onBeforeClipnullReceive clipped items just before they leave the buffer. Experimental. -
devSettingsis an optional object for logging, timing, caching and scroll behavior. See Development settings for its options and defaults.
The Adapter API extends the scrolling engine with reactive state observation and runtime control. It provides access to loading state, visible items and dataset boundaries, supports adding, removing and updating items or reloading data, and enables synchronization of application actions with scroller activity. These capabilities support interactive interfaces such as chats, live feeds and editable lists, where content evolves in response to incoming data and user actions.
The Adapter API is available when a datasource is created through the makeDatasource factory exported by vscroll.
const Datasource = VScroll.makeDatasource();
const datasource = new Datasource({ get, settings });
const adapter = datasource.adapter;
// Reload data when the refresh button is clicked.
refreshButton.addEventListener('click', () => adapter.reload());
// Log loading state changes.
adapter.isLoading$.on(isLoading => console.log('Loading:', isLoading));The Adapter is created when the datasource is instantiated. Its reactive properties can be observed before constructing Workflow, but method calls have no effect until Workflow finishes initializing. See Calling Adapter methods.
makeDatasource also accepts an optional configuration factory for customizing the Adapter's reactive properties. See Custom Adapter reactivity.
The tables below provide a brief overview of the Adapter's properties and methods. See Adapter properties and Adapter methods for details, and the ngx-ui-scroll Adapter demos for interactive examples.
Properties are read-only. Each $ counterpart provides reactive updates.
| Property | Purpose |
|---|---|
init, init$ |
Whether the Adapter is initialized. |
isLoading, isLoading$ |
Whether a workflow cycle is running, including fetching and rendering. |
loopPending, loopPending$ |
Whether an inner workflow loop is running. |
paused, paused$ |
Whether workflow processing is paused. |
bufferInfo |
Buffer, cache and dataset index bounds, plus the estimated item size. |
itemsCount |
Number of rendered buffer items, including offscreen items. |
firstVisible, firstVisible$ |
First item intersecting the viewport, including a partially visible item. |
lastVisible, lastVisible$ |
Last item intersecting the viewport, including a partially visible item. |
bof, bof$ |
Whether the buffer has reached the dataset's beginning. |
eof, eof$ |
Whether the buffer has reached the dataset's end. |
packageInfo |
Core and consumer package names and versions. |
| Method | Purpose |
|---|---|
relax |
Wait until the scroller is idle. |
reload |
Reload data at an optional starting index, keeping the current configuration. |
reset |
Restart the scroller with optional datasource and settings changes. |
pause, resume |
Suspend or resume workflow processing. |
append, prepend |
Add items after or before the known range. |
insert |
Insert items before or after a target item. |
remove |
Remove selected items by predicate or indexes. |
replace |
Replace matching buffered items with a new set of items. |
update |
Keep, remove or replace buffered items using a callback. |
check |
Re-measure rendered items after their sizes change. |
clip |
Trim offscreen buffer items beyond the configured padding. |
fix |
Directly adjust scroll position, index bounds or items. Experimental. |
showLog |
Print collected debug logs. |
See the documentation index for a guided path through the reference pages below.
-
Core integration
- Virtual scrolling model — understand how the viewport, item buffer and DOM rows fit together.
- Workflow and lifecycle — construct, dispose and recreate an integration.
- Datasource — provide data, handle failures and cache items.
- Rendering — implement the consumer's DOM and rendering contract.
-
Configuration and extensions
- Configuration — configure sizing, buffering, scrolling and diagnostics.
- Adapter properties — inspect workflow state and visible items.
- Adapter methods — control the scroller and modify buffered items.
- Custom Routines — customize DOM operations and scheduling.
-
Help
- Troubleshooting — diagnose integration problems and use debug logs.
- To Mike Feingold, who started this project family in 2013.
- To Joshua Toenyes, who transferred ownership of the vscroll npm package name.
- To all contributors to ui-scroll and ngx-ui-scroll.
- To everyone supporting the project through donations.
2026 © Denis Hilt · MIT license
