VirtualSelect

A headless virtualized select component with search, multi-select, grouped options, and async loading. Built on TanStack Virtual.

Basic Usage

10,000 items · Single select · Virtualized

Use the useVirtualSelect hook for full control:

import { useVirtualSelect } from 'virtualized-ui';

const select = useVirtualSelect({
  options: countries,
  getOptionValue: (c) => c.code,
  getOptionLabel: (c) => c.name,
  searchable: true,
  placeholder: 'Choose a country...',
});

Or use the VirtualSelect component:

import { VirtualSelect } from 'virtualized-ui';

<VirtualSelect
  options={countries}
  getOptionValue={(c) => c.code}
  getOptionLabel={(c) => c.name}
  searchable
  placeholder="Choose a country..."
/>
OptionTypeDefault
optionsT[] | OptionGroup<T>[]
getOptionValue(o) => stringrequired
getOptionLabel(o) => stringrequired
placeholderstring
disabledbooleanfalse

Features

Explore each feature on its own page:

Return Values (Hook)

const {
  // Virtualizer
  virtualizer,      // TanStack Virtual instance
  virtualItems,     // Currently visible virtual items
  totalSize,        // Total scrollable height (px)
  menuRef,          // Ref for menu scroll container
  measureElement,   // Ref callback for dynamic sizing

  // State
  isOpen,           // Whether dropdown is open
  searchValue,      // Current search text
  focusedIndex,     // Currently focused option index
  selectedValues,   // Array of selected value strings
  selectedOptions,  // Array of selected option objects
  isLoading,        // Whether async options are loading
  flattenedItems,   // Flattened items (options + group headers)

  // Actions
  open, close, toggle,
  setSearch, selectValue, deselectValue, toggleValue, clearAll,
  setFocusedIndex, openSubMenu, closeSubMenus,

  // Event handlers
  handleKeyDown,    // For the container/trigger
  handleMenuKeyDown,// For the menu list
  handleSearchInput,// For the search input

  // Refs
  containerRef, triggerRef, inputRef,

  // ARIA helpers
  getTriggerProps, getMenuProps, getOptionProps, getInputProps,
} = useVirtualSelect(options);

Controlled vs Uncontrolled

Uncontrolled

State managed internally:

const { selectedValues } = useVirtualSelect({
  options: items,
  getOptionValue: (o) => o.id,
  getOptionLabel: (o) => o.name,
});
// `selectedValues` reflects internal state

Controlled

You own the state:

const [value, setValue] = useState<string[]>([]);
useVirtualSelect({
  options: items,
  getOptionValue: (o) => o.id,
  getOptionLabel: (o) => o.name,
  value,
  onValueChange: setValue,
});
Powered by grishaLR
© 2026 virtualized-ui. All rights reserved.