Overview
Autocomplete speeds up typing that has a known shape but not a closed list: addresses, cities, job titles, internal page names. The person can always type something no suggestion anticipated ā the list advises, it does not decide.
That is the boundary with Combobox:
- The value must be one of your options ā Combobox. It rejects anything else by design.
- Free text, helped along ā Autocomplete. This component.
- Many free-form values in one field ā Tags Input.
If you find yourself validating that autocomplete input "matches one of the options", you wanted a combobox; if you find yourself fighting a combobox to accept custom values, you wanted this.
Anatomy
The field is an input plus a popup of suggestions. Parts mirror the combobox family: AutocompleteInput, AutocompletePopup, AutocompleteList, AutocompleteItem, AutocompleteEmpty, AutocompleteGroup(Label), AutocompleteStatus for loading and result counts, AutocompleteClear, and AutocompleteTrigger when it should open on click like a select.
Behavior
Keyboard.
The critical difference from select-family controls: dismissing never destroys the typed value. Suggestions accelerate input; they are never a gate on it.
Async suggestions. Fetch on change, keep the previous results visible while new ones load, and report state through AutocompleteStatus. Debounce on your side; the component re-renders cheaply but your API is not free.
Grouping. Group suggestions when they mix kinds ā recent searches next to full results, or cities grouped by country. Group labels are announced by screen readers.
States
While loading, say so via AutocompleteStatus; while empty, AutocompleteEmpty should distinguish "nothing matches" from "still fetching" whenever you can. Invalid state follows aria-invalid with the danger border and pairs with an error message via Field. Disabled skips focus and submission entirely.
Long suggestions wrap inside the popup; long typed values scroll horizontally in the input as any text input does. Tokens handle dark mode and RTL mirroring automatically.
Accessibility
The visible label names the input. The popup is a real listbox: screen-reader users hear how many suggestions matched, which is highlighted, and can ignore the popup entirely without losing their typed text. Because the field's whole model is "typed text is the truth", nothing in the suggestion UI overwrites input silently ā acceptance happens only through explicit selection.
Suggestion copy matters more than usual here: people scan fragments mid-typing. Put the distinguishing part of each label first, and include enough context ("Springfield, Illinois") that similar entries are tellable apart.
Installation
Usage
import {
Autocomplete,
AutocompleteEmpty,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
AutocompletePopup,
} from "@/components/ui/autocomplete";<Autocomplete items={cities}>
<AutocompletePopup>
<AutocompleteInput placeholder="Your city" />
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.value} value={item}>
{item.label}
</AutocompleteItem>
)}
</AutocompleteList>
<AutocompleteEmpty>No matches ā you can still use your city.</AutocompleteEmpty>
</AutocompletePopup>
</Autocomplete>Don't do this
Gatekeeping free text
// Bad
// rejecting submit unless the typed value equals some suggestion
if (!options.includes(input)) setError("Choose one of the listed options");// Good
<Combobox items={options} /> // closed list, honestly enforcedAn autocomplete that refuses non-suggested values is a combobox with worse UX ā the person fought the field to enter something legitimate (a new store location) and lost. If the set is truly closed, use the control whose contract says so; if it is open, let typed values through and validate on meaning.
Blank popups during fetches
// Bad
{results.length > 0 && <AutocompleteList>ā¦</AutocompleteList>}
// the whole list vanishes mid-load// Good
<AutocompleteStatus>
{loading ? "Searchingā¦" : `${results.length} matches`}
</AutocompleteStatus>When suggestions disappear without explanation, people stop typing to wait or assume their query found nothing and rephrase it. Status text at the top of the popup keeps the feedback loop honest during fetches.
Examples
Async suggestions
Grouped results
With clear button
In a form
API reference
Autocomplete accepts Base UI Autocomplete props:
Parts: AutocompleteInput, AutocompleteTrigger, AutocompletePopup, AutocompleteList, AutocompleteItem, AutocompleteGroup(Label), AutocompleteEmpty, AutocompleteStatus, AutocompleteClear, AutocompleteValue.
See the Base UI Autocomplete API.