VirtualList
The VirtualList component efficiently renders large lists by only rendering visible items. It uses virtualization to maintain performance regardless of list size.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | - | Array of items to render |
itemHeight | number | 'dynamic' | - | Fixed height in pixels, or 'dynamic' for variable height items |
estimatedItemHeight | number | 40 | Estimated height for dynamic mode initial calculation |
height | number | string | '100%' | Container height |
overscan | number | 3 | Number of extra items to render outside visible area |
getItemKey | (item: T, index: number) => string | number | - | Function to get a unique key for each item |
children | (item: T, index: number, style: CSSProperties) => ReactNode | - | Render function for each item |
onScroll | (scrollTop: number) => void | - | Callback when scroll position changes |
onRangeChange | (startIndex: number, endIndex: number) => void | - | Callback when visible range changes |
className | string | - | Additional className for the container |
Basic Usage
A simple list with fixed height items.
Item 1
This is the description for item 1
Item 2
This is the description for item 2
Item 3
This is the description for item 3
Item 4
This is the description for item 4
const items = Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `Item ${i + 1}`,
description: `This is the description for item ${i + 1}`
}));
<VirtualList
items={items}
itemHeight={48}
height={250}
getItemKey={(item) => item.id}
className="border border-line-primary rounded-uk-md"
>
{(item, index, style) => (
<div
style={style}
className="flex items-center px-4 border-b border-line-primary hover:bg-bg-secondary"
>
<div className="flex-1">
<div className="font-medium">{item.name}</div>
<div className="text-uk-sm text-text-secondary">{item.description}</div>
</div>
</div>
)}
</VirtualList>
Dynamic Height Items
For items with variable heights, use itemHeight="dynamic". The component will measure each item's height using ResizeObserver.
Message 1
This is a short message.
Message 2
This is a medium length message that spans multiple lines to demonstrate variable height rendering in the virtual list component.
Message 3
This is a longer message that contains much more content. It demonstrates how the VirtualList component handles items of varying heights efficiently. The component measures each item and calculates proper positions for smooth scrolling.
Message 4
This is a short message.
const items = Array.from({ length: 500 }, (_, i) => ({
id: i,
title: `Message ${i + 1}`,
content: i % 3 === 0
? `This is a short message.`
: i % 3 === 1
? `This is a medium length message...`
: `This is a longer message...`
}));
<VirtualList
items={items}
itemHeight="dynamic"
estimatedItemHeight={60}
height={300}
getItemKey={(item) => item.id}
>
{(item, index, style) => (
<div style={style} className="p-4 border-b border-line-primary">
<div className="font-medium text-brand-primary">{item.title}</div>
<div className="mt-1 text-uk-sm text-text-secondary">{item.content}</div>
</div>
)}
</VirtualList>
With Range Change Callback
Track which items are currently visible using the onRangeChange callback.
Visible range: 0 - 0 (of 10000 items)
Row 1
Row 2
Row 3
Row 4
const [range, setRange] = React.useState({ start: 0, end: 0 });
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Row ${i + 1}`
}));
<div className="flex flex-col gap-2">
<div className="px-2 py-1 bg-bg-secondary rounded-uk-md text-uk-sm">
Visible range: {range.start} - {range.end} (of {items.length} items)
</div>
<VirtualList
items={items}
itemHeight={32}
height={250}
getItemKey={(item) => item.id}
onRangeChange={(start, end) => setRange({ start, end })}
>
{(item, index, style) => (
<div
style={style}
className="flex items-center h-[32px] px-4 border-b"
>
{item.name}
</div>
)}
</VirtualList>
</div>
File List Example
A practical example showing a file list with icons.
.tsfile-1.ts8KB
.tsxfile-2.tsx64KB
.jsfile-3.js9KB
.jsonfile-4.json23KB
const files = Array.from({ length: 500 }, (_, i) => ({
id: i,
name: `file-${i + 1}.ts`,
path: `/src/file-${i + 1}.ts`,
size: Math.floor(Math.random() * 100) + 1,
type: 'ts'
}));
<VirtualList
items={files}
itemHeight={32}
height={300}
getItemKey={(item) => item.id}
>
{(item, index, style) => (
<div
style={style}
className="flex items-center h-[32px] px-3 gap-2 border-b hover:bg-bg-secondary"
>
<span className="text-uk-sm text-blue-400">.{item.type}</span>
<span className="flex-1 truncate">{item.name}</span>
<span className="text-uk-sm text-text-tertiary">{item.size}KB</span>
</div>
)}
</VirtualList>
Types
interface VirtualListProps<T> {
items: T[];
itemHeight: number | 'dynamic';
estimatedItemHeight?: number;
height?: number | string;
overscan?: number;
getItemKey?: (item: T, index: number) => string | number;
children: (item: T, index: number, style: CSSProperties) => ReactNode;
onScroll?: (scrollTop: number) => void;
onRangeChange?: (startIndex: number, endIndex: number) => void;
className?: string;
}
Hook: useVirtualList
The useVirtualList hook can be used to build custom virtualized list implementations.
import { useVirtualList } from '@vuer-ai/vuer-uikit';
const { visibleRange, totalHeight, getItemStyle, measureItem } = useVirtualList({
items,
itemHeight: 40,
estimatedItemHeight: 40,
containerHeight: 400,
overscan: 3,
scrollTop: 0,
});
Usage
import { VirtualList } from '@vuer-ai/vuer-uikit';
function MyComponent() {
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i,
label: `Item ${i + 1}`
}));
return (
<VirtualList
items={items}
itemHeight={40}
height={400}
getItemKey={(item) => item.id}
>
{(item, index, style) => (
<div style={style} className="p-2">
{item.label}
</div>
)}
</VirtualList>
);
}