OPERATOR

ComboBox

Search and select

Selected source: alpha

const [station, setStation] = useState<string | null>(null);
 
<ComboBox
  label="STATION"
  options={[
    { value: "alpha", label: "Alpha station" },
    { value: "bravo", label: "Bravo station" },
  ]}
  value={station}
  onValueChange={setStation}
  placeholder="Search stations…"
/>;

Use unique, stable option values. Clearing the input selects null; unmatched queries do not create options. Labels keep authored case, and a long label wraps. defaultValue enables uncontrolled selection; the forwarded ref targets the input.

The suggestions open at least as wide as the field, wrap at about 30rem, keep 16px from the screen edges, and long options wrap, so an identifier never runs the list off a phone screen.

Sizes and states

Loading options.

Disabled options stay visible but cannot be selected. name submits the selected value.

Caller-supplied results

Matching defaults to a language-sensitive contains search. For caller-filtered results, set filter={false}, control the query with inputValue / onInputChange, and supply options. Query and selection state are independent, but keep the selected option in options (include it in your results): a selected value with no matching option shows an empty field while the form still submits the value. In development the ComboBox reports such a value once, unless you own the query and its results (filter={false} with inputValue), where results change while the operator types.

<ComboBox
  label="REMOTE STATION"
  options={results}
  inputValue={query}
  onInputChange={setQuery}
  value={station}
  onValueChange={setStation}
  filter={false}
  loading={loading}
  emptyMessage="No stations found."
/>

Loading leaves supplied options selectable, and "Loading options." is announced politely when loading starts. The caller owns cancellation and stale results; use disabled to pause interaction (a disabled field keeps its value readable: dim text on a muted fill). Multiselect is not supported.

Motion

The popup arrives from its trigger in 200ms and exits in 150ms, with a small directional movement and scale. The chevron rotates up while open and returns down on close using the same timing. Selection and callbacks update immediately. Typing, keyboard navigation, Escape, assistive activation, and system reduced motion stay instant. Use motion="none" to disable the treatment.

Props

PROPTYPEDEFAULTREQUIRED
motion

Tactile pointer arrival, exit, and open-state chevron rotation; keyboard, assistive activation, and system reduced motion remain immediate.

enum
auto | none
"auto"no
options

Available options. Values must be unique and stable; labels preserve authored case.

array—yes
value

Controlled selected option value, or null to clear selection. Omit for internal selection state.

string—no
defaultValue

Initial selected option value for uncontrolled use.

string—no
onValueChange

(value: string | null) => void. Called on option selection or when clearing the input clears the selected value.

function—no
inputValue

Controlled search input text; independent from the selected value.

string—no
onInputChange

(text: string) => void. Use with inputValue for caller-owned search and asynchronous results.

function—no
label

Visible accessible label. Alternatively, use Field or aria-label.

string—no
aria-label

Accessible name when there is no visible label or surrounding Field.

string—no
aria-describedby

Description IDs; otherwise inherits the surrounding Field help or error message.

string—no
placeholderstring—no
disabledbooleanfalseno
requiredbooleanfalseno
sizeenum
sm | md | lg
"md"no
invalid

Error appearance and aria-invalid; otherwise inherits Field error state.

booleanfalseno
loading

Announces loading and marks input busy. Supplied options remain selectable unless disabled.

booleanfalseno
filter

Language-sensitive contains matching by default. Set false when the caller already filters options or supplies asynchronous results.

booleantrueno
emptyMessagestring"No options found."no
name

Form field name; submits the selected option value.

string—no
id

Input ID; otherwise inherits Field or React Aria's generated ID.

string—no

Accessibility

  • Name the input with label, aria-label, or Field; Field help, errors, invalid and required state are inherited. An aria-label also names the suggestions button and the list ("Show suggestions Origin station"), so two such fields are told apart.
  • Arrow keys navigate enabled options; Enter selects; Escape dismisses.
  • Selection has a checkmark; options have visible focus and at least 24px targets.
  • Empty and loading states have text; loading marks the input busy.
  • The field and its button use the control outline (--oprt-border-control, 3:1 or more).
  • In forced-colors (high contrast) mode, focus draws a 2px outline in the system text color around the field and its button, and the option about to be chosen is drawn in the system highlight color.

Manifest entry

<ComboBox
  label="STATION"
  options={[{ value: "alpha", label: "Alpha station" }, { value: "bravo", label: "Bravo station" }]}
  value={station}
  onValueChange={setStation}
  placeholder="Search stations…"
/>
View agent manifest
{
  "description": "Searchable single-value selection with keyboard navigation and caller-supplied asynchronous results.",
  "props": {
    "motion": {
      "type": "enum",
      "values": [
        "auto",
        "none"
      ],
      "default": "auto",
      "description": "Tactile pointer arrival, exit, and open-state chevron rotation; keyboard, assistive activation, and system reduced motion remain immediate."
    },
    "options": {
      "type": "array",
      "items": {
        "value": "string (required, unique)",
        "label": "string (required)",
        "disabled": "boolean (default false)"
      },
      "required": true,
      "description": "Available options. Values must be unique and stable; labels preserve authored case."
    },
    "value": {
      "type": "string",
      "description": "Controlled selected option value, or null to clear selection. Omit for internal selection state."
    },
    "defaultValue": {
      "type": "string",
      "description": "Initial selected option value for uncontrolled use."
    },
    "onValueChange": {
      "type": "function",
      "description": "(value: string | null) => void. Called on option selection or when clearing the input clears the selected value."
    },
    "inputValue": {
      "type": "string",
      "description": "Controlled search input text; independent from the selected value."
    },
    "onInputChange": {
      "type": "function",
      "description": "(text: string) => void. Use with inputValue for caller-owned search and asynchronous results."
    },
    "label": {
      "type": "string",
      "description": "Visible accessible label. Alternatively, use Field or aria-label."
    },
    "aria-label": {
      "type": "string",
      "description": "Accessible name when there is no visible label or surrounding Field."
    },
    "aria-describedby": {
      "type": "string",
      "description": "Description IDs; otherwise inherits the surrounding Field help or error message."
    },
    "placeholder": {
      "type": "string"
    },
    "disabled": {
      "type": "boolean",
      "default": false
    },
    "required": {
      "type": "boolean",
      "default": false
    },
    "size": {
      "type": "enum",
      "values": [
        "sm",
        "md",
        "lg"
      ],
      "default": "md"
    },
    "invalid": {
      "type": "boolean",
      "default": false,
      "description": "Error appearance and aria-invalid; otherwise inherits Field error state."
    },
    "loading": {
      "type": "boolean",
      "default": false,
      "description": "Announces loading and marks input busy. Supplied options remain selectable unless disabled."
    },
    "filter": {
      "type": "boolean",
      "default": true,
      "description": "Language-sensitive contains matching by default. Set false when the caller already filters options or supplies asynchronous results."
    },
    "emptyMessage": {
      "type": "string",
      "default": "No options found."
    },
    "name": {
      "type": "string",
      "description": "Form field name; submits the selected option value."
    },
    "id": {
      "type": "string",
      "description": "Input ID; otherwise inherits Field or React Aria's generated ID."
    }
  },
  "composable_with": [
    "Field",
    "DataGrid"
  ],
  "never_inside": [
    "Button",
    "Badge"
  ],
  "example": "<ComboBox\n  label=\"STATION\"\n  options={[{ value: \"alpha\", label: \"Alpha station\" }, { value: \"bravo\", label: \"Bravo station\" }]}\n  value={station}\n  onValueChange={setStation}\n  placeholder=\"Search stations…\"\n/>"
}

On this page