Virtualized Select
Render a Select that virtualizes its options.
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>
)
}