Skip to content

Components · Forms

Combobox

Searchable single or multiple select with groups, async search, a create option and an inline mode.

@rookcom/monarch-reactComponent

Searchable picker

Usage examples

Multiple with groups and hints

Server search with a typed fallback

Inline list

Purpose

A select people type into: the input is the combobox and the options are a listbox. It covers long static lists, server search with debounce and abort, several values as chips, and the inline list that EntityPicker and TimezoneSelect build on.

Use when

  • The list is long enough that people search rather than scan.
  • Options come from a server: pass onSearch and Combobox handles debounce, abort, loading and errors.
  • Several values from a known list: multiple.
  • A picker list inside a Popover, Dialog or Sheet: inline.

Avoid when

  • A short, fixed list: use Select.
  • A record picker opened from a button or chip: use EntityPicker.
  • Free-form values with no list: use TagInput.

Design principles

  • The input keeps focus; options follow the aria-activedescendant pattern (WAI-ARIA APG combobox).
  • In single mode the input always reflects the selection: unmatched text is reverted on blur and Escape.
  • The product owns data and rules: search, creation and option content come in through props.
  • Late server answers never overwrite newer ones: a new query aborts the last request.

API reference

import { Combobox, matchComboboxOption } from '@rookcom/monarch-react';
ExportKindRenderingRoot class
Comboboxcomponentclient.mn-combobox-
matchComboboxOptionutilityclient–

Forwards its ref to the <input>. Native input attributes pass through, except value, defaultValue, onChange, size and type.

matchComboboxOption(option, query) is the default filter, exported for products that extend it.

Single mode uses value / defaultValue / onValueChange; multiple uses values / defaultValues / onValuesChange.

Generated from the TypeScript declarations of packages/react/src/components/forms/combobox.

Properties

onSearch(query, { signal }) returns a promise of options. Pass the signal to fetch so superseded requests are cancelled.

onCreate(query) may return the new option (or a promise of it) to select it.

Combobox

Searchable select. The input is the combobox and the options are a listbox (WAI-ARIA APG combobox pattern with `aria-activedescendant`): single or multiple selection, grouped options with media and hints, async search with debounce and abort, a create option and an inline mode for pickers. Keyboard: Down/Up move, Home/End jump, PageDown/PageUp skip ten, Enter selects, Escape closes, Alt+Down/Alt+Up open and close, typing filters.

PropTypeDescription
childrenReactNode
classNamestring
clearablebooleanAllow clearing the selection with the clear button / Backspace on empty input.
clearOptionComboboxClearOptionThe list starts with an option that clears the selection, such as "Unassigned" or "No country".
closeOnSelectbooleanClose the list after a pick. Default: true, or false with `multiple` or `inline`.
defaultOpenboolean
defaultValuestring | null
defaultValuesstring[]
disabledboolean
emptyMessageReactNodeShown when no option matches.
filter((option: ComboboxOption, query: string) => boolean)Custom filter. Default: case-insensitive match on label, hint and keywords. Pass `() => true` for server-filtered lists.
idstring
inlinebooleanShow the list inline under a search input, always open, instead of in a popover: for pickers inside a Popover, Dialog or Sheet. The input is a search box: it never shows the selected label.
inputValuestringText typed in the input (controlled). Use for server-side filtering.
invalidboolean
limitnumberRender at most this many matching options, with a message to keep typing. Default: all.
listLabelstringAccessible name of the listbox. Default: the Field label, `aria-label`, or "Options".
loadingbooleanShown while options load.
messagesPartial<ComboboxMessages>Interface text, for translation.
minSearchLengthnumberShortest query `onSearch` runs for; shorter queries show `emptyMessage`. Default 0.
multiplebooleanSelect several values. Use `values`, `defaultValues` and `onValuesChange`; the list stays open after each pick.
onCreate((query: string) => ComboboxOption | null | undefined | Promise<ComboboxOption | null | undefined>)Adds a "Create “query”" option when the typed text matches no option's label. Return the new option to select it; return nothing to only close (single) or clear the text (multiple).
onInputValueChange((text: string) => void)
onOpenChange((open: boolean) => void)
onSearch((query: string, context: ComboboxSearchContext) => Promise<ComboboxOption[]>)Load options from a server. Called with the query (`''` when the list opens with nothing typed) after `searchDebounce` ms. A newer query aborts the previous call through `signal`, and late answers are ignored. Replaces `filter`.
onValueChange((value: string | null) => void)
onValuesChange((values: string[]) => void)
openbooleanWhether the list is open (controlled). Ignored with `inline`.
optionsComboboxOption[]The options. With `onSearch`, the options shown before the first search finishes (usually none).
renderOption((option: ComboboxOption, state: ComboboxOptionState) => ReactNode)Custom option content. Default: media, label, description, hint and a check mark.
searchDebouncenumberMilliseconds to wait after typing before `onSearch`. Default 250.
size"sm" | "md" | "lg"
sortByMatchbooleanOrder matches by quality: labels that start with the query, then a word that does, then the rest. Default false (keep the list order).
valuestring | nullSelected option value (controlled). Single-select only.
valueOptionsComboboxOption[]Options for the current value(s) when they may not be in `options`, for example after a server search. Used only for labels. Options seen in earlier results are remembered too; an unknown value shows as its raw string.
valuesstring[]Selected values (controlled), with `multiple`.

Variants

PropValuesDescription
size'sm''md''lg'Height, matching Input sizes. Default md.
multiple'false''true'One value, or several shown as removable chips.
inline'false''true'List in a popover under the input, or always open under a search box.

States

StateExposed asBehaviour
Openaria-expanded="true"The listbox is shown.
Active optiondata-highlightedThe option the keyboard or pointer is on.
Selectedaria-selected="true"Shows a check indicator.
Disabled optionaria-disabled="true"Cannot be selected.
Loadingaria-busy="true" on the listboxA small Spinner in the input; "Loading…" while there are no options yet.
Errordata-tone="danger" on the statusonSearch rejected: a message with a "Try again" button.
Empty–emptyMessage (default "No matches") in a status region.
Invaliddata-invalidDanger border, from invalid or the surrounding Field.
Disableddata-disabledInput and toggle are disabled; chips lose their remove buttons.

Interaction behaviour

  • Typing opens the list and filters it (label, hint and keywords; sortByMatch ranks prefix matches first).
  • Clicking an option selects it. Single mode closes the list; multiple toggles the value and keeps it open.
  • With onSearch, opening searches the empty query at once; typing searches after searchDebounce ms (default 250).
  • With onCreate, a "Create “text”" option appears when no label matches the text exactly.
  • clearOption adds a first option that clears the selection.
  • limit renders only the first matches and says how many are hidden.
  • Chip remove buttons and Backspace remove values; each change is announced in a polite status region.
  • The chevron button toggles the list and returns focus to the input.

Keyboard support

KeysAction
ArrowDown / ArrowUpOpens the list on the selected option, then moves the active option.
Alt+ArrowDown / Alt+ArrowUpOpens without moving / closes the list.
PageDown / PageUpMoves ten options.
Home / EndJumps to the first or last option while open.
EnterSelects (or toggles) the active option unless disabled; retries a failed search when nothing is listed.
EscapeCloses the list and restores the selected label.
BackspaceOn an empty input: removes the last chip (multiple) or clears the selection (clearable).

Accessibility

Role
combobox, controlling a listbox
Accessible name
A FieldLabel in the surrounding Field, or aria-label. The listbox is named by listLabel, the Field label or aria-label.
  • aria-autocomplete="list", aria-expanded, aria-controls and aria-activedescendant are set on the input.
  • Grouped options sit in role="group" elements labelled by their heading.
  • With multiple the listbox is aria-multiselectable; chips are a list with named remove buttons.
  • Loading, empty, error and "showing N of M" messages are role="status".
  • Option media is decorative (aria-hidden); the label names the option.

Responsive behaviour

  • The popover list matches the input width (at least 12rem) and limits its height to the space available.
  • In multiple mode chips wrap onto new lines and the control grows; the text input keeps at least 5rem.
  • Long labels wrap; hints truncate with an ellipsis at 40% of the row.

Theme support

  • Input, chip and menu colours are semantic tokens shared with Input, TagInput and DropdownMenu, so light, dark and more-contrast themes apply.
  • The menu entrance animation comes from the foundation and respects reduced motion.

Do and don't

Do

  • Use onSearch for server lists rather than wiring inputValue and filter={() => true} by hand.
  • Pass valueOptions when a selected value may not be in the current results, so it keeps its label.
  • Write an emptyMessage that says what to try next.

Don't

  • Do not use Combobox for free text entry without onCreate; single mode reverts unmatched text.
  • Do not render thousands of options: use onSearch or limit.
  • Select: Short lists without search.
  • EntityPicker: Button-triggered record picker built on the inline Combobox.
  • TagInput: Free-form values as chips.
  • Field: Label and error wiring.

Dependencies

KindDepends on
npm packages@radix-ui/react-popoverreact
Monarch componentsField, Spinner

Uses .mn-input from text-field and .mn-menu from the shared menu styles.

Testing coverage

  • Filters, navigates with the keyboard (aria-activedescendant) and selects with Enter; Escape restores the selection.
  • Grouped options in labelled groups; hint and keyword matching; prefix ranking; custom rendering; limit message; clear option.
  • PageDown/PageUp, Home/End, Alt+ArrowDown/Up; Backspace clears.
  • Multiple: toggles, stays open, chips with remove buttons, Backspace removes, announcements, disabled chips.
  • Async: debounce, abort of superseded queries, late answers ignored, error with retry (button and Enter), minSearchLength, valueOptions labels.
  • Create option selects what onCreate returns; hidden on an exact match. Inline mode.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/combobox; Cross-browser axe: /components/combobox

Tests: packages/react/src/components/forms/combobox