Virtual Lists
For lists with thousands of items, rendering everything at once is too slow. virtualEach only renders the items currently visible in the viewport, plus a small buffer above and below.
Basic Usage
import { virtualEach } from "@effex/dom";
virtualEach(items, {
key: (item) => item.id,
itemHeight: 48,
height: 400,
render: (item) =>
$.li({}, $.of(item.pipe(Readable.map((i) => i.text)))),
});
The itemHeight and height are in pixels. itemHeight is the height of each row, and height is the height of the scrollable container. Items outside the visible range are not mounted at all — they’re created on scroll and cleaned up when they leave the viewport.
Scroll Control
Use VirtualListRef to programmatically control scrolling:
import { virtualEach, VirtualListRef } from "@effex/dom";
const listRef = yield* VirtualListRef.make();
yield* virtualEach(items, {
key: (item) => item.id,
itemHeight: 60,
height: 400,
overscan: 5, // Render 5 extra items above/below viewport
ref: listRef,
render: (item, index) => ListItem({ item, index }),
});
// Scroll to item 100
yield* listRef.ready.pipe(
Effect.flatMap((control) => control.scrollTo(100)),
);
Control API
Once the list is ready, the control object provides:
control.scrollTo(index); // Scroll to a specific item
control.scrollToTop(); // Scroll to the top
control.scrollToBottom(); // Scroll to the bottom
control.visibleRange; // Readable<{ start: number, end: number }>
control.totalItems; // Readable<number>
Options
| Option | Type | Description |
|---|---|---|
key |
(item) => string | number |
Unique key for each item |
itemHeight |
number |
Height of each row in pixels |
height |
number |
Height of the scrollable container |
overscan |
number |
Extra items to render outside the viewport (default: 3) |
ref |
VirtualListRef |
Ref for scroll control |
render |
(item, index) => Element |
Render function for each item |
The overscan option controls how many extra items are rendered above and below the visible area. Higher values reduce the chance of seeing blank space during fast scrolling, at the cost of rendering more items.