Multi-value picker with a searchable text input, built on Headless UI Combobox in multiple mode. Selected values render as removable chips; options are filtered as the user types and can be loaded asynchronously.
Reach for MultiCombobox when the user picks several values from a list and should be able to filter by typing (or the options come from a server).
Combobox.MultiSelect.Use @uxf/ui/multi-combobox for a controlled component outside a form. Inside a @uxf/form form, use @uxf/form/multi-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 { MultiCombobox, MultiComboboxOption, MultiComboboxValue } from "@uxf/ui/multi-combobox";
import { useState } from "react";
const options: MultiComboboxOption<string>[] = [
{ color: "red", id: "one", label: "Option red" },
{ color: "blue", id: "two", label: "Option blue" },
{ id: "three", label: "Option three" },
];
function Example() {
const [value, setValue] = useState<MultiComboboxValue<string>[] | null>(null);
return (
<MultiCombobox
label="Choose options"
name="example"
onChange={setValue}
options={options}
placeholder="Search..."
value={value}
/>
);
}
value is an array of option objects (MultiComboboxValue[], i.e. { id, label }[]) or null, and onChange receives the new array. Selected chips can be removed with their close button or by pressing Backspace while the query is empty.
Exports: MultiCombobox and the types MultiComboboxProps, MultiComboboxOption, MultiComboboxValue, MultiComboboxValueId, MultiComboboxRef.
MultiComboboxProps<ValueId = MultiComboboxValueId, Option = MultiComboboxOption<ValueId>> extends FormControlProps<Option[] | null>. MultiComboboxValueId is number | string; MultiComboboxValue is { id: ValueId; label: string }; MultiComboboxOption adds color?: ChipColor and disabled?: boolean. It is not Clearable — there is no isClearable prop.
| Prop | Type | Default | Description |
|---|---|---|---|
label |
ReactNode |
— | Required. Field label. |
name |
string |
— | Required. Field name (also set as data-name). |
value |
Option[] | null |
— | Required. Selected option objects, or null. |
onChange |
(value: Option[]) => void |
— | Required. Called with the new array of selected options. |
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. |
withCheckboxes |
boolean |
false |
Render options as a checkbox list (keeps selected options visible in the list). |
placeholder |
string |
— | Placeholder for the text input. |
helperText |
ReactNode |
— | Text under the control; styled as an error when isInvalid. |
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) => ReactNode |
option.label |
Custom renderer for each dropdown option (ignored when withCheckboxes). |
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 |
localized "Nothing found" | Shown when a non-empty query matches nothing. |
allOptionsSelectedMessage |
string |
localized "All options are already selected" | Shown when every option is selected. |
iconName |
IconName |
"caretDown" |
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. |
The component is a forwardRef — the ref points at the text <input> (MultiComboboxRef = HTMLInputElement). Layout/dropdown props: className, style, form, leftAddon, rightAddon, leftElement, rightElement, inputArrow, dropdownClassName, dropdownMatchesInputWidth, dropdownMaxHeight (default 240), dropdownPlacement (default "bottom"), dropdownStrategy.
options locally by label (accent-insensitive via slugify) and hides already-selected options; with loadOptions, filtering is delegated to the loader (debounced ~200 ms).withCheckboxes renders each option as a checkbox and keeps selected options in the list rather than hiding them.Chip; its color comes from the option's color. Chips are removable unless the control or the option is disabled.small, default, large (via size).isInvalid, isDisabled, isReadOnly).The component composes the input, label, dropdown, form-component, icon, and chip primitives, so import 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/chip.css");
@import url("@uxf/ui/css/multi-combobox.css");
When using withCheckboxes, also import the checkbox stylesheets:
@import url("@uxf/ui/css/checkbox.css");
@import url("@uxf/ui/css/checkbox-input.css");
Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.