NUIv3.0.7

Virtual List

A composable, accessible VirtualList component for modern React applications.

tsx
import { VirtualList } from '@nofinite/nui';

Interactive Preview

VirtualList (High performance rendering)

A high-performance scrollable list component that efficiently renders only visible rows. Supports dynamic row virtualization, custom item rendering, fixed row heights, and large datasets without performance degradation.


Usage

Basic usage

tsx
import { VirtualList } from '@nofinite/nui';

const items = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`);

<VirtualList
  items={items}
  height={400}
  renderItem={(item) => <div>{item}</div>}
/>;

With custom row height

tsx
<VirtualList
  items={items}
  height={400}
  itemHeight={60}
  renderItem={(item) => <div>{item}</div>}
/>

With overscan for smoother scrolling

tsx
<VirtualList
  items={items}
  height={400}
  overscan={5}
  renderItem={(item) => <div>{item}</div>}
/>

Using key extractor

tsx
<VirtualList
  items={items}
  height={400}
  keyExtractor={(item, index) => `key-${index}`}
  renderItem={(item) => <div>{item}</div>}
/>


Variants

Large datasets

tsx
const items = Array.from({ length: 10000 }, (_, i) => `Row ${i + 1}`);

<VirtualList
  items={items}
  height={500}
  renderItem={(item) => <div>{item}</div>}
/>;

Custom content per row

tsx
<VirtualList
  items={items}
  height={400}
  renderItem={(item) => (
    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
      <span>{item}</span>
      <button>Edit</button>
    </div>
  )}
/>

Available variants

  • Fixed row height (default 40px)
  • Custom row heights (via itemHeight)
  • Overscan for smooth scrolling
  • Key extractor for dynamic items
  • Render any JSX per row

Guidelines

  • Use for large datasets where full DOM rendering is inefficient
  • Always provide keyExtractor when using dynamic items
  • Keep itemHeight consistent for best performance
  • Use overscan to prevent flickering when scrolling

States

  • Default (visible portion rendered)
  • Hover (row highlight)
  • Scroll (virtualization offsets applied)

Keyboard Interaction

  • Standard browser scrolling applies
  • Focusable elements inside rows can receive keyboard events
  • Container supports tab navigation if interactive elements exist inside rows

Accessibility

  • role="list" on container
  • role="listitem" on each row
  • Fully scrollable via keyboard or pointer
  • Supports focusable interactive content inside rows
  • Uses semantic roles for screen readers

ze | | --fg-default | Row text color | | --bg-subtle | Row hover background | | --space-4 | Row horizontal padding |


Best Practices

Do

  • Use for very large lists (thousands of rows)
  • Keep row heights consistent for optimal virtualization
  • Provide keyExtractor for dynamic or filtered items
  • Combine with interactive row content safely
  • Use overscan to prevent flickering

Don’t

  • Use for very small lists where regular mapping is sufficient
  • Mix variable heights unless properly calculated
  • Overload rows with heavy components causing render jank
  • Forget to provide unique keys for dynamic items
  • Remove scrollable container or overflow properties

API Reference

PropTypeDefaultDescription
items*T[]Array of data items to render
heightnumberHeight of the scrollable viewport in pixels. If omitted, it will automatically size to its container.
itemHeightnumberFixed height for each row in pixels. Defaults to 40.
overscannumberExtra rows to render above and below the viewport to prevent white flashes during fast scrolling
keyExtractor((item: T, index: number) => string | number)Extracts a unique React key for each item. Crucial for stable rendering during sorts/filters!
renderItem*(item: T, index: number) => ReactNodeThe render function for the row
refForwardedRef<VirtualListHandle>