List
List is a high-performance, virtualized container for rendering large datasets with built-in grouping, sorting, and visual formatting. It only renders visible items in the viewport, making it ideal for displaying thousands of records while maintaining smooth scrolling performance.
Key features:
- Virtualization: Renders only visible items for optimal performance with large datasets
- Advanced grouping: Group data by any field with customizable headers and footers
- Built-in sorting: Sort by any data field in ascending or descending order
- Visual formatting: Pre-styled list appearance with borders, spacing, and layout
- Pagination support: Handle large datasets with built-in pagination controls
- Empty state handling: Customizable templates for when no data is available
List vs Items:
Use List for complex data presentation requiring performance optimization, grouping, sorting, or visual formatting. Use Items for simple data iteration without layout requirements.
List keeps a bounded cache of recently rendered virtualized rows by default. This reduces remount and measurement flash when users scroll back through content they have already seen. Set renderCache="{false}" to minimize mounted DOM nodes, tune renderCacheSize for the number of recently rendered rows to retain, and use virtualBufferSize when fast scrolling should prepare more never-seen rows just outside the viewport.
In the following examples all use the same list of data which looks like so:
| Id | Name | Quantity | Unit | Category | Key |
|---|---|---|---|---|---|
| 0 | Apples | 5 | pieces | fruits | 5 |
| 1 | Bananas | 6 | pieces | fruits | 4 |
| 2 | Carrots | 100 | grams | vegetables | 3 |
| 3 | Spinach | 1 | bunch | vegetables | 2 |
| 4 | Milk | 10 | liter | diary | 1 |
| 5 | Cheese | 200 | grams | diary | 0 |
The data is provided as JSON.
Context variables available during execution:
$group: Group information when usinggroupBy(available in group templates)$isFirst: Boolean indicating if this is the first item$isLast: Boolean indicating if this is the last item$isSelected: Boolean indicating if this item is currently selected$item: Current data item being rendered$itemIndex: Zero-based index of current item
Use children as Content Template
The itemTemplate property can be replaced by setting the item template component directly as the List's child. In the following example, the two List are functionally the same:
<App>
<!-- This is the same -->
<List>
<property name="itemTemplate">
<Text>Template</Text>
</property>
</List>
<!-- As this -->
<List>
<Text>Template</Text>
</List>
</App>Behaviors
This component supports the following behaviors:
| Behavior | Properties |
|---|---|
| Animation | animation, animationOptions |
| Bookmark | bookmark, bookmarkLevel, bookmarkTitle, bookmarkOmitFromToc |
| Component Label | label, labelPosition, labelWidth, labelBreak, required, enabled, shrinkToLabel, style, readOnly |
| Tooltip | tooltip, tooltipMarkdown, tooltipOptions |
| Styling Variant | variant |
Properties
availableGroups
This property is an array of group names that the List will display. If not set, all groups in the data are displayed.
<App>
<List
data="{[...]}"
groupBy="category"
availableGroups="{['fruits', 'vegetables']}">
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
</App>borderCollapse
default: true
Collapse items borders
Note how the List on the right has different borders:
<App>
<HStack>
<List
data="{[...]}"
groupBy="category"
borderCollapse="false"
width="$space-64"
>
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
<List
data="{[...]}"
groupBy="category"
borderCollapse="true"
width="$space-64"
>
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
</HStack>
</App>data
The component receives data via this property. The data property is a list of items that the List can display.
Note how the List infers the given data and provides a simple layout for it.
To tweak what data and how it is displayed, see the itemTemplate section.
<App>
<List data='{[...]}' />
</App>You can also provide the List with data directly from an API via this property.
In the example below, the List also uses the itemTemplate property to access the data attributes as well.
See the itemTemplate section.
dataRefreshMode
default: "reset"
Controls how the list handles later data refreshes after the initial load. reset keeps the list's default refresh behavior. preserve-state reconciles refreshed data with the current view state for unchanged source row IDs.
Available values: reset (default), preserve-state
Use dataRefreshMode="preserve-state" when a List receives refreshed data after backend mutations. The list keeps the current scroll position, group expansion state, and row selection for source rows whose idKey values still exist.
For a complete backend-style insert, update, and delete workflow, see Preserve collection state across data refreshes.
For mutation flows where only the next refresh should preserve state, call preserveStateOnNextDataRefresh() before updating or refetching the data:
Stable, unique idKey values are required. If an inserted row is outside the viewport and the refresh options use operation: "insert" or scrollTarget: "first-inserted", the list scrolls the first inserted visible row into view after reconciliation.
defaultGroups
This property adds an optional list of default groups for the List and displays the group headers in the specified order. If the data contains group headers not in this list, those headers are also displayed (after the ones in this list); however, their order is not deterministic.
For the
defaultGroupsproperty to work, the data must be sectioned using thegroupByproperty, and either agroupHeaderTemplateor agroupFooterTemplateneeds to be provided.
<App>
<List
data='{[...]}'
defaultGroups="{['dairy', 'meat', 'vegetables']}"
groupBy="category" >
<property name="groupHeaderTemplate">
<VStack>
<Text variant="subtitle" value="{$group.key}" />
</VStack>
</property>
</List>
</App>emptyListTemplate
This property defines the template to display when the list is empty.
<App>
<List>
<property name="emptyListTemplate">
<VStack horizontalAlignment="center">
<Text variant="strong" value="Empty..." />
</VStack>
</property>
</List>
</App><App>
<List>
<property name="emptyListTemplate">
<VStack horizontalAlignment="center">
<Text variant="strong" value="Empty..." />
</VStack>
</property>
</List>
</App>enableMultiRowSelection
default: true
This boolean property indicates whether you can select multiple rows in the list. This property only has an effect when the rowsSelectable property is set. Setting it to false limits selection to a single row.
<App>
<List data='{[...]}' rowsSelectable="true" enableMultiRowSelection="false">
<Text>{$item.name}</Text>
</List>
</App>fixedItemSize
default: false
When set to true, the list will measure the height of the first item and use that as a fixed size hint for all items. This improves scroll performance when all items have the same height. If items have varying heights, leave this as false.
groupBy
This property sets which data item property is used to group the list items. Accepts a field name string or a function that receives an item and returns the group key. If not set, no grouping is done.
For the
groupByproperty to work, either agroupHeaderTemplateor agroupFooterTemplateneeds to be provided.
groupBy accepts either a string (the name of the item field to group by) or a function that receives each list item and returns the grouping key.
String usage — group by a field name directly:
<App>
<List
data='{[...]}'
groupBy="category">
<property name="groupHeaderTemplate">
<VStack>
<Text variant="subtitle" value="{$group.key}" />
</VStack>
</property>
</List>
</App>Function usage — compute the grouping key from each item:
<App>
<List
data='{[...]}'
groupBy="{(item) => item.name[0]}">
<property name="groupHeaderTemplate">
<VStack>
<Text variant="subtitle" value="{$group.key}" />
</VStack>
</property>
</List>
</App>groupFooterTemplate
Enables the customization of how the the footer of each group is displayed. Combine with groupHeaderTemplate to customize sections. You can use the $item context variable to access an item group and map its individual attributes.
The structure of $group in a groupFooterTemplate is the following:
| Attribute | Description |
|---|---|
| id | Unique identifier for the section. It is commonly generated from the attribute name provided via groupBy. |
| items | The items filtered from the original data list that fall into this section. |
| key | The attribute name to section by provided via groupBy |
This example displays a separator line in the groups' footer:
<App>
<List data='{[...]}' groupBy="category">
<property name="groupHeaderTemplate">
<VStack>
<Text variant="subtitle" value="{$group.key}" />
</VStack>
</property>
<property name="groupFooterTemplate">
<VStack paddingVertical="$space-normal">
<ContentSeparator/>
</VStack>
</property>
</List>
</App>groupHeaderTemplate
Enables the customization of how the groups are displayed, similarly to the itemTemplate. You can use the $item context variable to access an item group and map its individual attributes.
The structure of $group in a groupHeaderTemplate is the following:
| Attribute | Description |
|---|---|
| id | Unique identifier for the section. It is commonly generated from the attribute name provided via groupBy. |
| items | The items filtered from the original data list that fall into this section. |
| key | The attribute name to section by provided via groupBy |
<App>
<List data='{[...]}' groupBy="category">
<property name="groupHeaderTemplate">
<Stack padding="$space-2">
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
</App>groupsInitiallyExpanded
default: true
This Boolean property defines whether the list groups are initially expanded.
Note how the groups in the right List are expanded by default:
<App>
<HStack gap="$space-2">
<List data="{[...]}"
groupBy="category"
groupsInitiallyExpanded="false"
width="$space-48">
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
<List data="{[...]}"
groupBy="category"
groupsInitiallyExpanded="true"
width="$space-48">
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
</HStack>
</App>hideEmptyGroups
default: true
This boolean property indicates if empty groups should be hidden (no header and footer are displayed).
Note how the meats category is not displayed in the right List:
<App>
<HStack gap="$space-2">
<List
data="{[...]}"
defaultGroups="{['meats']}"
groupBy="category"
hideEmptyGroups="false"
width="$space-48">
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
<List
data="{[...]}"
defaultGroups="{['meats']}"
groupBy="category"
hideEmptyGroups="true"
width="$space-48">
<property name="groupHeaderTemplate">
<Stack>
<Text variant="subtitle" value="{$group.key}" />
</Stack>
</property>
</List>
</HStack>
</App>hideSelectionCheckboxes
default: false
If true, hides selection checkboxes. Selection logic still works via API and keyboard.
The default value is false. When set to true, the selection checkboxes are hidden while
selection via click, keyboard, and the programmatic API still work as expected.
<App>
<List
data='{[...]}'
rowsSelectable="true"
enableMultiRowSelection="true"
hideSelectionCheckboxes="true"
>
<Text>{$item.name}</Text>
</List>
</App>idKey
default: "id"
Denotes which attribute of an item acts as the ID or key of the item. The named attribute must hold a value that is unique across the data and never empty: it is the row's identity, so duplicate or empty values make virtualized rows reconcile incorrectly (rows can paint over one another) and cause selection state to be shared between rows.
<App>
<List idKey="key" data='{[...]}' />
</App>The named attribute is the row's identity, not a display hint. Its values must be unique across the data and never empty. Duplicate or empty values make virtualized rows reconcile incorrectly — rows can paint on top of one another once the row set changes size — and cause selection state to be shared between rows. Neither failure raises an error; in a development build
Listwarns on the console when it sees them.When rows come from several sources that each carry their own ids, those ids are usually not unique together. Synthesize a key that is unique across the combined set and point
idKeyat that instead.
initiallySelected
An array of IDs that should be initially selected when the list is rendered. This property only has an effect when the rowsSelectable property is set to true.
The following example pre-selects the first and third items (IDs 0 and 2) when the list renders:
<App>
<List data='{[...]}' rowsSelectable="true" initiallySelected="{[0, 2]}">
<Text>{$isSelected ? '✓ ' : ''}{$item.name}</Text>
</List>
</App>itemTemplate
This property allows the customization of mapping data items to components. You can use the $item context variable to access an item and map its individual attributes.
Note how in the example below the $item is used to access the name, quantity and unit attributes.
<App>
<List data='{[...]}'>
<property name="itemTemplate">
<Card>
<HStack verticalAlignment="center">
<Icon name="info" />
<Text value="{$item.name}" variant="strong" />
</HStack>
<HStack>
<Text value="{$item.quantity}" />
<Text value="{$item.unit}" variant="em" />
</HStack>
</Card>
</property>
</List>
</App>keyBindings
This property defines keyboard shortcuts for list actions. Provide an object with action names as keys and keyboard shortcut strings as values. Available actions: selectAll, cut, copy, paste, delete. If not provided, default shortcuts are used.
This property uses the following default key bindings:
{
"selectAll": "CmdOrCtrl+A",
"cut": "CmdOrCtrl+X",
"copy": "CmdOrCtrl+C",
"paste": "CmdOrCtrl+V",
"delete": "Delete"
}You can use these accelerator key names:
CmdOrCtrl: Command on macOS, Ctrl on Windows/LinuxAlt: Alt/OptionShift: ShiftSuper: Command on macOS, Windows key on Windows/LinuxCtrl: Control keyCmd: Command key (macOS only)
limit
This property limits the number of items displayed in the List. If not set, all items are displayed.
<App>
<List limit="3" data='{[...]}' />
</App>loading
This property delays the rendering of children until it is set to false, or the component receives usable list items via the data property.
This boolean property indicates whether the list is currently loading data. While loading is true
and no list items are available, the list shows its loading UI after the configured
loadingDelay.
<App>
<List loading="true" loadingDelay="0" />
</App><App>
<List loading="true" loadingDelay="0" />
</App>loadingDelay
default: 400
The delay in milliseconds before showing the loading UI. Set to 0 to show immediately, or a higher value to prevent flicker for fast-loading data.
The loadingDelay property controls how many milliseconds the list waits before showing its loading
UI. The default is 400, which prevents flicker when data loads quickly. Set it to 0 to show the
loading UI immediately.
<App var.isLoading="{false}" var.items="{[]}">
<Button
label="Load"
onClick="isLoading = true; delay(1000); items = [{ id: 1, name: 'Loaded item' }]; isLoading = false" />
<List loading="{isLoading}" loadingDelay="400" data="{items}">
<Text>{$item.name}</Text>
</List>
</App><App var.isLoading="{false}" var.items="{[]}">
<Button
label="Load"
onClick="isLoading = true; delay(1000); items = [{ id: 1, name: 'Loaded item' }]; isLoading = false" />
<List loading="{isLoading}" loadingDelay="400" data="{items}">
<Text>{$item.name}</Text>
</List>
</App>orderBy
This optioanl property enables the ordering of list items by specifying an attribute in the data.
<App>
<List orderBy="{{ field: 'quantity', direction: 'desc' }}" data='{[...]}' />
</App>pageInfo
This property contains the current page information. Setting this property also enures the List uses pagination.
It contains the following boolean attributes:
| Attribute | Description |
|---|---|
hasPrevPage | Does the list have a previous page |
hasNextPage | Does the list have a next page |
isFetchingPrevPage | TBD |
isFetchingNextPage | TBD |
refreshOn
Bind this property to a global variable (or expression) whose change should force all visible list items to re-render and pick up the latest reactive state. When not set, items re-render on every XMLUI reactive cycle (safe but less optimal). When set, items only re-render when the bound value changes, which eliminates spurious re-renders from unrelated global-variable updates (e.g. focus events).
renderCache
default: true
Controls whether the list keeps a bounded set of recently rendered virtualized rows mounted while they are outside the viewport. Keeping rows mounted reduces remount and measurement flash when users scroll back through recently viewed content.
renderCacheSize
default: 80
Maximum number of recently rendered virtualized rows to keep mounted when renderCache is enabled. Larger values can make repeat scrolling smoother but retain more DOM nodes.
rowsSelectable
Indicates whether the rows are selectable (true) or not (false).
The default value is false. When enabled, clicking a row selects it and highlights it.
The current selection state is exposed to the item template via the $isSelected context variable.
<App>
<List data='{[...]}' rowsSelectable="true">
<Text color="{$isSelected ? '$color-primary' : 'inherit'}">{$item.name}</Text>
</List>
</App>rowUnselectablePredicate
This property defines a predicate function with a return value that determines if the row should be unselectable. The function retrieves the item to display and should return a Boolean-like value. This property only has an effect when the rowsSelectable property is set to true.
Rows matching this predicate cannot be selected and are excluded from select-all operations. The selection checkbox is automatically displayed as disabled for these rows, providing visual feedback to users.
<App>
<List data='{[...]}'
rowsSelectable="true"
rowUnselectablePredicate="{(item) => item.category === 'vegetables'}">
<Text>{$item.name}</Text>
</List>
</App>scrollAnchor
default: "top"
This property pins the scroll position to a specified location of the list. Available values are shown below.
Available values: top (default), bottom
<App>
<List scrollAnchor="bottom" data='{[...]}' height="300px" />
</App>selectionCheckboxAnchor
default: "left-center"
The corner of the item that the overlay checkbox is anchored to. Only applies when selectionCheckboxPosition is "overlay". Offsets are measured inward from the chosen corner.
Available values: top-left, top-right, bottom-left, bottom-right, center-left, center-right
Use "center-left" or "center-right" to vertically center the checkbox regardless of
row height — ideal for card-style layouts.
<App>
<List data='{[...]}'
rowsSelectable="true"
selectionCheckboxPosition="overlay"
selectionCheckboxAnchor="bottom-left">
<Card paddingLeft="40px">{$item.name}</Card>
</List>
</App>selectionCheckboxOffsetX
default: "$space-2"
Horizontal distance of the overlay checkbox from its anchor corner. Accepts any CSS length value (e.g. "8px", "1rem"). Only applies when selectionCheckboxPosition is "overlay".
selectionCheckboxOffsetY
default: "$space-2"
Vertical distance of the overlay checkbox from its anchor corner. Accepts any CSS length value (e.g. "8px", "1rem"). Only applies when selectionCheckboxPosition is "overlay".
selectionCheckboxPosition
default: "before"
Controls the placement mode of selection checkboxes. "before" (default) renders the checkbox inline before the item content. "overlay" renders the checkbox absolutely positioned inside the item's bounding box, overlapping the content. Use selectionCheckboxAnchor, selectionCheckboxOffsetX, and selectionCheckboxOffsetY to control the overlay position.
Available values: before (default), overlay
When "overlay" is active, use
selectionCheckboxAnchor,
selectionCheckboxOffsetX, and
selectionCheckboxOffsetY to fine-tune the position.
<App>
<List data='{[...]}' rowsSelectable="true" selectionCheckboxPosition="overlay">
<Card paddingLeft="40px">{$item.name}</Card>
</List>
</App>selectionCheckboxSize
CSS size of the checkbox (e.g. "16px", "20px"). When not set the browser default size is used. Applies in both "before" and "overlay" modes.
Sets the width and height of the checkbox element. Accepts any CSS length value such as
"16px" or "20px". When not set the browser default size is used. Applies in both
"before" and "overlay" modes.
<App>
<List data='{[...]}' rowsSelectable="true" selectionCheckboxSize="20px">
<Text>{$item.name}</Text>
</List>
</App>syncWithVar
The name of a global variable to synchronize the list's selection state with. The named variable must reference an object; the list will read from and write to its 'selectedIds' property. When provided, this takes precedence over initiallySelected.
The following example demonstrates two independent MyList components sharing selection state
through a global variable. Selecting a row in either list immediately reflects in the other:
syncWithVarworks with both global and local variables. When using local variables, ensure all Lists in the sync have that variable in their scope.
<App global.selState="{{}}">
<MyList />
<Text>Selection: {JSON.stringify(selState)}</Text>
<MyList />
</App><Component name="MyList">
<List
syncWithVar="selState"
data='{[
{ id: 0, name: "Apples", category: "fruits" },
{ id: 1, name: "Bananas", category: "fruits" },
{ id: 2, name: "Carrots", category: "vegetables" },
{ id: 3, name: "Spinach", category: "vegetables" }
]}'
rowsSelectable="true"
enableMultiRowSelection="true"
>
<Text>{$item.name} — {$item.category}</Text>
</List>
</Component>Change the selection in one of the lists and check how it is synced.
<App global.selState="{{}}">
<MyList />
<Text>Selection: {JSON.stringify(selState)}</Text>
<MyList />
</App><Component name="MyList">
<List
syncWithVar="selState"
data='{[
{ id: 0, name: "Apples", category: "fruits" },
{ id: 1, name: "Bananas", category: "fruits" },
{ id: 2, name: "Carrots", category: "vegetables" },
{ id: 3, name: "Spinach", category: "vegetables" }
]}'
rowsSelectable="true"
enableMultiRowSelection="true"
>
<Text>{$item.name} — {$item.category}</Text>
</List>
</Component>Change the selection in one of the lists and check how it is synced.
virtualBufferSize
Extra virtualizer buffer, in pixels, to render before and after the viewport. Increase this when fast scrolling reaches rows that have not been rendered before; unlike renderCache, this prepares never-seen rows near the viewport.
Events
contextMenu
This event is triggered when the List is right-clicked (context menu).
Signature: contextMenu(event: MouseEvent): void
event: The mouse event object.
copyAction
This event is triggered when the user presses the copy keyboard shortcut (default: Ctrl+C/Cmd+C) and rowsSelectable is set to true.
Signature: copy(row: ListRowContext | null, selectedItems: any[], selectedIds: string[]): void | Promise<void>
row: The currently focused row context, or null if no row is focused.selectedItems: Array of selected row items.selectedIds: Array of selected row IDs (as strings).
<App var.log="">
<Text>{log}</Text>
<List data='{[...]}' rowsSelectable="true"
onCopyAction="(row, items, ids) => log = 'Copied: ' + ids.join(', ')">
<Text>{$item.name}</Text>
</List>
</App>cutAction
This event is triggered when the user presses the cut keyboard shortcut (default: Ctrl+X/Cmd+X) and rowsSelectable is set to true.
Signature: cut(row: ListRowContext | null, selectedItems: any[], selectedIds: string[]): void | Promise<void>
row: The currently focused row context, or null if no row is focused.selectedItems: Array of selected row items.selectedIds: Array of selected row IDs (as strings).
See also copyAction, pasteAction, and deleteAction for the other keyboard-triggered events.
deleteAction
This event is triggered when the user presses the delete keyboard shortcut (default: Delete key) and rowsSelectable is set to true.
Signature: delete(row: ListRowContext | null, selectedItems: any[], selectedIds: string[]): void | Promise<void>
row: The currently focused row context, or null if no row is focused.selectedItems: Array of selected row items.selectedIds: Array of selected row IDs (as strings).
See copyAction for a similar example.
pasteAction
This event is triggered when the user presses the paste keyboard shortcut (default: Ctrl+V/Cmd+V) and rowsSelectable is set to true.
Signature: paste(row: ListRowContext | null, selectedItems: any[], selectedIds: string[]): void | Promise<void>
row: The currently focused row context, or null if no row is focused.selectedItems: Array of selected row items.selectedIds: Array of selected row IDs (as strings).
See copyAction for a similar example.
rowDoubleClick
This event is fired when the user double-clicks a list row. The handler receives the clicked row item as its only argument.
Signature: rowDoubleClick(item: any): void
item: The clicked list row item.
This event is triggered when a list row is double-clicked. The handler receives the row's data item as its only argument.
<App var.lastClicked="">
<Text>Double-clicked: {lastClicked}</Text>
<List data='{[...]}' onRowDoubleClick="(item) => lastClicked = item.name">
<Text>{$item.name}</Text>
</List>
</App>scroll
This event fires as the user scrolls the list. The handler receives an object describing the current scroll state. It is only fired for user-driven scrolls; the list's own programmatic and auto-follow scrolls do not trigger it. Use it together with the atEnd flag and the scrollToBottom() method to implement follow-newest and read-pause behavior, or with visibleRange and itemCount to display a visible item range.
Signature: scroll(event: { scrollTop: number, scrollHeight: number, viewportSize: number, atEnd: boolean, visibleRange: { startIndex: number, endIndex: number }, itemCount: number }): void
event: The scroll state:scrollTop(current scroll offset),scrollHeight(total scrollable size),viewportSize(visible size), andatEnd(true when scrolled to within ~1.5px of the bottom), plusvisibleRangeanditemCount.
This event fires as the user scrolls the list. The handler receives an object describing the
current scroll state: scrollTop (the current scroll offset), scrollHeight (the total
scrollable size), viewportSize (the visible size), and atEnd (true when the list is
scrolled to within ~1.5px of the bottom).
The object also includes visibleRange and itemCount, which you can use to display a range such as 234-245 of 1000.
The event fires only for user-driven scrolls; the list's own programmatic scrolls and its
scrollAnchor="bottom" auto-follow do not trigger it. This makes it suitable for
implementing follow-newest plus read-pause: track atEnd from this event, and when new data
arrives call scrollToBottom() only while the user is still at the end.
<App var.atBottom="{true}">
<Text>At bottom: {atBottom}</Text>
<List
data='{[...]}'
scrollAnchor="bottom"
onScroll="(e) => atBottom = e.atEnd">
<Text>{$item.name}</Text>
</List>
</App>selectAllAction
This event is triggered when the user presses the select all keyboard shortcut (default: Ctrl+A/Cmd+A) and rowsSelectable is set to true. The component automatically selects all rows before invoking this handler.
Signature: selectAll(row: ListRowContext | null, selectedItems: any[], selectedIds: string[]): void | Promise<void>
row: The currently focused row context, or null if no row is focused.selectedItems: Array of all selected row items.selectedIds: Array of all selected row IDs (as strings).
<App var.log="">
<Text>{log}</Text>
<List
data='{[...]}'
rowsSelectable="true"
enableMultiRowSelection="true"
onSelectAllAction="(row, items, ids) =>
log = 'Selected all: ' + ids.join(', ')
">
<Text>{$item.name}</Text>
</List>
</App>selectionDidChange
This event is triggered when the list's current selection changes. Its parameter is an array of the selected list row items.
Signature: selectionDidChange(selectedItems: any[]): void
selectedItems: An array of the selected list row items.
The handler receives an array of the selected items. If multi-row selection is disabled, the array will contain zero or one item.
<App var.selection="">
<Text>Selected: {selection}</Text>
<List data='{[...]}'
rowsSelectable="true"
enableMultiRowSelection="true"
onSelectionDidChange="(items) => selection = items.map(i => i.name).join(', ')">
<Text>{$item.name}</Text>
</List>
</App>visibleRangeDidChange
This event fires when the range of visible (mounted) items changes — whatever caused it: a user scroll, a programmatic scroll, or content growth. Unlike the scroll event, it is not suppressed during the list's own programmatic and auto-follow scrolls, because consumers of the visible range (e.g. prioritizing work for on-screen items) care about what is visible, not why it became visible. It fires only when the range actually shifts (deduplicated by value).
Signature: visibleRangeDidChange(range: { startIndex: number, endIndex: number }): void
range: The visible range:startIndex(first visible item index) andendIndex(last visible item index), inclusive, in the list's row order.
Exposed Methods
clearSelection
This method clears the list of currently selected list rows.
Signature: clearSelection(): void
The following example demonstrates clearSelection and the other selection API methods:
<App>
<HStack>
<Button label="Select all" onClick="list.selectAll()" />
<Button label="Select 0, 2" onClick="list.selectId([0, 2])" />
<Button label="Clear" onClick="list.clearSelection()" />
</HStack>
<List
id="list"
data='{[...]}'
rowsSelectable="true"
enableMultiRowSelection="true"
onSelectionDidChange="(items) => selection = items.map(i => i.id).join(', ')"
>
<Text>{$item.name}</Text>
</List>
</App>getItemCount
This method returns the number of items in the list's current virtualized row model.
Signature: getItemCount(): number
getSelectedIds
This method returns the list of currently selected list row IDs.
Signature: getSelectedIds(): Array<string>
(See the example at the clearSelection method)
getSelectedItems
This method returns the list of currently selected list row items.
Signature: getSelectedItems(): Array<any>
(See the example at the clearSelection method)
getVisibleRange
This method returns the currently visible item range as an object with startIndex and endIndex (inclusive, in the list's row order). Returns { startIndex: -1, endIndex: -1 } when the list is empty or not yet measured. The pull-style counterpart of the visibleRangeDidChange event.
Signature: getVisibleRange(): { startIndex: number, endIndex: number }
Use getVisibleRange() with getItemCount(), or read the same values from the scroll event, to display the currently visible item range.
<App
scrollWholePage="false"
var.itemCount="{0}"
var.range="{{ startIndex: -1, endIndex: -1 }}">
<Text
variant="strong"
value="{range.startIndex < 0
? 'No items'
: (range.startIndex + 1) + '-'
+ (range.endIndex + 1) + ' of ' + itemCount}"
/>
<List
id="list"
height="*"
onScroll="(e) => { range = e.visibleRange; itemCount = e.itemCount }"
onVisibleRangeDidChange="(r) => {
range = r;
itemCount = list.getItemCount()
}"
data="{Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
name: 'Item ' + (i + 1),
category: i % 2 === 0 ? 'Even' : 'Odd',
}))}">
<Text value="{$item.name + ' - ' + $item.category}" />
</List>
</App><App
scrollWholePage="false"
var.itemCount="{0}"
var.range="{{ startIndex: -1, endIndex: -1 }}">
<Text
variant="strong"
value="{range.startIndex < 0
? 'No items'
: (range.startIndex + 1) + '-'
+ (range.endIndex + 1) + ' of ' + itemCount}"
/>
<List
id="list"
height="*"
onScroll="(e) => { range = e.visibleRange; itemCount = e.itemCount }"
onVisibleRangeDidChange="(r) => {
range = r;
itemCount = list.getItemCount()
}"
data="{Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
name: 'Item ' + (i + 1),
category: i % 2 === 0 ? 'Even' : 'Odd',
}))}">
<Text value="{$item.name + ' - ' + $item.category}" />
</List>
</App>preserveStateOnNextDataRefresh
Preserve the current list view state for the next data refresh, even when dataRefreshMode is reset. Optional operation metadata controls post-refresh scroll behavior.
Signature: preserveStateOnNextDataRefresh(options?: { operation?: "insert" | "delete" | "update", scrollTarget?: string | number | "first-inserted" | "preserve" }): void
scrollToBottom
This method scrolls the list to the bottom.
Signature: scrollToBottom(): void
The following example demonstrates scrollToBottom and all the other scroll methods:
scrollToBottom() scrolls the list's resolved scroll container to its absolute end. When the List has its own bounded viewport, such as an explicit height or maxHeight, that viewport scrolls. In an outside-scroll arrangement, where the List has no bounded height and a parent element provides the scrolling viewport, the method scrolls that nearest overflow ancestor instead. It does not scroll an unrelated page or application shell scroller; scroll that outer container directly if it is the one the user sees moving.
scrollAnchor="bottom" uses the same bottom target for initial positioning and auto-follow as rows are appended, but it is declarative state, not a replacement for imperative calls. Use scrollAnchor when the list should stay pinned to the newest content, and scrollToBottom() when a user action or handler should jump to the end once.
<App layout="condensed-sticky" scrollWholePage="false">
<AppHeader>
<HStack>
<Button onClick="myList.scrollToBottom()">Scroll to Bottom</Button>
<Button onClick="myList.scrollToTop()">Scroll to Top</Button>
<Button onClick="myList.scrollToIndex(25)">Scroll to #25</Button>
<Button onClick="myList.scrollToId('item-40')">Scroll to ID 'item-40'</Button>
</HStack>
</AppHeader>
<List
id="myList"
height="*"
data="{
Array.from({ length: 100 })
.map((_, i) => ({id: 'item-' + i, value: 'Item #' + i}))
}">
<property name="itemTemplate">
<Card>
<Text value="{$item.value}" />
</Card>
</property>
</List>
</App><App layout="condensed-sticky" scrollWholePage="false">
<AppHeader>
<HStack>
<Button onClick="myList.scrollToBottom()">Scroll to Bottom</Button>
<Button onClick="myList.scrollToTop()">Scroll to Top</Button>
<Button onClick="myList.scrollToIndex(25)">Scroll to #25</Button>
<Button onClick="myList.scrollToId('item-40')">Scroll to ID 'item-40'</Button>
</HStack>
</AppHeader>
<List
id="myList"
height="*"
data="{
Array.from({ length: 100 })
.map((_, i) => ({id: 'item-' + i, value: 'Item #' + i}))
}">
<property name="itemTemplate">
<Card>
<Text value="{$item.value}" />
</Card>
</property>
</List>
</App>scrollToId
This method scrolls the list to a specific item. The method accepts an item ID as a parameter.
Signature: scrollToId(id: string): void
id: The ID of the item to scroll to.
See the scrollToBottom example.
scrollToIndex
This method scrolls the list to a specific index. The method accepts an index as a parameter.
Signature: scrollToIndex(index: number): void
index: The index to scroll to.
See the scrollToBottom example.
scrollToTop
This method scrolls the list to the top.
Signature: scrollToTop(): void
scrollToTop() scrolls the list's resolved scroll container to its absolute start. When the List owns its viewport, that means the top of the list. In an outside-scroll arrangement, where a parent element is the scrolling viewport, the call targets that nearest overflow ancestor and scrolls past any content that appears above the List inside the same scroller.
scrollAnchor="top" is the default initial anchor. It keeps the list positioned from the top during its own layout work, while scrollToTop() is the imperative API to jump the active list scroller back to the start. If your UI uses a separate page-level scroller outside the List's resolved scroll parent, call that container's scrolling API instead.
See the scrollToBottom example.
selectAll
This method selects all the rows in the list. This method has no effect if the rowsSelectable property is set to false.
Signature: selectAll(): void
(See the example at the clearSelection method)
selectId
This method selects the row with the specified ID. This method has no effect if the rowsSelectable property is set to false. The method argument can be a single id or an array of them.
Signature: selectId(id: string | Array<string>): void
id: The ID of the row to select, or an array of IDs to select multiple rows.
(See the example at the clearSelection method)
Styling
List supports the following theme variables for customizing selection colors and the
appearance of selection checkboxes.
Selection colors:
| Theme variable | Default |
|---|---|
backgroundColor-List | $backgroundColor |
backgroundColor-selected-List | $color-primary-100 |
backgroundColor-selected-List--hover | $color-primary-100 |
backgroundColor-row-List--hover | $color-primary-50 |
Selection checkbox appearance — each variable falls back to the equivalent Checkbox
component theme variable when not explicitly set, so the checkboxes automatically
follow your form input styling:
| Theme variable | Fallback |
|---|---|
borderRadius-selectionCheckbox-List | borderRadius-Checkbox |
borderColor-selectionCheckbox-List | borderColor-Checkbox |
backgroundColor-selectionCheckbox-List | backgroundColor-Checkbox |
borderColor-checked-selectionCheckbox-List | borderColor-checked-Checkbox |
backgroundColor-checked-selectionCheckbox-List | backgroundColor-checked-Checkbox |
backgroundColor-indicator-selectionCheckbox-List | backgroundColor-indicator-Checkbox |
outlineWidth-selectionCheckbox-List--focus | outlineWidth-Checkbox--focus |
outlineColor-selectionCheckbox-List--focus | outlineColor-Checkbox--focus |
outlineStyle-selectionCheckbox-List--focus | outlineStyle-Checkbox--focus |
outlineOffset-selectionCheckbox-List--focus | outlineOffset-Checkbox--focus |
Theme Variables
| Variable | Default Value (Light) | Default Value (Dark) |
|---|---|---|
| backgroundColor-checked-selectionCheckbox-List | none | none |
| backgroundColor-indicator-selectionCheckbox-List | none | none |
| backgroundColor-List | $backgroundColor | $backgroundColor |
| backgroundColor-row-List--hover | $color-primary-50 | $color-primary-50 |
| backgroundColor-selected-List | $color-primary-100 | $color-primary-100 |
| backgroundColor-selected-List--hover | $color-primary-100 | $color-primary-100 |
| backgroundColor-selectionCheckbox-List | none | none |
| borderColor-checked-selectionCheckbox-List | none | none |
| borderColor-selectionCheckbox-List | none | none |
| borderRadius-selectionCheckbox-List | none | none |
| outlineColor-selectionCheckbox-List--focus | none | none |
| outlineOffset-selectionCheckbox-List--focus | none | none |
| outlineStyle-selectionCheckbox-List--focus | none | none |
| outlineWidth-selectionCheckbox-List--focus | none | none |