Skip to main contentSkip to docs navigation

Chassis Combobox combines a text input with a filterable listbox, letting users search and select from a list of options.

Overview

Combobox combines a text input with a filterable list of options — type to narrow the list, arrow keys to move through it, Enter to select. It replaces hand-wiring a Menu to a text input with real combobox semantics: role="combobox" on the input, role="listbox"/role="option" on the list, and aria-expanded/aria-controls/aria-activedescendant kept in sync automatically. For a display-only toggle that opens the option list instead of typing directly into it, see Autocomplete.

Compose a ComboboxItem per option, each identified by an id.

JSX
import { Combobox, ComboboxItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Combobox aria-label="Fruit" placeholder="Select a fruit…">
      <ComboboxItem id="apple">Apple</ComboboxItem>
      <ComboboxItem id="banana">Banana</ComboboxItem>
      <ComboboxItem id="cherry">Cherry</ComboboxItem>
      <ComboboxItem id="grape">Grape</ComboboxItem>
      <ComboboxItem id="mango">Mango</ComboboxItem>
      <ComboboxItem id="orange">Orange</ComboboxItem>
      <ComboboxItem id="peach">Peach</ComboboxItem>
      <ComboboxItem id="strawberry">Strawberry</ComboboxItem>
    </Combobox>
  )
}

Rich item content

Add icon/description to a ComboboxItem for a leading icon and a secondary line of text below the label. Content still filters and renders as plain text when neither is set.

JSX
import { Combobox, Icon, ComboboxItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Combobox aria-label="Role" placeholder="Choose a role…">
      <ComboboxItem
        id="admin"
        icon={<Icon name="shield-outline" size={16} />}
        description="Full access to every setting"
      >
        Admin
      </ComboboxItem>
      <ComboboxItem
        id="editor"
        icon={<Icon name="gear-outline" size={16} />}
        description="Can edit content, not settings"
      >
        Editor
      </ComboboxItem>
      <ComboboxItem
        id="viewer"
        icon={<Icon name="eye-outline" size={16} />}
        description="Read-only access"
      >
        Viewer
      </ComboboxItem>
    </Combobox>
  )
}

Grouped items

Wrap related ComboboxItem elements in a ComboboxGroup to render them under a shared header. Filtering a query out of every item in a group hides that group's header along with it.

JSX
import { Combobox, ComboboxGroup, ComboboxItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Combobox aria-label="Language" placeholder="Choose a language…">
      <ComboboxGroup label="Frontend">
        <ComboboxItem id="html">HTML</ComboboxItem>
        <ComboboxItem id="css">CSS</ComboboxItem>
        <ComboboxItem id="js">JavaScript</ComboboxItem>
      </ComboboxGroup>
      <ComboboxGroup label="Backend">
        <ComboboxItem id="python">Python</ComboboxItem>
        <ComboboxItem id="ruby">Ruby</ComboboxItem>
      </ComboboxGroup>
      <ComboboxItem id="sql">SQL</ComboboxItem>
    </Combobox>
  )
}

Disabled options

Add disabled to a ComboboxItem to remove it from keyboard navigation and prevent it from being selected. The combobox itself stays usable.

JSX
import { Combobox, ComboboxItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Combobox aria-label="Role" placeholder="Choose a role…">
      <ComboboxItem id="admin">Admin</ComboboxItem>
      <ComboboxItem id="editor" disabled>
        Editor (unavailable)
      </ComboboxItem>
      <ComboboxItem id="viewer">Viewer</ComboboxItem>
    </Combobox>
  )
}

Data-driven items

Pass an items array instead of composing children to build the option list from data. A 'header' entry starts a group that every following item joins until the next header or the end of the array; items wins over children when both are set.

JSX
import { Combobox } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Combobox
      aria-label="Timezone"
      placeholder="Choose a timezone…"
      items={[
        { type: 'header', id: 'americas', label: 'Americas' },
        { id: 'est', label: 'Eastern Time' },
        { id: 'cst', label: 'Central Time' },
        { type: 'header', id: 'europe', label: 'Europe' },
        { id: 'gmt', label: 'Greenwich Mean Time' },
        { id: 'cet', label: 'Central European Time' }
      ]}
    />
  )
}

As a form field

Pass label/help to wrap the combobox in a .form-field with an associated FormLabel and FormHelp. Pass name to have Combobox create and manage a hidden <input> for native form submission — its value stays in sync with the selection.

With required, a combobox with no option selected holds its form back from submitting, and its input takes focus. Text typed without selecting an option is not a value: the form submits the selected option's id. The field then shows as invalid, with its invalidFeedback, until it has a value or the form is reset; set invalid yourself, to false too, to decide how it looks instead.

The billing region.
JSX
import { Combobox, ComboboxItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Combobox
      label="Country"
      help="The billing region."
      name="country"
      placeholder="Pick a country…"
    >
      <ComboboxItem id="us">United States</ComboboxItem>
      <ComboboxItem id="uk">United Kingdom</ComboboxItem>
      <ComboboxItem id="ca">Canada</ComboboxItem>
      <ComboboxItem id="au">Australia</ComboboxItem>
      <ComboboxItem id="de">Germany</ComboboxItem>
    </Combobox>
  )
}

Controlled selection

Pass value/onChange to Combobox to control the selected option yourself. Omit them (use defaultValue instead) to let Combobox manage selection internally.

Scope

This is the single-select, type-to-filter combobox — the strong-fit case for React Aria's combobox hooks, and the most common combobox pattern. Two related patterns live in other components instead:

  • A button-trigger toggle with its own search field — see Autocomplete.
  • Multiple selection — also Autocomplete, via its multiple prop, built on the same button-trigger composition.

No async filtering. Matching happens entirely client-side against the options already passed as children/items; there's no controlled input value or onInputChange callback to drive a server-side search.

API

Combobox

PropTypeDefault
aria-label?string—
An accessible label for the combobox, used when there's no visible <label>.
aria-labelledby?string—
Identifies a visible <label> element for the combobox.
children?ReactNode—
ComboboxItem elements, optionally wrapped in ComboboxGroup — read as data by Combobox to build the option list. Not rendered directly. Ignored when items is set.
className?string—
A string of all className you want applied to the base component.
defaultValue?Key | null—
The initial selected option's id (uncontrolled).
disabled?boolean—
Prevents the combobox from being focused or interacted with.
help?ReactNode—
A description for the field, rendered below the combobox.
id?string—
id forwarded to the input element — useful for pairing with a <label for>.
invalid?boolean—
Set component validation state to invalid. Left unset, the component shows as invalid by itself from a submit it held back (an empty required one, say) until it no longer would, or its form is reset. Set it, to false too, and it alone decides.
invalidFeedback?ReactNode—
An error message for the field, rendered below the combobox when invalid is set, and after a submit the field held back, until it no longer would.
items?MenuItemsDef—
Array of item/header/divider definitions for data-driven rendering. When provided, children are ignored. A 'header' entry starts a group that all following items join until the next header or the end of the array. 'divider' entries are a no-op here (dividers aren't meaningful for a listbox/option collection) — use ComboboxGroup composition instead if you need finer control over grouping. An entry's href/onClick are Menu-only and are not read here — use onChange to react to the selection instead.
label?ReactNode—
The field's caption, rendered as a FormLabel associated with the input.
name?string—
name of an auto-created hidden input, kept in sync with the selection, for native form submission. Omit to skip creating one.
noResultsText?ReactNodeNo results found
Text shown in the listbox when no options match the current query.
onChange?((value: Key | null) => void)—
Callback fired when the selected option changes.
placeholder?string—
Placeholder shown in the input when nothing is selected.
required?boolean—
Toggle the required state for the component. A required combobox with no option selected holds its form back from submitting, and takes focus: text typed without selecting an option is not a value.
size?"sm" | "lg"—
Size the component sm or lg.
valid?boolean—
Set component validation state to valid.
validFeedback?ReactNode—
A success message for the field, rendered below the combobox when valid is set.
value?Key | null—
The selected option's id (controlled).

ComboboxItem

PropTypeDefault
childrenReactNode—
Content of the option. Used as the filter/typeahead text when textValue isn't set — pass textValue explicitly whenever children isn't a plain string (e.g. it wraps an icon or other rich markup), since it can't otherwise be derived from rich content.
description?ReactNode—
Secondary line of text rendered below children (.menu-item-description).
disabled?boolean—
Prevents the option from being selected, focused, or otherwise interacted with.
icon?ReactNode—
Icon rendered at the option's leading edge (.menu-item-icon).
idKey—
Identifies this option. Submitted as the value when this option is selected.
textValue?string—
Text used for filtering and typeahead. Required when children isn't a plain string — falls back to children itself when omitted and children is a string.

ComboboxGroup

PropTypeDefault
childrenReactNode—
Items belonging to this group.
labelReactNode—
Header content for the group (.menu-header).