VirtualList

The VirtualList component efficiently renders large lists by only rendering visible items. It uses virtualization to maintain performance regardless of list size.

Props

PropTypeDefaultDescription
itemsT[]-Array of items to render
itemHeightnumber | 'dynamic'-Fixed height in pixels, or 'dynamic' for variable height items
estimatedItemHeightnumber40Estimated height for dynamic mode initial calculation
heightnumber | string'100%'Container height
overscannumber3Number 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
classNamestring-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>
  );
}