Autocomplete
The MD3 editable exposed dropdown menu: a text field whose menu of options is filtered as you type. The field looks exactly like a TextField (filled or outlined), and the list is the one Select uses. As the list is filtered, the options that stay ease into their new position.
Variants
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
const fruits = ['Apple', 'Banana', { value: 'Cherry', label: 'Cherry', disabled: true }]
<Autocomplete variant="outlined" label="Fruit" options={fruits} />
options takes strings (the value is the label) or { value, label, disabled, supportingText } objects. Typing opens the list with only the options whose label contains the
text, ignoring case and accents. When nothing matches, the list closes. Pass
noOptionsText to show a message instead.
Value and input text
An Autocomplete has two pieces of state, each controlled or uncontrolled:
value/defaultValue/onChange(event, value): the picked option's value (''= nothing picked).onChangefires when an option is picked or the text is cleared.inputValue/defaultInputValue/onInputChange(event, inputValue): the text in the field. It fires on every keystroke and when picking an option puts its label in the field.
If the text no longer matches an option when you leave the field, it goes back to the picked option's label.
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
const [value, setValue] = useState('')
const [text, setText] = useState('')
<Autocomplete
label="Fruit"
options={fruits}
value={value}
onChange={(event, next) => setValue(next)}
inputValue={text}
onInputChange={(event, next) => setText(next)}
noOptionsText="No fruit matches"
/>
The open state can be controlled too, with open / defaultOpen /
onOpenChange.
Free text
With freeSolo, any text is a value: Enter (with no option
active) or leaving the field commits it. Options are still suggested, but an
option is selected only when you pick it (click it, or Enter on the active
one): typed text stays the typed string even when it equals an option's
label, as in MUI. Compose also selects only on an item click, and m3 doesn't
say. In 1.5 and earlier, an exact label match committed that option's value instead.
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
<Autocomplete freeSolo label="Favorite fruit" options={fruits} onChange={(event, value) => save(value)} />
Clear on Escape
Escape with the list open only closes the list. With the list
closed it does nothing by default, so the key reaches enclosing handlers — an
enclosing Dialog closes. With clearOnEscape, that
Escape clears the text and the value instead (and the Dialog stays open), as
the WAI-ARIA combobox pattern allows.
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
<Autocomplete clearOnEscape label="Fruit" options={fruits} defaultValue="Apple" />
Custom filtering
filterOptions(options, { inputValue }) replaces the default filter. It gets
every option as { value, label, disabled } and returns the ones to show, in
order. This one matches from the start of the label:
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
<Autocomplete
label="Starts with"
options={fruits}
filterOptions={(options, { inputValue }) =>
options.filter((option) => option.label.toLowerCase().startsWith(inputValue.toLowerCase()))
}
/>
Option children and icons
Instead of options, pass Option children (the same component Select
uses), for example to give options a startIcon. The text that is matched and
put in the field is the option's text content. Set textValue when the
children are not plain text.
- Flight
- Car
- Train
- Bike
<Autocomplete label="Travel mode" startIcon={<CommuteIcon />} defaultValue="train">
<Option value="flight" startIcon={<FlightIcon />}>Flight</Option>
<Option value="train" startIcon={<TrainIcon />}>Train</Option>
<Option value="bike" textValue="Bike">
<strong>Bike</strong> (fastest in town)
</Option>
</Autocomplete>
Option supporting text
A supportingText on an options object (or on an Option child) adds a
second line below the label (m3 menu-item supporting text: body-small,
on-surface-variant). Both lines wrap, and the list keeps the width of the
field. Only the label is matched by the filter and put in the input. The
option's accessible name is its label, and the supporting text is its
description (aria-describedby).
- HanedaTokyo, Japan
- Narita InternationalTokyo, Japan
- Kansai InternationalOsaka, Japan
<Autocomplete
label="Airport"
options={[
{ value: 'hnd', label: 'Haneda', supportingText: 'Tokyo, Japan' },
{ value: 'kix', label: 'Kansai International', supportingText: 'Osaka, Japan' },
]}
/>
Label placement
labelPlacement and shrinkLabel work as they do on
TextField: labelPlacement="above" puts the label
above the container, always minimized, and shrinkLabel keeps the floating
label minimized and shows the placeholder while the input is empty. The list
still opens right below the container. m3 doesn't describe an above label for
dropdowns. This is the Compose TextFieldLabelPosition mapping from
TextField, applied to the field that anchors the menu.
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
<Autocomplete label="Fruit" labelPlacement="above" placeholder="Type a fruit" options={fruits} />
<Autocomplete label="Fruit" variant="outlined" shrinkLabel placeholder="Type a fruit" options={fruits} />
Open on focus
By default the list opens when you type, click the field, or press
ArrowDown. Neither m3 nor Compose opens it just because the input
got focus. openOnFocus (the MUI name) opts in: the list opens with every
option and no active option when focus enters the input.
- Apple
- Apricot
- Banana
- Blackberry
- Blueberry
- Cherry
- Grape
- Mango
- Orange
- Pear
<Autocomplete openOnFocus label="Fruit" options={fruits} />
Supporting text, error & states
- Japan
- United States
- Japan
- United States
- Japan
- United States
error switches to the error colors and sets aria-invalid. errorText
replaces supportingText while in error and is announced (role="alert").
label, supportingText and errorText also accept React content (e.g. a
short link in the supporting text), with the same guidance as
TextField: a short, single-line
label without links. fullWidth stretches the field, and with it the list, to the container's
width.
Forms
As in Select, the value is carried by a visually hidden native <input>.
With name it is submitted with its form. required takes part in native
validation, and a form reset puts an uncontrolled Autocomplete back to its
default value and text. inputRef points at the text input you type in.
<form onSubmit={handleSubmit}>
<Autocomplete label="Fruit" name="fruit" required options={fruits} defaultValue="Mango" />
<Button type="submit">Submit</Button>
<Button type="reset" variant="outlined">Reset</Button>
</form>
Motion
m3: "Menu items ease into their new position as the menu is filtered." The
options that stay move to their new place on the default spatial spring, and
options that appear fade in. With prefers-reduced-motion: reduce the list
updates without motion.
Accessibility
Follows the WAI-ARIA APG editable combobox with list autocomplete pattern:
- The text input is
role="combobox"witharia-autocomplete="list",aria-expanded,aria-controls, andaria-describedbypointing at the supporting text. It is named by thelabel(or byaria-label/aria-labelledby). The popup is arole="listbox"ofoptions witharia-selected. - Focus stays in the input. The active option is exposed through
aria-activedescendantand shown with a focus ring. - Typing opens the list without activating an option. ↓ / ↑ open it and move through the options, wrapping at the ends. Alt+↓ opens it without moving.
- Enter picks the active option. Escape closes the
list (an enclosing Dialog stays open). When the list is already closed,
Escape is left alone — an enclosing Dialog closes — unless you set
clearOnEscape: then it clears the field. - The arrow at the end is a button that is not in the Tab order. It toggles
the list and is named by
toggleLabel(default "Toggle dropdown menu"). - Disabled options are announced (
aria-disabled) but skipped by the keyboard.
Props
Autocomplete
| Prop | Type | Default | Description |
|---|---|---|---|
autoFocus | boolean | — | Focus the input on mount. |
children | ReactNode | — | Option elements (direct children or inside Fragments), used when options is not given. |
clearOnEscape | boolean | false | Escape with the listbox closed clears the input and the value (and is consumed). Without it, that Escape does nothing and reaches enclosing handlers — an enclosing Dialog closes. Escape with the listbox open only closes the listbox either way. |
defaultInputValue | string | — | Uncontrolled initial text of the input. Defaults to the label of
defaultValue (or defaultValue itself with freeSolo). |
defaultOpen | boolean | false | Uncontrolled initial open state. |
defaultValue | string | '' | Uncontrolled initial value. |
disabled | boolean | false | Disables the field (and leaves the value out of form submission). |
error | boolean | false | Error state — error colors, aria-invalid, and errorText. Ignored while disabled. |
errorText | ReactNode | — | Shown instead of supportingText (with role="alert") when error is set.
Accepts rich content, like supportingText. |
filterOptions | ((options: AutocompleteOptionItem[], state: AutocompleteFilterState) => AutocompleteOptionItem[]) | — | Custom filtering. Receives every option and { inputValue }; returns the
options to show, in order. The default keeps the options whose label
contains the text, ignoring case and accents. |
form | string | — | Associates the hidden native input with a <form> elsewhere in the page (native form attribute). |
freeSolo | boolean | false | Accept any text, not only an option: Enter (with no option active) or leaving the field commits the typed text as the value — as typed, even when it equals an option's label (an option is selected only by picking it, B52). Without it, the text returns to the selected option's label on blur. |
fullWidth | boolean | false | Stretch the field (and therefore the listbox) to the container's width. |
id | string | — | Applied to the role="combobox" <input>. |
inputRef | Ref<HTMLInputElement> | — | Ref to the role="combobox" text <input> (the forwarded ref points at the root). |
inputValue | string | — | Controlled text of the input. |
label | ReactNode | — | Floating label; also names the input and the listbox. Rendered with a trailing * when required.
Accepts rich content (text, emphasis, a small icon) — keep it short and
on one line, as m3 asks: the label is a single line (truncated with an
ellipsis), clicks on it go to the field, and the outlined notch draws a
hidden copy of it to measure its width. So no links or buttons in the
label, and no elements with an id. |
labelPlacement | "floating" | "above" | 'floating' | Where the label sits, as TextField labelPlacement (B37 / B52):
'floating' inside the container (the outlined variant notches the
border), 'above' minimized above the container. The listbox still
opens right below the container. |
name | string | — | Form field name of the hidden native input that carries the value. |
noOptionsText | ReactNode | — | Shown in the listbox when no option matches. Without it the listbox
closes instead (Compose EditableExposedDropdownMenuSample). |
onChange | ((event: SyntheticEvent<Element, Event>, value: string) => void) | — | Fires when the value changes: an option is picked, the input is cleared
(or Escape clears it, with clearOnEscape), or — with freeSolo — typed text is committed with
Enter or on blur. Not fired on form reset. |
onInputChange | ((event: SyntheticEvent<Element, Event>, inputValue: string) => void) | — | Fires on every change of the input text: typing, picking an option, clearing, and the reset on blur. |
onOpenChange | ((open: boolean) => void) | — | Notified when the listbox should open or close. |
open | boolean | — | Controlled open state of the listbox. |
openOnFocus | boolean | false | Open the listbox when the input receives focus (with no active option). Neither m3 nor Compose opens on focus, so this is opt-in, as in MUI (B52). |
options | readonly AutocompleteOption[] | — | The options, as strings or { value, label, disabled, supportingText } objects. Use Option children instead for icons. |
placeholder | string | — | Placeholder of the input (shown once the label has floated, or always without a label). |
required | boolean | false | Requires a value for form submission (native constraint validation) and appends * to the label. |
shrinkLabel | boolean | false | Keep the floating label minimized even while the input is empty, and
show the placeholder (TextField shrinkLabel). No effect without a
label or with labelPlacement="above". |
startIcon | ReactNode | — | Icon at the start of the field (decorative). |
supportingText | ReactNode | — | Helper line below the field. Replaced by errorText while in error.
Accepts rich content — a short link is fine (it stays reachable and is
part of the field's description via aria-describedby); keep it to
about one line, as m3 asks. |
toggleLabel | string | 'Toggle dropdown menu' | Accessible name of the trailing toggle button (Compose
Strings.ToggleDropdownMenu). |
value | string | — | Controlled value ('' = nothing selected; with freeSolo, any text). |
variant | "filled" | "outlined" | 'filled' | Text field container style (the same two as TextField). |
Option
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The option's label. Also what the field shows once it is selected. |
disabled | boolean | false | Visible, inert, and skipped by keyboard navigation. |
startIcon | ReactNode | — | Icon at the start of the option (decorative, 24dp). |
supportingText | ReactNode | — | A second line below the label (m3 menu-item supporting text: body-small,
on-surface-variant). Both lines wrap. Exposed as the option's
description (aria-describedby) — the accessible name stays the label,
and only the label is shown in the field, matched by typeahead, and
matched by Autocomplete filtering. |
textValue | string | — | Plain-text label for an Autocomplete: the text the input shows once the
option is picked and the text filtering matches. Defaults to the text
content of children. Ignored by Select. |
value * | string | — | The value this option selects (submitted with the form). |