Combobox
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.
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.
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.
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.
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.
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.
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
multipleprop, 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
| Prop | Type | Default |
|---|---|---|
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? | ReactNode | No 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
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
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). | ||
id | Key | — |
| 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
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
| Items belonging to this group. | ||
label | ReactNode | — |
Header content for the group (.menu-header). | ||