# VirtualList
A long list, or a grid of rows and columns, that renders only the items near the viewport, with measured row heights.
```ehtml Example
import { VirtualList, VirtualItem, VirtualCell, initialRange } from "@elements/ui/virtual-list";
people[i].id} index={i}>
{people[i].name}
...
r} index={r}>
c} column={c}>{r}, {c}
```
## Parts
| Part | What it is |
|---|---|
| `VirtualList` | The scroll container. Owns the virtualizer and writes range, the indexes to render. |
| `VirtualItem` | One rendered item. Takes its index; the list measures it and places it at its offset. |
| `VirtualCell` | One cell of a grid row (columns > 0). Takes its column; the list places it at the column's offset and width. |
| `.virtual-list-inner` | The track inside the scroller, as long as every item together. |
## Attributes
### VirtualList
| Attribute | Type | Default | Description |
|---|---|---|---|
| `count` | `number` | (required) | How many items the list has. Required. |
| `range` | `number[]` (two-way) | `[]` | The indexes to render, written by the list as it scrolls. Loop over it with ``. Seed it with initialRange(count, n) so the server renders the first n items. |
| `estimateSize` | `number \| ((index: number) => number)` | `40` | Item size in px before it is measured. Sizes the scrollbar until items render. |
| `overscan` | `number` | `5` | Extra items rendered before and after the viewport. With lanes, rows of items. |
| `orientation` | `"vertical" \| "horizontal"` | `"vertical"` | Scroll axis. |
| `gap` | `number` | `0` | Space between items in px. |
| `lanes` | `number` | `1` | Columns (vertical) or rows (horizontal): a masonry layout; each item goes in the shortest lane. |
| `paddingStart` | `number` | `0` | Space before the first item in px. |
| `paddingEnd` | `number` | `0` | Space after the last item in px. |
| `scrollPaddingStart` | `number` | `0` | Offset kept clear when scrolling an item into view at the start (a sticky header's height). |
| `scrollPaddingEnd` | `number` | `0` | Offset kept clear at the end. |
| `initialOffset` | `number` | `0` | Scroll position in px when the list starts. |
| `scroller` | `"self" \| "window"` | `"self"` | self: the list scrolls (give it a height). window: the page scrolls and the list is as tall as its items. |
| `anchor` | `"start" \| "end"` | `"start"` | The side that holds still when items change. end suits chat and logs. |
| `followOnAppend` | `boolean \| "auto" \| "smooth"` | `false` | With anchor end: stay at the end when items are added while already there; "smooth" glides to the new item. |
| `measure` | `boolean` | `true` | Measure each item's real size. false uses estimateSize as the exact size (fixed rows, no measuring). |
| `rangeExtractor` | `((range) => number[]) \| null` | `null` | Which indexes render for the visible range. defaultRangeExtractor is exported to build on. |
| `sticky` | `number[]` | `[]` | Indexes of header items. The last one above the viewport stays rendered and pinned to the top. |
| `itemKey` | `(index: number) => string \| number` | `(i) => i` | A stable key per index, so measurements follow an item when the list reorders. |
| `endThreshold` | `number` | `0` | How many items before the end onEndReached fires. |
| `scrolling` | `boolean` (two-way) | `false` | True while the list is scrolling. Written by the list. |
| `busy` | `boolean` | `false` | With role feed, marks the feed aria-busy. Set it while the next page loads (not while scrolling). |
| `role` | `"list" \| "feed" \| "grid" \| "none"` | `"list"` | Role of the track; items take the matching role (listitem, article, row). With grid, cells are gridcells and rows and cells carry aria-rowindex and aria-colindex. |
| `label` | `string` | `""` | Accessible name of the list. |
| `dir` | `"" \| "ltr" \| "rtl"` | `""` | Direction. A horizontal list scrolls from the right under rtl. |
| `class` | `string` | `""` | Extra classes on the scroll container; set its height here. |
| `attrs` | `Record` | `{}` | Other attributes for the scroll container: id, data-\*, aria-\*. |
| `onRangeChange` | `(range: number[]) => void` | `() => {}` | Called when the rendered indexes change. |
| `onScrollingChange` | `(scrolling: boolean) => void` | `() => {}` | Called when scrolling starts and stops. |
| `onEndReached` | `() => void` | `() => {}` | Called once per count when the last item renders: load the next page here. |
| `onVirtualizer` | `(v: Virtualizer) => void` | `() => {}` | Hands over the virtualizer: scrollToIndex, scrollToOffset, scrollBy, scrollToEnd, measure, getTotalSize. |
| `columns` | `number` | `0` | Column count of a grid. Above 0, a second virtualizer on the same scroller windows the columns, and each row loops over columnRange with ``. |
| `columnRange` | `number[]` (two-way) | `[]` | The column indexes to render, written by the list as it scrolls sideways. |
| `estimateColumnSize` | `number \| ((index: number) => number)` | `120` | Width of each column in px. Rows of a grid are estimateSize tall. |
| `columnOverscan` | `number` | `2` | Extra columns rendered on each side of the viewport. |
| `onColumnRangeChange` | `(range: number[]) => void` | `() => {}` | Called when the rendered columns change. |
| `onColumnVirtualizer` | `(v: Virtualizer) => void` | `() => {}` | Hands over the column virtualizer: scrollToIndex on a column. |
| `virtualizerOptions` | `Record` | `{}` | Any other virtualizer option, passed through: enabled, isScrollingResetDelay, useScrollendEvent, scrollEndThreshold, laneAssignmentMode, initialMeasurementsCache, useCachedMeasurements, initialRect, smoothScrollDuration. |
### VirtualCell
| Attribute | Type | Default | Description |
|---|---|---|---|
| `column` | `number` | (required) | The cell's column index. Required. |
| `class, attrs` | `string, Record` | `"", {}` | Extra classes and attributes on the cell. |
### VirtualItem
| Attribute | Type | Default | Description |
|---|---|---|---|
| `index` | `number` | (required) | The item's index in the list. Required. |
| `setsize` | `number` | `-1` | aria-setsize, when the list's count is not the set size (the list sets count otherwise). |
| `class, attrs` | `string, Record` | `"", {}` | Extra classes and attributes on the item. |
## Slots
| Slot | Content |
|---|---|
| `default` | The rendered items: ``. |
## Keyboard
| Keys | Action |
|---|---|
| `Tab` | Focuses the scroll container (or the first focusable item). |
| `ArrowDown / ArrowUp` | Scroll the focused list (native scrolling). |
| `PageDown / PageUp, Home / End` | Scroll by a page, or to the start and end. |
| `Arrows (role grid)` | A grid with columns moves focus one cell; a cell outside the window scrolls in (one Tab stop, APG grid). |
| `Home / End, Ctrl + Home / Ctrl + End (role grid)` | First or last cell of the row; first cell of the grid, last cell of the last row. |
| `PageDown / PageUp (role grid)` | Move focus a viewport of rows. |
| `PageDown / PageUp (role feed)` | From inside an article, focus the next or previous article (APG feed). |
| `Ctrl + End / Ctrl + Home (role feed)` | Focus the first focusable element after the feed, or the last one before it. |
## Data attributes
| Attribute | Values | On | Description |
|---|---|---|---|
| `data-orientation` | `"vertical" \| "horizontal"` | .virtual-list | |
| `data-scroller` | `"self" \| "window"` | .virtual-list | |
| `data-ready` | present | .virtual-list | The virtualizer is running; items are placed by transform. |
| `data-scrolling` | present | .virtual-list | While scrolling. |
| `data-index` | the index | .virtual-item | |
| `data-lane` | the lane number | .virtual-item | |
| `data-columns` | the column count | .virtual-list | A grid. |
| `data-column` | the column index | .virtual-cell | |
| `data-sticky` | present | .virtual-item | The pinned header item. |
## Accessibility
- The track has role list (or feed, grid) and an aria-label from label; items take the matching item role.
- A grid track has aria-rowcount and aria-colcount; rows have aria-rowindex and cells are gridcells with aria-colindex, so the position in the whole grid is announced.
- There is no listbox role: a list of options needs selection and an active descendant, which Combobox, Command and Select provide.
- Each item has aria-posinset (index + 1) and aria-setsize (count), so a screen reader announces the position in the whole list, not the rendered window.
- The scroll container is in the Tab order so keyboard users can scroll it.
- A feed's articles are each focusable, and the feed is aria-busy only while busy is set (a page loading).
- A grid with columns is one Tab stop, the focused cell, and arrows move between cells, so every gridcell announced can be reached.
## See also
- [DataTable](data-table): A data grid: sort, search, filter, resize, reorder, pin, group, select, edit, page or virtualize, save views and export.