Examples
Virtualized select

Virtualized Select

Render a Select that virtualizes its options.

import { Select, createListCollection } from '@ark-ui/react/select'
import { type ScrollToOptions, useVirtualizer } from '@tanstack/react-virtual'
import { ChevronsUpDownIcon } from 'lucide-react'
import { useMemo, useRef } from 'react'
import { items } from './data'

interface ScrollToIndexDetails {
  index: number
  immediate?: boolean
}

export const Example = () => {
  const contentRef = useRef<HTMLDivElement>(null)

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => contentRef.current,
    estimateSize: () => 32,
  })

  const collection = useMemo(
    () =>
      createListCollection({
        items,
      }),
    [],
  )

  const timerRef = useRef<NodeJS.Timer>()

  const handleScrollToIndexFn = (details: ScrollToIndexDetails) => {
    const scrollOptions: ScrollToOptions = { align: 'center', behavior: 'auto' }
    if (details.immediate) {
      virtualizer.scrollToIndex(details.index, scrollOptions)
    } else {
      clearTimeout(timerRef.current)
      timerRef.current = setTimeout(() => {
        virtualizer.scrollToIndex(details.index, scrollOptions)
      })
    }
  }

  return (
    <Select.Root
      collection={collection}
      scrollToIndexFn={handleScrollToIndexFn}
      positioning={{ sameWidth: true }}
    >
      <Select.Label>Country</Select.Label>
      <Select.Control>
        <Select.Trigger>
          <Select.ValueText placeholder="Select a Country" />
          <ChevronsUpDownIcon />
        </Select.Trigger>
      </Select.Control>
      <Select.Positioner>
        <Select.Content
          ref={contentRef}
          style={{
            height: '400px',
            overflow: 'auto',
          }}
        >
          <div
            style={{
              height: `${virtualizer.getTotalSize()}px`,
              width: '100%',
              position: 'relative',
            }}
          >
            {virtualizer.getVirtualItems().map((virtualItem) => {
              const item = items[virtualItem.index]
              return (
                <Select.Item
                  key={item.value}
                  item={item}
                  style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    width: '100%',
                    height: `${virtualItem.size}px`,
                    transform: `translateY(${virtualItem.start}px)`,
                    whiteSpace: 'nowrap',
                    overflow: 'hidden',
                    textOverflow: 'ellipsis',
                  }}
                >
                  <Select.ItemText>{item.label}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              )
            })}
          </div>
        </Select.Content>
      </Select.Positioner>
      <Select.HiddenSelect />
    </Select.Root>
  )
}