Autocomplete
A button-trigger combobox — opens a searchable option list from a toggle, with single- or multi-select and grouped items.
Overview
Autocomplete is chassis-css's button-trigger combobox — a display-only toggle that opens a panel containing its own search field, rather than a text input that's itself the trigger. That's the difference from Combobox: there, the text input is the combobox; here, the toggle just opens one. Compose an AutocompleteItem per option, each identified by an id, optionally grouped with AutocompleteGroup. Pass placeholder for the toggle's empty-state text and searchPlaceholder for the search field inside the panel.
import { Autocomplete, AutocompleteItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete aria-label="Fruit" placeholder="Select a fruit…">
<AutocompleteItem id="apple">Apple</AutocompleteItem>
<AutocompleteItem id="banana">Banana</AutocompleteItem>
<AutocompleteItem id="cherry">Cherry</AutocompleteItem>
<AutocompleteItem id="grape">Grape</AutocompleteItem>
<AutocompleteItem id="mango">Mango</AutocompleteItem>
</Autocomplete>
)
}Multiple selection
Set multiple to allow more than one option to be selected. The toggle shows the single selection's label, or an "N selected" count once more than one is picked, and the panel stays open after each selection so more can be picked. Backspace in the empty search field removes the last selected option.
import { Autocomplete, AutocompleteItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete aria-label="Fruit" placeholder="Select fruits…" multiple>
<AutocompleteItem id="apple">Apple</AutocompleteItem>
<AutocompleteItem id="banana">Banana</AutocompleteItem>
<AutocompleteItem id="cherry">Cherry</AutocompleteItem>
<AutocompleteItem id="grape">Grape</AutocompleteItem>
<AutocompleteItem id="mango">Mango</AutocompleteItem>
</Autocomplete>
)
}Rich item content
Add icon/description to an AutocompleteItem for a leading icon and a secondary line of text below the label.
import { Autocomplete, Icon, AutocompleteItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete aria-label="Role" placeholder="Choose a role…">
<AutocompleteItem
id="admin"
icon={<Icon name="shield-outline" size={16} />}
description="Full access to every setting"
>
Admin
</AutocompleteItem>
<AutocompleteItem
id="editor"
icon={<Icon name="gear-outline" size={16} />}
description="Can edit content, not settings"
>
Editor
</AutocompleteItem>
<AutocompleteItem
id="viewer"
icon={<Icon name="eye-outline" size={16} />}
description="Read-only access"
>
Viewer
</AutocompleteItem>
</Autocomplete>
)
}Grouped items
Wrap related AutocompleteItem elements in an AutocompleteGroup to render them under a shared header.
import { Autocomplete, AutocompleteGroup, AutocompleteItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete aria-label="Language" placeholder="Choose a language…">
<AutocompleteGroup label="Frontend">
<AutocompleteItem id="html">HTML</AutocompleteItem>
<AutocompleteItem id="css">CSS</AutocompleteItem>
<AutocompleteItem id="js">JavaScript</AutocompleteItem>
</AutocompleteGroup>
<AutocompleteGroup label="Backend">
<AutocompleteItem id="python">Python</AutocompleteItem>
<AutocompleteItem id="ruby">Ruby</AutocompleteItem>
</AutocompleteGroup>
<AutocompleteItem id="sql">SQL</AutocompleteItem>
</Autocomplete>
)
}Disabled options
Add disabled to an AutocompleteItem to remove it from keyboard navigation and prevent it from being selected.
import { Autocomplete, AutocompleteItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete aria-label="Role" placeholder="Choose a role…">
<AutocompleteItem id="admin">Admin</AutocompleteItem>
<AutocompleteItem id="editor" disabled>
Editor (unavailable)
</AutocompleteItem>
<AutocompleteItem id="viewer">Viewer</AutocompleteItem>
</Autocomplete>
)
}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.
import { Autocomplete } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete
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 autocomplete in a .form-field with an associated FormLabel and FormHelp. Pass name to have Autocomplete create and manage hidden <input>s for native form submission — one in single-select mode, one per selected key in multiple mode.
With required, an autocomplete with nothing selected holds its form back from submitting: the toggle takes focus and shows as invalid, with its invalidFeedback, until an option is selected or the form is reset. invalid, once set, and valid decide how it looks instead.
import { Autocomplete, AutocompleteItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Autocomplete
label="Country"
help="The billing region."
name="country"
placeholder="Pick a country…"
>
<AutocompleteItem id="us">United States</AutocompleteItem>
<AutocompleteItem id="uk">United Kingdom</AutocompleteItem>
<AutocompleteItem id="ca">Canada</AutocompleteItem>
<AutocompleteItem id="au">Australia</AutocompleteItem>
<AutocompleteItem id="de">Germany</AutocompleteItem>
</Autocomplete>
)
}Controlled selection
Pass value/onChange to control the selection directly — an array when multiple is set, a single id otherwise. Omit them (use defaultValue instead) to let Autocomplete manage selection internally.
Accessibility
Opening the panel moves focus to the search field; closing it — via Escape, a selection, or an outside click — returns focus to the toggle. The toggle renders as a <div role="button"> rather than a native <button>: an earlier version rendered removable chips for each selection directly inside it, which failed an automated accessibility check for nesting interactive controls inside a button, so selections now render as plain text instead and the non-native trigger stays in place.
Scope
- No typeahead while closed. Unlike a native
<select>, there's no way to jump to an option by typing a letter before opening the panel — the search field only exists once the toggle has been activated. - No async filtering. Matching happens entirely client-side against the options already passed as
children/items; there's no controlled input value oronInputChangecallback to drive a server-side search.
API
Autocomplete
| Prop | Type | Default |
|---|---|---|
aria-label? | string | — |
An accessible label for the autocomplete, used when there's no visible <label>. | ||
aria-labelledby? | string | — |
Identifies a visible <label> element for the autocomplete. | ||
children? | ReactNode | — |
AutocompleteItem elements, optionally wrapped in AutocompleteGroup — read as data by
Autocomplete 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 | Key[] | null | — |
The initial selected option's id(s) (uncontrolled). An array when multiple is set. | ||
disabled? | boolean | — |
| Prevents the autocomplete from being focused or interacted with. | ||
help? | ReactNode | — |
| A description for the field, rendered below the autocomplete. | ||
id? | string | — |
id forwarded to the search input inside the popover. The toggle — the field's real,
always-focusable surface — has no single labelable element <label for> can target (it's a
role="button" <div>, and the search input itself is hidden until open), so label is
associated via aria-labelledby on the toggle instead. See FORMS.md's "role=group" pattern. | ||
invalid? | boolean | — |
| Set component validation state to invalid. | ||
invalidFeedback? | ReactNode | — |
An error message for the field, rendered below the autocomplete when invalid is set, and
after a submit that a required autocomplete with nothing selected held back. | ||
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 — use
AutocompleteGroup 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 toggle. | ||
multiple? | boolean | false |
Allows more than one option to be selected. The toggle shows the single selection's label,
or an "N selected" count once more than one is picked. The menu stays open after each
selection, and Backspace in the empty search field removes the last selected option. | ||
name? | string | — |
name of auto-created hidden input(s), kept in sync with the selection, for native form
submission. Single-select renders one; multiple renders one per selected key. Omit to skip
hidden-input creation. | ||
noResultsText? | ReactNode | No results found |
| Text shown in the listbox when no options match the current query. | ||
onChange? | ((value: Key | Key[] | null) => void) | — |
Callback fired when the selection changes. Receives an array of keys when multiple is set. | ||
placeholder? | string | — |
| Text shown on the toggle when nothing is selected. | ||
required? | boolean | — |
Toggle the required state for the component. A required autocomplete with nothing selected
holds its form back from submitting, takes focus on its toggle and shows as invalid, with
invalidFeedback, until an option is selected or the form is reset. invalid, once set, and
valid decide how it looks instead. | ||
searchPlaceholder? | string | — |
| Placeholder for the search field inside the dropdown. | ||
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 autocomplete when valid is set. | ||
value? | Key | Key[] | null | — |
The selected option's id(s) (controlled). An array when multiple is set. | ||
AutocompleteItem
| 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. | ||
AutocompleteGroup
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
| Items belonging to this group. | ||
label | ReactNode | — |
Header content for the group (.menu-header). | ||