Skip to content

Combobox

import { Combobox } from '@yarcl/react';

With a controlled value, the input shows the selected option’s label on mount and follows changes to the value or its label, including options loaded later. Typed searches remain through option updates until selection, blur or dismissal. Changing the selected value resets the search. A controlled inputValue takes precedence over the selected label.

One component covers four modes.

The default. Typing filters the options; the value must be one of them. On blur, the text resets to the selected option’s label.

Value: none

<Combobox options={countries} value={country} onValueChange={setCountry} placeholder="Search countries" />

allowCustomValue accepts any text; the options are suggestions.

Value: none

<Combobox options={frameworks} allowCustomValue onValueChange={setValue} />

Filter on the server: set filter={false}, update options from onInputValueChange, and show loading in between.

Results arrive after 500 ms.

<Combobox
options={results}
filter={false}
loading={loading}
onInputValueChange={setQuery}
emptyMessage="No countries found"
/>

multiple makes the value an array. Each pick becomes a removable chip before the input; the list stays open and the input clears so you can keep going. Picking a selected option again removes it. maxSelected caps the count, and name submits one hidden input per value.

It works with the other modes: add allowCustomValue to turn typed text into a chip on Enter, or filter={false} with onInputValueChange for async search.

  • Sweden
  • Japan

Value: sweden, japan

const [visited, setVisited] = useState<string[]>([]);
<Combobox multiple options={countries} value={visited} onValueChange={setVisited} name="visited" />

Chips are Badges in the control’s size and color, with the Badge’s variant (by default defaults.softVariant). The control keeps the input height on one line and grows as chips wrap.

Key Action
typing filters and opens the list, highlighting the first match
↑ ↓ moves the highlight (focus stays in the input)
Enter chooses the highlighted option; with multiple, toggles it
Esc closes the list
Backspace with multiple and an empty input, removes the last chip
← → with multiple, at the start of the input, moves focus across the chips and back
Backspace Delete Enter on a chip, removes it and returns focus to the input

The input has role="combobox" and points at the highlighted option with aria-activedescendant. With multiple, the listbox has aria-multiselectable and selected options have aria-selected. Chips are a list named “Selected”, and each remove button is labelled with the option, like “Remove Sweden”. Chips are not tab stops, so Tab goes straight to the input.

Single and multiple selection popups follow shared components.Listbox.slots settings with data-part="panel" and "item". The same settings style Select popups. See component parts.

Accepts every native <input> attribute except color, size, value, defaultValue, onChange and multiple.

Plus the value props, typed by multiple:

Prop Single (default) multiple
value V | null V[]
defaultValue V | null V[]
onValueChange (value: V | null) => void (value: V[]) => void
maxSelected not allowed number
PropTypeDefaultDescription
options requiredreadonly SelectOption<V>[]The options to suggest. For async search, update this as results arrive and set filter={false}.
allowCustomValuebooleanfalseAccepts typed text that doesn't match an option (typeahead / autocomplete). When false, unchosen text is discarded on blur: the input resets to the selected option's label, or clears with multiple. With multiple, Enter adds the typed text as a chip.
colorColorconfigconfig.defaults.colorSemantic color, from the colors config.
emptyMessageReactNodeconfig.labels.noResultsShown when no options match.
filterboolean | (option: SelectOption<V>, text: string) => booleantrueHow options are matched against the typed text. true: case-insensitive "contains" on the label; a function for custom matching; false to show options as given (e.g. already filtered by a server).
inputValuestringControlled text in the input.
loadingbooleanShows a loading row instead of options.
loadingMessageReactNodeconfig.labels.loadingResultsShown while loading is set.
onInputValueChange(text: string) => voidCalled on every keystroke with the input text. Use it to fetch options for async search.
radius'size' | Radiusconfigconfig.defaults.radiusBorder radius, from the radii config, or 'size' for the radius named like the control's size.
removeLabel(name: string) => stringconfig.labels.removeItemAccessible name of a chip removal button, given the option label.
selectedLabelstringconfig.labels.selectedItemsAccessible name of the multi-select chip list.
size'lg' | 'xs' | 'sm' | 'md' | 'xl'config.defaults.sizeControl size, from the sizes config.