Async Loading

Load options from an API with built-in debounce, caching, and loading states. No external state management needed.

Async loading with 300ms debounce · Cached for 30s

Pass an async config object instead of static options:

import { useVirtualSelect } from 'virtualized-ui';
import type { AsyncConfig } from 'virtualized-ui';

const asyncConfig: AsyncConfig<User> = {
  loadOptions: async (inputValue) => {
    const res = await fetch(
      `/api/users?q=${inputValue}`
    );
    return res.json();
  },
  debounceMs: 300,
  loadOnOpen: true,
  cacheTtlMs: 30000,
};

const select = useVirtualSelect({
  getOptionValue: (u) => u.id,
  getOptionLabel: (u) => u.name,
  async: asyncConfig,
  searchable: true,
});

// Loading state
select.isLoading; // true while fetching

The hook handles debouncing, caching, and cleanup automatically. When async is provided, static options are ignored.

AsyncConfig

OptionTypeDefaultDescription
loadOptions(input: string) => Promise<T[] | OptionGroup<T>[]>requiredFetch function
debounceMsnumber300Debounce delay in ms
loadOnOpenbooleantrueCall loadOptions when menu opens
cacheTtlMsnumber0Cache TTL in ms (0 = no cache)

Loading & Empty States

The isLoading return value lets you render custom loading UI:

const { isLoading, flattenedItems } = useVirtualSelect({ async: asyncConfig });

if (isLoading) return <Spinner />;
if (flattenedItems.length === 0) return <p>No results</p>;

Or customize via component slots:

<VirtualSelect
  async={asyncConfig}
  components={{
    Loading: () => <div>Searching...</div>,
    NoOptions: ({ searchValue }) => (
      <div>No results for "{searchValue}"</div>
    ),
  }}
/>
Powered by grishaLR
© 2026 virtualized-ui. All rights reserved.