Combobox
An input with a filterable dropdown list — local options with client-side filtering, or async remote search with debounced fetching and scroll-to-load-more
Installation
Usage
Basic Usage
Pass options as local string options — typing filters them case-insensitively, ArrowDown / ArrowUp move the highlight, Enter or a click on a row selects.
"use client";
import { Combobox } from "@/ui";
const countries = [
"China",
"United States",
"Japan",
"Germany",
"France",
"United Kingdom",
"Canada",
"Australia",
"Italy",
"Brazil",
];
export default function Demo() {
return <Combobox options={countries} placeholder="Select a country" />;
}
Async Search
Pass fetcher instead of options for large or remote datasets — keyword search is debounced and loadMore fires when the list is scrolled to the bottom.
"use client";
import { Combobox, type ComboboxFetcher } from "@/ui";
const fetchAsyncOptions: ComboboxFetcher = async ({ page, size, keyword }) => {
await new Promise((resolve) => setTimeout(resolve, 200));
const totalItems = 1000;
const allItems = Array.from({ length: totalItems }, (_, i) => `Item ${i + 1}`);
const filtered = keyword
? allItems.filter((item) => item.toLowerCase().includes(keyword.toLowerCase()))
: allItems;
const start = (page - 1) * size;
const paged = filtered.slice(start, start + size);
return { list: paged, total: filtered.length };
};
export default function Demo() {
return <Combobox fetcher={fetchAsyncOptions} placeholder="Search items" />;
}
Notes
- Combobox is composed internally from Picker, List, and the useCombobox / useRemotePagination utils — installing via CLI resolves them automatically.
fetchertakes precedence overoptionswhen both are provided.- Async mode searches on mount and on every keystroke (debounced); the empty state shows "Loading..." while a request is in flight.
- Focus never leaves the input:
ArrowDownopens the panel, then arrows drive the highlight andEnterselects; the panel closes on selection, outside mousedown, or Escape. - After selecting, the input text equals the chosen option — the list filters against it on reopen; clear the text to see all options again.
API Reference
Combobox
| Prop | Type | Default | Description |
|---|---|---|---|
options | string[] | - | Local options for client-side filtering |
fetcher | ({ page, size, keyword }) => Promise<{ list: string[]; total: number }> | - | Remote fetcher for async mode |
defaultValue / value | string | "" | Input text (uncontrolled / controlled) |
onValueChange | (value: string) => void | - | Fired on typing and on selection |
invalid | boolean | false | Invalid state styling on the input |
empty | React.ReactNode | "No data" / "Loading..." | Empty-state node rendered in the list |
pageSize | number | 20 | Page size for async fetching |
debounceMs | number | 300 | Debounce delay for async keyword search |
className | ClassNameValue | - | Custom classes, applied to the input wrapper |
classNames | { panel? / item? } | - | Custom classes for the panel and list items |
Chip Group
A primitive component that automatically collapses overflowing chips, exposing the hidden set via renderMore / onOverflowChange for consumer-side composition
Date Picker
A date picker built on the native Temporal API — an editable input, a month panel and pointer-anchored popover in one shipped component