Skip to main content

Virtual List

High-performance virtual list hook for React. Efficiently renders large lists by only rendering visible items.

Installation

npm install @windowing-kit/virtual-list
# or
pnpm add @windowing-kit/virtual-list
# or
yarn add @windowing-kit/virtual-list

Quick Start

import { useVirtualList } from "@windowing-kit/virtual-list";

function MyList() {
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
}));

const { containerRef, listHeight, virtualItems } = useVirtualList({
itemHeight: 50,
items,
overscan: 3,
});

return (
<div
ref={containerRef}
style={{
height: "400px",
overflow: "auto",
}}
>
<div style={{ height: listHeight, position: "relative" }}>
{virtualItems.map((virtualItem) => (
<div
key={virtualItem.index}
style={{
position: "absolute",
top: virtualItem.offsetY,
height: 50,
width: "100%",
}}
>
{virtualItem.data.name}
</div>
))}
</div>
</div>
);
}

API

useVirtualList<T>(options)

Virtual list hook.

Options

PropertyTypeRequiredDefaultDescription
itemHeightnumberYes-Height of each item in pixels
itemsT[]Yes-Array of items to render
overscannumberNo3Number of items to render outside the viewport

Return

PropertyTypeDescription
containerRef(element: HTMLElement | null) => voidRef callback to attach to the scroll container
listHeightnumberTotal height of the list in pixels
virtualItemsVirtualItem<T>[]Array of virtual items currently visible in the viewport

VirtualItem<T>

Each virtual item has the following properties:

PropertyTypeDescription
indexnumberIndex in the original array
offsetYnumberY offset in pixels within the list
dataTOriginal item data

Examples

Basic Usage

import { useVirtualList } from "@windowing-kit/virtual-list";

function TodoList({ todos }) {
const { containerRef, listHeight, virtualItems } = useVirtualList({
itemHeight: 60,
items: todos,
overscan: 5,
});

return (
<div ref={containerRef} style={{ height: "500px", overflow: "auto" }}>
<div style={{ height: listHeight, position: "relative" }}>
{virtualItems.map((virtualItem) => (
<div
key={virtualItem.index}
style={{
position: "absolute",
top: virtualItem.offsetY,
height: 60,
width: "100%",
padding: "10px",
borderBottom: "1px solid #eee",
}}
>
{virtualItem.data.title}
</div>
))}
</div>
</div>
);
}

With Custom Styling

function StyledList({ items }) {
const { containerRef, listHeight, virtualItems } = useVirtualList({
itemHeight: 80,
items,
});

return (
<div
ref={containerRef}
className="scroll-container"
style={{ height: "600px", overflow: "auto" }}
>
<div style={{ height: listHeight, position: "relative" }}>
{virtualItems.map((virtualItem) => (
<div
key={virtualItem.index}
className="list-item"
style={{
position: "absolute",
top: virtualItem.offsetY,
height: 80,
width: "100%",
}}
>
<h3>{virtualItem.data.title}</h3>
<p>{virtualItem.data.description}</p>
</div>
))}
</div>
</div>
);
}