Skip to content
KaUI is under active development. If you run into a bug, please open an issue.

Combobox

A single-select combobox with inline search, async loading support, and full controlled/uncontrolled API.

pnpm dlx shadcn@latest add @kaui/combobox
import { Combobox } from "@/components/ui/combobox";
import type { SelectionOption } from "@/hooks/use-filtered-options";
const options: SelectionOption<string>[] = [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
];
<Combobox
items={options}
selected={selected}
onSelectedChange={setSelected}
placeholder="Search..."
closeAfterSelect
/>;

Each option is { value: T, label: string, disabled?: boolean }. The generic T extends string so you can keep values fully typed.

Use startAddon and endAddon to embed any element inside the input — a search icon, a clear button, a status indicator. The slots are rendered inside InputGroup so they align and interact correctly with the text field.

Wire onQueryChange to a debounced fetch, set disableLocalFilter so the component shows whatever you pass in items, and toggle isLoading while the request is in flight. The skeleton appears in place of the list until results arrive.

Pass renderOption to take full control of how each item looks. Receives the option object and a boolean for selected state — use them to show icons, badges, or secondary text.

Use emptyContent with a controlled query to show an inline “Add” action when no results match. onMouseDown on the button prevents the input from blurring (which would close the dropdown before the click fires).

PropTypeDefaultDescription
selectedSelectionOption<T> | nullRequired. The currently selected option, or null
onSelectedChange(value: SelectionOption<T> | null) => voidRequired. Called when selection changes; receives null on deselect
itemsSelectionOption<T>[]Required. Full list of options to display
openbooleanControlled open state. Omit to let the component manage it
onOpenChange(open: boolean) => voidCalled whenever the dropdown opens or closes
querystringControlled search query. Omit for uncontrolled
onQueryChange(query: string) => voidCalled whenever the search input changes
placeholderstring"Search..."Placeholder text shown in the search input
closeAfterSelectbooleanfalseClose the dropdown immediately after an option is selected
disableLocalFilterbooleanSkip client-side filtering. Use when items are already filtered server-side
filterFn(item: SelectionOption<T>, query: string) => booleanCustom filter predicate. Replaces the default case-insensitive label match
isLoadingbooleanfalseShow the loading state in place of the option list
emptyContentReactNode"No results found."Content shown when no options match the query
loadingContentReactNodeSkeleton barContent shown in place of the list while isLoading is true
renderOption(option: SelectionOption<T>, selected: boolean) => ReactNodeCustom renderer for each list item
startAddonReactNodeLeading addon rendered inside the input (e.g. a search icon)
endAddonReactNodeTrailing addon rendered inside the input (e.g. a clear button)

All PopoverContent props (align, side, sideOffset, etc.) are also accepted and forwarded to the dropdown.