Skip to main content

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). onChange fires 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
Value: — · Text: —
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
Value: —
<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​

Used for shipping
  • Japan
  • United States
Choose a country
  • 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.

  • Apple
  • Apricot
  • Banana
  • Blackberry
  • Blueberry
  • Cherry
  • Grape
  • Mango
  • Orange
  • Pear
Submitted: —
<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" with aria-autocomplete="list", aria-expanded, aria-controls, and aria-describedby pointing at the supporting text. It is named by the label (or by aria-label / aria-labelledby). The popup is a role="listbox" of options with aria-selected.
  • Focus stays in the input. The active option is exposed through aria-activedescendant and 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​

PropTypeDefaultDescription
autoFocusboolean—Focus the input on mount.
childrenReactNode—Option elements (direct children or inside Fragments), used when options is not given.
clearOnEscapebooleanfalseEscape 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.
defaultInputValuestring—Uncontrolled initial text of the input. Defaults to the label of defaultValue (or defaultValue itself with freeSolo).
defaultOpenbooleanfalseUncontrolled initial open state.
defaultValuestring''Uncontrolled initial value.
disabledbooleanfalseDisables the field (and leaves the value out of form submission).
errorbooleanfalseError state — error colors, aria-invalid, and errorText. Ignored while disabled.
errorTextReactNode—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.
formstring—Associates the hidden native input with a <form> elsewhere in the page (native form attribute).
freeSolobooleanfalseAccept 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.
fullWidthbooleanfalseStretch the field (and therefore the listbox) to the container's width.
idstring—Applied to the role="combobox" <input>.
inputRefRef<HTMLInputElement>—Ref to the role="combobox" text <input> (the forwarded ref points at the root).
inputValuestring—Controlled text of the input.
labelReactNode—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.
namestring—Form field name of the hidden native input that carries the value.
noOptionsTextReactNode—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.
openboolean—Controlled open state of the listbox.
openOnFocusbooleanfalseOpen 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).
optionsreadonly AutocompleteOption[]—The options, as strings or { value, label, disabled, supportingText } objects. Use Option children instead for icons.
placeholderstring—Placeholder of the input (shown once the label has floated, or always without a label).
requiredbooleanfalseRequires a value for form submission (native constraint validation) and appends * to the label.
shrinkLabelbooleanfalseKeep 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".
startIconReactNode—Icon at the start of the field (decorative).
supportingTextReactNode—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.
toggleLabelstring'Toggle dropdown menu'Accessible name of the trailing toggle button (Compose Strings.ToggleDropdownMenu).
valuestring—Controlled value ('' = nothing selected; with freeSolo, any text).
variant"filled" | "outlined"'filled'Text field container style (the same two as TextField).

Option​

PropTypeDefaultDescription
childrenReactNode—The option's label. Also what the field shows once it is selected.
disabledbooleanfalseVisible, inert, and skipped by keyboard navigation.
startIconReactNode—Icon at the start of the option (decorative, 24dp).
supportingTextReactNode—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.
textValuestring—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).