Virtualizer

Render large lists, grids, and window-scrolled content by only mounting the items in view.

Loading...

Usage

The virtualizer keeps the DOM small by rendering only the items that intersect the scroll viewport, plus a few extra for smooth scrolling. It comes in three flavors, each with a hook and a matching set of components:

  • useListVirtualizer + ListVirtualizer for vertical or horizontal lists that scroll inside their own container.
  • useGridVirtualizer + GridVirtualizer for two-dimensional grids of rows and cells.
  • useWindowVirtualizer + WindowVirtualizer for lists that scroll with the window or the nearest scrollable ancestor.

Create a virtualizer with one of the hooks, pass it to Root as value, render the spacer with Content, and map the virtual items to Item elements.

import { ListVirtualizer, useListVirtualizer } from '@ark-ui/react/virtualizer'

const virtualizer = useListVirtualizer({
  count: items.length,
  estimatedSize: () => 48,
})

<ListVirtualizer.Root value={virtualizer}>
  <ListVirtualizer.Content>
    {virtualizer.getVirtualItems().map((item) => (
      <ListVirtualizer.Item key={item.key} item={item}>
        {items[item.index]}
      </ListVirtualizer.Item>
    ))}
  </ListVirtualizer.Content>
</ListVirtualizer.Root>

Root is the scroll container: it receives the ARIA attributes, the overflow styles, and the scroll listener. Give it a fixed height (or width for horizontal lists) so it can scroll. Content is the spacer that grows to the total size of the list. Item is positioned absolutely with a transform; its size comes from your CSS or from the estimate, so give your items a height that matches estimatedSize, or opt into measurement as shown below.

List

Dynamic Size

When items have different heights, set the measure prop on Item. The rendered size of each item is then measured and fed back into the virtualizer, so estimatedSize only needs to be a rough guess.

Horizontal

Set orientation to horizontal to virtualize along the x-axis. Content grows in width and items are translated horizontally.

Scroll to Index

Use scrollToIndex on the virtualizer to jump to an item. It accepts an align option (start, center, end, or auto) and a smooth option for animated scrolling.

Grid

The grid virtualizer is row-first: map getVirtualRows() to Row elements and each row's columns to Cell elements. Cell reads its row from context, so it only needs the column. Rows can opt into height measurement with the measure prop.

Window

The window virtualizer delegates scrolling to the window or the nearest scrollable ancestor, so Root does not need a height or an overflow style. Use it for long pages where the list is part of the document flow.

Using the Context

Context exposes the virtualizer to child components, which is useful for rendering derived state such as the visible range.

<ListVirtualizer.Root value={virtualizer}>
  <ListVirtualizer.Context>
    {(virtualizer) => <span>{virtualizer.getVirtualItems().length} items rendered</span>}
  </ListVirtualizer.Context>
</ListVirtualizer.Root>

Composing with other components

The virtualizer parts compose with any Ark component through the render prop. Render the component's scroll container as ListVirtualizer.Root and each of its items as ListVirtualizer.Item; the component's own props (roles, ids, handlers) win over the virtualizer's, and the virtualizer adds the positioning and the aria-posinset / aria-setsize attributes.

<Combobox.List render={<ListVirtualizer.Root value={virtualizer} />}>
  <ListVirtualizer.Content>
    {virtualizer.getVirtualItems().map((virtualItem) => {
      const item = collection.items[virtualItem.index]
      return (
        <Combobox.Item key={item.value} item={item} render={<ListVirtualizer.Item item={virtualItem} />}>
          <Combobox.ItemText>{item.label}</Combobox.ItemText>
        </Combobox.Item>
      )
    })}
  </ListVirtualizer.Content>
</Combobox.List>

Pass the component's scrollToIndexFn through to virtualizer.scrollToIndex so keyboard navigation reaches items that are not mounted. See the virtualized examples for Select, Combobox, Listbox, and Tree View.

API Reference

GridCell

Renders a <div> element.

PropDefaultType
column
VirtualColumn

The virtual column to render within the current row.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

GridContent

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

GridRoot

Renders a <div> element.

PropDefaultType
value
UseGridVirtualizerReturn

The virtualizer instance returned by `useGridVirtualizer`.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

GridRow

Renders a <div> element.

PropDefaultType
row
VirtualRow

The virtual row to render.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
measurefalse
boolean

Whether to measure the rendered height of the row and use it instead of the estimate.

render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

ListContent

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

ListItem

Renders a <div> element.

PropDefaultType
item
VirtualItem

The virtual item to render.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
measurefalse
boolean

Whether to measure the rendered size of the item and use it instead of the estimate.

render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

ListRoot

Renders a <div> element.

PropDefaultType
value
UseListVirtualizerReturn

The virtualizer instance returned by `useListVirtualizer`.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

WindowContent

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

WindowItem

Renders a <div> element.

PropDefaultType
item
VirtualItem

The virtual item to render.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
measurefalse
boolean

Whether to measure the rendered size of the item and use it instead of the estimate.

render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.

WindowRoot

Renders a <div> element.

PropDefaultType
value
UseWindowVirtualizerReturn

The virtualizer instance returned by `useWindowVirtualizer`.

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
render
ReactElement<unknown, string | JSXElementConstructor<any>> | RenderFn<EmptyState>

Render the part as a custom element, combining their props and behavior. Pass an element to have the part's props merged into it, or a function to control the merge yourself and read the part's state.