Virtualizer
Virtualizer efficiently renders large collections by only mounting visible items. It renders no DOM of its own: wrap a collection component such as ListBox, GridList, Table, or Tree, and that collection element becomes the scroll container. It supports list, grid, waterfall, and table layouts with full keyboard navigation and drag-and-drop.
Import
import {
Virtualizer,
ListLayout,
GridLayout,
WaterfallLayout,
TableLayout
} from '@proyecto-viviana/solidaria-components';Basic List
A virtual list rendering 1,000 items efficiently. Only visible items are mounted in the DOM. The ListBox is the scroller, so give it the height and overflow.
<Virtualizer layout={ListLayout} layoutOptions={{ itemSize: 40, overscan: 5 }}>
<ListBox
aria-label="Virtualized list"
selectionMode="multiple"
items={items}
getKey={(item) => item.id}
getTextValue={(item) => item.label}
class="viewport"
style={{ height: "256px" }}
>
{(item) => (
<ListBoxOption id={item.id} textValue={item.label} class="row">
{item.label}
</ListBoxOption>
)}
</ListBox>
</Virtualizer>Grid Layout
Display items in a fixed-column grid. GridLayout windows rows of rowHeight with columnCount items each; set layout="grid" on the ListBox so arrow keys move across columns, and lay the collection content out in the same columns.
<Virtualizer layout={GridLayout} layoutOptions={{ rowHeight: 120, columnCount: 3 }}>
<ListBox
aria-label="Virtualized grid"
layout="grid"
items={items}
getKey={(item) => item.id}
getTextValue={(item) => item.label}
class="viewport viewport--grid"
style={{
height: "320px",
"--grid-columns": "repeat(3, minmax(0, 1fr))",
"--grid-row-height": "120px",
}}
>
{(item) => (
<ListBoxOption id={item.id} textValue={item.label}>
<div class="tile">{item.label}</div>
</ListBoxOption>
)}
</ListBox>
</Virtualizer>Waterfall Layout
Columns follow the available width: WaterfallLayout derives the column count from minColumnWidth and gap against the measured collection width, and windows rows of rowHeight. Match it with an auto-fill grid on the collection content.
<Virtualizer
layout={WaterfallLayout}
layoutOptions={{ minColumnWidth: 200, gap: 8, rowHeight: 120 }}
>
<ListBox
aria-label="Virtualized waterfall"
layout="grid"
items={items}
getKey={(item) => item.id}
getTextValue={(item) => item.label}
class="viewport viewport--grid"
style={{
height: "384px",
"--grid-columns": "repeat(auto-fill, minmax(200px, 1fr))",
"--grid-row-height": "120px",
"--grid-gap": "8px",
}}
>
{(item) => (
<ListBoxOption id={item.id} textValue={item.label}>
<div class="tile">{item.label}</div>
</ListBoxOption>
)}
</ListBox>
</Virtualizer>Virtualizer Props
Props
| Prop | Type | Default | Description |
|---|---|---|---|
layout | VirtualizerLayoutClass<O> | VirtualizerLayout<O> | — | Layout implementation (ListLayout, GridLayout, WaterfallLayout, TableLayout) or a custom layout object |
layoutOptions | O | — | Options passed to the layout. Varies by layout type (e.g., itemSize for ListLayout, columnCount for GridLayout) |
children | JSX.Element | — | The collection to virtualize (ListBox, GridList, Table, Tree). Virtualizer renders no element; the collection element is the scroll container, so size and overflow belong on it |
renderDropIndicator | (index: number, position: 'before' | 'after' | 'on') => JSX.Element | — | Render function for drag-and-drop indicators |
getDropOperation | (target, types, allowedOperations) => DropOperation | — | Resolver for determining allowed drop operations |
Layout Options
ListLayout / TableLayout
Props
| Prop | Type | Default | Description |
|---|---|---|---|
itemSize | number | 40 | Height of each row in pixels |
overscan | number | 5 | Number of extra items to render beyond the visible area |
GridLayout
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rowHeight | number | — | Height of each row in pixels |
columnCount | number | — | Number of columns in the grid |
WaterfallLayout
Props
| Prop | Type | Default | Description |
|---|---|---|---|
minColumnWidth | number | 200 | Minimum width of each column. The column count is derived from it and the measured collection width. |
gap | number | 0 | Horizontal gap between columns in pixels, counted when deriving the column count |
rowHeight | number | — | Height of each row in pixels |
Context
Child components can access virtualizer state via useVirtualizerContext(). This provides getVisibleRange(), getLayoutInfo(), and drop target resolution for building custom virtualized collection components.
Accessibility
- Keyboard navigation works seamlessly across virtualized items using
aria-activedescendant - Focus management preserves scroll position when items are focused via keyboard
- Screen readers announce the total item count and current position
- Drag and drop indicators are announced to assistive technologies
- Integrates with ListBox, Table, GridList, and Tree for full a11y support