Autocomplete
A text input that suggests matching options as you type.
Autocomplete supports three core interaction modes:
- Pick a single value from a predefined list, like a country picker: Combobox.
- Pick multiple values shown as chips, like a tag picker: Multiple values.
- 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
TextFieldlabelwhen possible, or addaria-labelif 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.
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:
valuewithvalue/onChange—the option the user has selected, set when they press Enter or click an option.inputValuewithinputValue/onInputChange—the text currently shown in the textbox.
Control them independently—the two states aren't linked.
JavaScript""Disabled options
Mark specific options as disabled with the getOptionDisabled prop.
Grouped
Group options with the groupBy prop. Sort the options by the same field you're grouping on—otherwise the same group header repeats.
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 groupgroup—the group name stringchildren—the list items in that group
The demo below groups countries by continent and customizes the group rendering.
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
getItemPropsonto each rendered item to preserve the component's built-in behavior. - If you replace the default Chip, destructure
onDeletefirst; it's specific toChip.
Fixed options
To lock certain tags so they can't be removed, mark their chips as disabled.
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.
Asynchronous requests
The component supports two async patterns:
- Load on open: wait until the user interacts before fetching options.
- Search as you type: make a new request on every keystroke.
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.
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 asclassName,data-item-index, andtabIndex; omitonDeleteunless you render a Chip.
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
config(object [optional]):
config.ignoreAccents(bool [optional]): Defaults totrue. Removes diacritics.config.ignoreCase(bool [optional]): Defaults totrue. Lowercases everything.config.limit(number [optional]): Defaults tonull. 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 tofalse. 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.
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.
GitHub's picker
A reproduction of GitHub's label picker:
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
AutocompleteCloseReasonandAutocompleteHighlightChangeReasonto 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.
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 thanid="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.