Skip to content
+

Autocomplete

A text input that suggests matching options as you type.

Autocomplete supports three core interaction modes:

  1. Pick a single value from a predefined list, like a country picker: Combobox.
  2. Pick multiple values shown as chips, like a tag picker: Multiple values.
  3. Accept any text with suggestions, like a search field: Free solo.

Usage guidelines

  • Use for filterable choices: Autocomplete is best for lists that are too long to scan. Use Select instead for short lists.
  • Form controls must have an accessible name: Use a visible TextField label when possible, or add aria-label if the label is hidden.
  • Keep the popup option-only: The listbox should only contain selectable options. Avoid buttons, links, or non-option controls such as "Select all" because they disrupt keyboard semantics and assistive technology behavior.

Combobox

Use the basic combobox when users need to search a predefined list and pick one value.

Press Enter to start editing

Options structure

By default, options can be strings or objects with a label string. You can add fields that model your data, such as a stable ID, timestamp, or grouping field. TypeScript infers the option type from the options prop, so callbacks like onChange are strongly typed.

interface AutocompleteOption {
  label: string;
  id?: string | number;
}
// or
type AutocompleteOption = string;

For example:

const options = [
  { label: 'The Godfather', id: 1 },
  { label: 'Pulp Fiction', id: 2 },
];
// or
const options = ['The Godfather', 'Pulp Fiction'];

When using object options, you must provide isOptionEqualToValue so the component can match the current value to the right option. The default comparison uses strict equality (===), which only works when the value reference is the same as one of the options:

<Autocomplete
  options={options}
  isOptionEqualToValue={(option, value) => option.id === value.id}
/>

To display a value other than label, use getOptionLabel to return a string representing each option:

const options = [
  { id: '1', email: 'alice@example.com' },
  { id: '2', email: 'bob@example.com' },
];

<Autocomplete options={options} getOptionLabel={(option) => option.email} />;

Autocomplete uses the label as the React key for each option by default. If two options share the same label, keep getOptionLabel for the display text and use getOptionKey to provide a stable key for each rendered option:

// Two contacts happen to share the same display name
const options = [
  { label: 'John Smith', id: 'usr_4f12a7b8' },
  { label: 'John Smith', id: 'usr_e9c3d521' },
];

<Autocomplete
  options={options}
  getOptionLabel={(option) => option.label}
  getOptionKey={(option) => option.id}
/>;

Playground

Each example below demonstrates one feature.

Country select

Use renderOption to customize each option. This country picker renders a flag, country code, and calling code.

Controlled states

Autocomplete has two states that can be controlled independently:

  1. value with value/onChange—the option the user has selected, set when they press Enter or click an option.
  2. inputValue with inputValue/onInputChange—the text currently shown in the textbox.

Control them independently—the two states aren't linked.

value: JavaScript
inputValue: ""

Disabled options

Mark specific options as disabled with the getOptionDisabled prop.

Press Enter to start editing

Grouped

Group options with the groupBy prop. Sort the options by the same field you're grouping on—otherwise the same group header repeats.

Press Enter to start editing

Custom group rendering

Customize how groups render with the renderGroup prop. It receives an object with:

  • key—the React key to apply to the rendered group
  • group—the group name string
  • children—the list items in that group

The demo below groups countries by continent and customizes the group rendering.

Press Enter to start editing

Free solo

Use freeSolo when the input should accept values outside the provided options.

Search input

Designed for search inputs with suggestions—for example, Google search or a typeahead field.

Creatable

To let users pick an existing option or create a new one, we recommend setting:

  • selectOnFocus: highlight the input's current text when it receives focus so the user can overwrite it.
  • clearOnBlur: clear leftover input text on blur when no option is picked or created.
  • handleHomeEndKeys: move focus to the first or last option with Home and End.
  • resetHighlightOnMouseLeave: clear mouse-created highlights when the pointer leaves the popup.
  • A trailing option like Add "${inputValue}" to make the create action discoverable.

Or open a dialog when the user wants to add a new value.

Multiple values

Set multiple={true} to let users select more than one value. By default, selected values render as removable Material UI Chips; customize their rendering with renderValue.

  • Spread the props from getItemProps onto each rendered item to preserve the component's built-in behavior.
  • If you replace the default Chip, destructure onDelete first; it's specific to Chip.
Inception
Inception
Inception
Fight Club
Inception

Fixed options

To lock certain tags so they can't be removed, mark their chips as disabled.

Schindler's List
Inception

Selection indicators

Use icons as a visual cue for sighted users to show which options are selected.

Limit tags

Use limitTags to limit how many selected items are visible when the input isn't focused.

Inception
Fight Club
+1
Press Enter to start editing

Asynchronous requests

The component supports two async patterns:

Load on open

Shows a loading state while the request is pending.

Search as you type

If you fetch new options on every keystroke and filter on the server, throttle the requests.

Also disable the built-in client-side filtering—the server has already filtered the options, so re-filtering them would hide valid matches. Pass an identity function to filterOptions:

<Autocomplete filterOptions={(x) => x} />

Google Maps place

A customized UI on top of Google Places Autocomplete. The demo loads the Google Maps JavaScript and Google Places APIs.

It uses autosuggest-highlight, a small (1 kB) utility for highlighting matched text.

Infinite loading

Uses @tanstack/react-query to fetch more options when the user scrolls to the bottom of the list. The list is virtualized with @tanstack/react-virtual.

Press Enter to start editing

Customization

Single value rendering

In the default single-selection mode (when multiple={false}), the selected option appears as plain text inside the input. Use renderValue to customize the display—for example, to add icons, badges, or formatted output.

The renderValue callback receives two parameters:

  • value: the selected option to render inside the input.
  • getItemProps: returns props for the rendered item. Forward the DOM-safe props you need, such as className, data-item-index, and tabIndex; omit onDelete unless you render a Chip.
Visa ending in 4242

Highlights

Uses autosuggest-highlight (1 kB) to highlight the matched portion of each option label.

Custom filter

Customize how options are filtered with createFilterOptions—a factory that returns a function suitable for the filterOptions prop.

import { createFilterOptions } from '@mui/material/Autocomplete';

createFilterOptions(config) => filterOptions

Arguments

  1. config (object [optional]):
  • config.ignoreAccents (bool [optional]): Defaults to true. Removes diacritics.
  • config.ignoreCase (bool [optional]): Defaults to true. Lowercases everything.
  • config.limit (number [optional]): Defaults to null. Limits how many matched options are shown. Useful when many options match and the listbox isn't virtualized.
  • config.matchFrom ('any' | 'start' [optional]): Defaults to 'any'.
  • config.stringify (func [optional]): Controls how an option is converted to a string for matching against the input.
  • config.trim (bool [optional]): Defaults to false. Removes trailing spaces.

Returns

filterOptions: a function ready to pass to the filterOptions prop of Autocomplete (or the option of the same name in useAutocomplete).

In the demo below, options must start with the query string:

const filterOptions = createFilterOptions({
  matchFrom: 'start',
  stringify: (option) => option.title,
});

<Autocomplete filterOptions={filterOptions} />;

Advanced filter

For richer filtering—like fuzzy matching—we recommend match-sorter:

import { matchSorter } from 'match-sorter';

const filterOptions = (options, { inputValue }) => matchSorter(options, inputValue);

<Autocomplete filterOptions={filterOptions} />;

Sizes

Use the size prop to render a smaller input.

Inception
Inception

HTML input attributes

When setting native input attributes on TextField—for example maxLength—merge them with params.slotProps.htmlInput instead of replacing the whole slot object. Autocomplete passes its input ref and event handlers through that object, and dropping them can break focus, keyboard, and selection behavior.

<Autocomplete
  options={options}
  renderInput={(params) => (
    <TextField
      {...params}
      slotProps={{
        ...params.slotProps,
        htmlInput: {
          ...params.slotProps.htmlInput,
          maxLength: 20,
          spellCheck: false,
        },
      }}
    />
  )}
/>

Custom input

Customize the rendered input with the renderInput prop. If you don't use TextField, attach params.slotProps.input.ref to the element that wraps the native input, and spread params.slotProps.htmlInput on the native input.

<Autocomplete
  options={options}
  renderInput={(params) => (
    <div ref={params.slotProps.input.ref}>
      <input type="text" {...params.slotProps.htmlInput} />
    </div>
  )}
/>

Globally customized options

To customize option rendering for every Autocomplete in your app, set renderOption in theme default props.

renderOption receives ownerState as its fourth argument, which exposes props and internal state. Use ownerState.getOptionLabel to render the label.

This keeps option styling consistent across the app while letting each instance customize its content.

Press Enter to start editing

GitHub's picker

A reproduction of GitHub's label picker:

buggood first issue

Hint

Add a hint (ghost text suggestion) inside the input:

Events

Callbacks such as onChange, onClose, onHighlightChange, and onInputChange include a reason argument. Use it to distinguish user input from selection, clear, and other internal updates:

  • Selected value changes use AutocompleteChangeReason, covering selection, creation, removal, clear, and blur transitions.
  • Textbox changes use AutocompleteInputChangeReason, which separates user typing from resets, clears, blur, and option selection or removal.
  • Popup and highlight changes use AutocompleteCloseReason and AutocompleteHighlightChangeReason to describe why the popup closed or how the highlighted option moved.
<Autocomplete
  onInputChange={(_event, value, reason) => {
    if (reason === 'input') {
      setQuery(value);
    }
  }}
/>

To override the default key handling, set defaultMuiPrevented to true on the event:

<Autocomplete
  onKeyDown={(event) => {
    if (event.key === 'Enter') {
      // Prevents the default 'Enter' behavior.
      event.defaultMuiPrevented = true;
      // your handler code
    }
  }}
/>

Virtualization

Searches through a fixed list of 10,000 randomly generated options. The list is virtualized with react-window.

useAutocomplete

Use the useAutocomplete hook when you need full control over markup. Import it from @mui/material/useAutocomplete (4.6 kB gzipped); it accepts the same options as Autocomplete, minus the rendering props. The snippet shows the essential setup for a headless combobox.

import useAutocomplete from '@mui/material/useAutocomplete';

interface Option {
  label: string;
}

interface MyAutocompleteProps {
  id: string;
  label: string;
  options: readonly Option[];
}

function MyAutocomplete({ id, label, options }: MyAutocompleteProps) {
  const {
    getRootProps,
    getInputLabelProps,
    getInputProps,
    getListboxProps,
    getOptionProps,
    groupedOptions,
  } = useAutocomplete({
    id,
    options,
  });

  return (
    <div>
      <div {...getRootProps()}>
        <label {...getInputLabelProps()}>{label}</label>
        <input {...getInputProps()} />
      </div>
      {groupedOptions.length > 0 ? (
        <ul {...getListboxProps()}>
          {groupedOptions.map((option, index) => {
            const { key, ...optionProps } = getOptionProps({ option, index });
            return (
              <li key={key} {...optionProps}>
                {option.label}
              </li>
            );
          })}
        </ul>
      ) : null}
    </div>
  );
}

export default function App() {
  return <MyAutocomplete id="movie" label="Movie" options={movies} />;
}

interface Film extends Option {
  year: number;
}

const movies: readonly Film[] = [
  { label: 'The Shawshank Redemption', year: 1994 },
  { label: 'The Godfather', year: 1972 },
];

This demo shows a fully customized multi-selection combobox built with useAutocomplete.

JavaScript

Limitations

autocomplete/autofill

Browsers use heuristics to try to autofill form inputs—but this can interfere with the Autocomplete experience.

By default, the component sets autoComplete="off" to disable the browser's autocomplete history (remembering past entries for a field). Google Chrome ignores this setting (Issue 41239842). A workaround: remove the id so the component generates a random one.

Browsers may also propose autofill suggestions (saved logins, addresses, payment details). To avoid these:

  • Use a generic input id that doesn't hint at the field's purpose—id="field1" rather than id="country". Leave it empty to get a random id.

  • Set autoComplete="new-password" (some browsers will offer a strong password for these inputs):

    <TextField
      {...params}
      slotProps={{
        ...params.slotProps,
        htmlInput: {
          ...params.slotProps.htmlInput,
          autoComplete: 'new-password',
        },
      }}
    />
    

Read the guide on MDN for more details.

iOS VoiceOver

VoiceOver on iOS Safari has poor support for aria-owns. Work around it with the disablePortal prop.

Custom listbox component

When you provide a custom listbox slot, set role="listbox" on the scroll container. This is what keyboard navigation looks for to scroll the highlighted item into view.

API

See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.