Widgets
VirtualList
Efficiently renders large lists by windowing only the visible range.
Efficiently renders large lists by windowing only the visible range.
Usage
ui.virtualList({
id: "items",
items: state.items,
itemHeight: 1,
renderItem: (item, index, focused) =>
ui.text(focused ? `> ${item}` : ` ${item}`, {
key: String(index),
style: focused ? { bold: true } : {},
}),
onSelect: (item) => openItem(item),
})Variable-Height (Estimated + Corrected)
ui.virtualList({
id: "chat",
items: state.messages,
estimateItemHeight: (msg) => (msg.preview ? 3 : 2),
renderItem: (msg, _index, focused) =>
ui.column(
{ gap: 0 },
[
ui.text(focused ? `> ${msg.author}` : ` ${msg.author}`),
ui.text(msg.text),
msg.preview ? ui.text(msg.preview, { style: { dim: true } }) : null,
].filter(Boolean),
),
})Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | required | Widget identifier for focus and routing |
items | T[] | required | Items to render |
itemHeight | number | (item, index) => number | 1 | Exact item height (fixed or precomputed variable) |
estimateItemHeight | number | (item, index) => number | - | Estimated height for variable-height virtualization |
measureItemHeight | (item, index, ctx) => number | internal measurer | Optional custom measurement callback for estimate mode |
renderItem | (item, index, focused) => VNode | required | Render function for each item |
overscan | number | 3 | Extra items rendered above/below viewport |
keyboardNavigation | boolean | true | Enable arrow/page/home/end navigation |
wrapAround | boolean | false | Wrap selection from end to start |
scrollDirection | "traditional" | "natural" | "traditional" | Mouse-wheel direction mode for this list |
selectionStyle | TextStyle | - | Optional style override for the selected row highlight |
onScroll | (scrollTop, range) => void | - | Scroll callback with visible range |
onSelect | (item, index) => void | - | Selection callback |
focusConfig | FocusConfig | - | Control focus visuals; { indicator: "none" } suppresses focused item highlight |
Behavior
- Arrow Up/Down navigates items. Page Up/Down and Home/End jump by page or to boundaries.
- Mouse scroll wheel scrolls the list (3 lines per tick).
scrollDirection: "traditional"(default): wheel down moves viewport down.scrollDirection: "natural": wheel down moves content down (trackpad-style).
- The
onScrollcallback fires for both keyboard navigation and mouse wheel input.
Notes
- Use
itemHeightfor fixed heights or when exact heights are known up front. - Use
estimateItemHeightwhen true height depends on rendered content/width. measureItemHeightis only used in estimate mode. It receives{ width, estimatedHeight, vnode }for each rendered item and can override the internal simple measurer.- Returned
measureItemHeightvalues are normalized before entering the measured-height cache: non-finite or non-positive values fall back toestimatedHeight, and positive values are truncated to whole cells with a minimum of 1. - In estimate mode, visible items are measured and cached, then scroll math is corrected on the following frame.
- Measured-height cache is reused only while viewport width and item count stay the same; changing either triggers remeasurement.
renderItemreceives afocusedflag for styling.selectionStylestill applies to the selected row in estimate mode.- The
rangepassed toonScrollis[startIndex, endIndex)and includes overscan.