Single-value picker with a searchable text input, built on Headless UI Combobox. As the user types, the option list is filtered (accent-insensitive); options can also be loaded asynchronously.
Reach for Combobox when the user picks one value from a list and should be able to filter by typing (or the options come from a server).
Select.MultiCombobox.Use @uxf/ui/combobox for a controlled combobox outside a form. Inside a @uxf/form form, use @uxf/form/combobox instead — it registers the field with react-hook-form and derives isInvalid / helperText from validation. This @uxf/ui component is the controlled primitive the form twin wraps.
import { Combobox, ComboboxOption, ComboboxValue } from "@uxf/ui/combobox";
import { useState } from "react";
const options: ComboboxOption[] = [
{ id: "one", label: "Option one" },
{ id: "two", label: "Option two", disabled: true },
{ id: "three", label: "Option three" },
];
function Example() {
const [value, setValue] = useState<ComboboxValue | null>(null);
return (
<Combobox
isClearable
label="Choose an option"
name="example"
onChange={setValue}
options={options}
placeholder="Search..."
value={value}
/>
);
}
Async options — pass loadOptions instead of (or alongside) options; the returned promise supplies the list and local filtering is skipped:
<Combobox
label="Country"
loadOptions={(query) => fetchCountries(query)}
name="country"
onChange={setValue}
placeholder="Search country..."
value={value}
/>
value is the selected option object (ComboboxValue, i.e. { id, label }) or null, and onChange receives that whole object — not just the id.
Exports: Combobox and the types ComboboxProps, ComboboxOption, ComboboxValue, ComboboxValueId, ComboboxTypeRef.
ComboboxProps<Id = ComboboxValueId, Option = ComboboxOption<Id>, Value = Option> extends FormControlProps<Value | null> and Clearable. ComboboxValueId is number | string; ComboboxValue is { id: Id; label: string }; ComboboxOption adds disabled?: boolean.
| Prop | Type | Default | Description |
|---|---|---|---|
label |
ReactNode |
— | Required. Field label. |
name |
string |
— | Required. Field name (also set as data-name). |
value |
Value | null |
— | Required. Selected option object, or null. |
onChange |
(value: Value | null) => void |
— | Required. Called with the selected option object (or null). |
options |
Option[] |
— | Static option list. Optional; omit when using loadOptions. |
loadOptions |
(query: string) => Promise<Option[]> |
— | Async option loader. When set, the component is async and skips local filtering. |
placeholder |
string |
— | Placeholder for the text input. |
helperText |
ReactNode |
— | Text under the control; styled as an error when isInvalid. |
isClearable |
boolean |
false |
Show a clear button that resets the value to null. |
isDisabled |
boolean |
false |
Disable the control. |
isReadOnly |
boolean |
false |
Prevent changes while staying focusable. |
isInvalid |
boolean |
false |
Invalid styling; sets aria-invalid and styles helperText as an error. |
isRequired |
boolean |
false |
Mark the field required (adds the label indicator). |
hiddenLabel |
boolean |
false |
Visually hide the label (kept for assistive tech). |
renderOption |
(option: Option, isSelected: boolean) => ReactNode |
option.label |
Custom renderer for each dropdown option. |
keyExtractor |
(option: Option) => string | number |
option.id |
Custom React key for options. |
noQueryMessage |
string |
localized "Start typing to see options" | Shown when the query is empty. |
noOptionsMessage |
string |
localized "No items found" | Shown when there are no options. |
notFoundMessage |
string |
"Nic nenalezeno" |
Shown when a non-empty query matches nothing. |
iconName |
IconName |
arrow icon | Icon for the dropdown arrow. |
size |
InputGroupSize |
"default" |
"small", "default", or "large". |
variant |
InputGroupVariant |
"default" |
Input group variant. |
id |
string |
auto (useId) |
Root id; the error-message id derives from it. |
onBlur / onFocus |
FocusEventHandler<HTMLInputElement> |
— | Focus handlers. |
Layout, dropdown, and ref props: className, style, form, leftAddon, rightAddon, leftElement, rightElement, inputArrow, dropdownClassName, dropdownMatchesInputWidth (default true), dropdownMaxHeight (default 240), dropdownPlacement (default "bottom"), dropdownStrategy, inputRef, inputGroupRef, inputWrapperRef.
options locally by label (accent-insensitive via slugify). With loadOptions, filtering is delegated to the loader (debounced ~200 ms).small, default, large (via size).isInvalid; combine with helperText to show an error message.isDisabled blocks interaction; isReadOnly keeps focus but prevents changes.isClearable, a remove button appears once a value is selected (unless disabled/read-only).The component composes the input, label, dropdown, form-component, and icon primitives, so import all of their stylesheets once in your global CSS:
@import url("@uxf/ui/css/icon.css");
@import url("@uxf/ui/css/dropdown.css");
@import url("@uxf/ui/css/label.css");
@import url("@uxf/ui/css/form-component.css");
@import url("@uxf/ui/css/input-basic.css");
@import url("@uxf/ui/css/input.css");
@import url("@uxf/ui/css/combobox.css");
Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.