The autocomplete is a normal text input enhanced by a panel of suggested options.
The widget is useful for setting the value of a single-line textbox in one of two types of scenarios:
Itās meant to be an improved version of the āreact-selectā and ādownshiftā packages.
The value must be chosen from a predefined set of allowed values.
{{ādemoā: āComboBox.jsā}}
By default, the component accepts the following options structures:
interface AutocompleteOption {
label: string;
}
// or
type AutocompleteOption = string;
for instance:
const options = [
{ label: 'The Godfather', id: 1 },
{ label: 'Pulp Fiction', id: 2 },
];
// or
const options = ['The Godfather', 'Pulp Fiction'];
However, you can use different structures by providing a getOptionLabel
prop.
If your options are objects, you must provide the isOptionEqualToValue
prop to ensure correct selection and highlighting. By default, it uses strict equality to compare options with the current value.
Each of the following examples demonstrates one feature of the Autocomplete component.
{{ādemoā: āPlayground.jsā}}
Choose one of the 248 countries.
{{ādemoā: āCountrySelect.jsā}}
The component has two states that can be controlled:
value
/onChange
props combination. This state represents the value selected by the user, for instance when pressing Enter.inputValue
/onInputChange
props combination. This state represents the value displayed in the textbox.These two states are isolated, and should be controlled independently.
- A component is controlled when itās managed by its parent using props.
- A component is uncontrolled when itās managed by its own local state.
Learn more about controlled and uncontrolled components in the React documentation.
{{ādemoā: āControllableStates.jsā}}
If you control the
value
, make sure itās referentially stable between renders. In other words, the reference to the value shouldnāt change if the value itself doesnāt change.
// ā ļø BAD return <Autocomplete multiple value={allValues.filter((v) => v.selected)} />; // š GOOD const selectedValues = React.useMemo( () => allValues.filter((v) => v.selected), [allValues], ); return <Autocomplete multiple value={selectedValues} />;
In the first example,
allValues.filter
is called and returns a new array every render. The fix includes memoizing the value, so it changes only when needed.
Set freeSolo
to true so the textbox can contain any arbitrary value.
The prop is designed to cover the primary use case of a search input with suggestions, for example Google search or react-autowhatever.
{{ādemoā: āFreeSolo.jsā}}
Be careful when using the free solo mode with non-string options, as it may cause type mismatch.
The value created by typing into the textbox is always a string, regardless of the type of the options.
If you intend to use this mode for a combo box like experience (an enhanced version of a select element) we recommend setting:
selectOnFocus
to help the user clear the selected value.clearOnBlur
to help the user enter a new value.handleHomeEndKeys
to move focus inside the popup with the Home and End keys.Add "YOUR SEARCH"
.{{ādemoā: āFreeSoloCreateOption.jsā}}
You could also display a dialog when the user wants to add a new value.
{{ādemoā: āFreeSoloCreateOptionDialog.jsā}}
You can group the options with the groupBy
prop.
If you do so, make sure that the options are also sorted with the same dimension that they are grouped by,
otherwise, you will notice duplicate headers.
{{ādemoā: āGrouped.jsā}}
To control how the groups are rendered, provide a custom renderGroup
prop.
This is a function that accepts an object with two fields:
group
āa string representing a group namechildren
āa collection of list items that belong to the groupThe following demo shows how to use this prop to define custom markup and override the styles of the default groups:
{{ādemoā: āRenderGroup.jsā}}
{{ādemoā: āDisabledOptions.jsā}}
useAutocomplete
For advanced customization use cases, a headless useAutocomplete()
hook is exposed.
It accepts almost the same options as the Autocomplete component minus all the props
related to the rendering of JSX.
The Autocomplete component is built on this hook.
import { useAutocomplete } from '@mui/base/useAutocomplete';
The useAutocomplete
hook is also reexported from @mui/material for convenience and backward compatibility.
import useAutocomplete from '@mui/material/useAutocomplete';
import * as React from 'react';
import { useAutocomplete } from '@mui/base/useAutocomplete';
import { styled } from '@mui/system';
const Label = styled('label')({
display: 'block',
});
const Input = styled('input')(({ theme }) => ({
width: 200,
backgroundColor: '#fff',
color: '#000',
...theme.applyStyles('dark', {
backgroundColor: '#000',
color: '#fff',
}),
}));
const Listbox = styled('ul')(({ theme }) => ({
width: 200,
margin: 0,
padding: 0,
zIndex: 1,
position: 'absolute',
listStyle: 'none',
backgroundColor: '#fff',
overflow: 'auto',
maxHeight: 200,
border: '1px solid rgba(0,0,0,.25)',
'& li.Mui-focused': {
backgroundColor: '#4a8df6',
color: 'white',
cursor: 'pointer',
},
'& li:active': {
backgroundColor: '#2977f5',
color: 'white',
},
...theme.applyStyles('dark', {
backgroundColor: '#000',
}),
}));
export default function UseAutocomplete() {
const {
getRootProps,
getInputLabelProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions,
} = useAutocomplete({
id: 'use-autocomplete-demo',
options: top100Films,
getOptionLabel: (option) => option.title,
});
return (
<div>
<div {...getRootProps()}>
<Label {...getInputLabelProps()}>useAutocomplete</Label>
<Input {...getInputProps()} />
</div>
{groupedOptions.length > 0 ? (
<Listbox {...getListboxProps()}>
{(groupedOptions as typeof top100Films).map((option, index) => {
const { key, ...optionProps } = getOptionProps({ option, index });
return (
<li key={key} {...optionProps}>
{option.title}
</li>
);
})}
</Listbox>
) : null}
</div>
);
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: "Schindler's List", year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{
title: 'The Lord of the Rings: The Return of the King',
year: 2003,
},
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{
title: 'The Lord of the Rings: The Fellowship of the Ring',
year: 2001,
},
{
title: 'Star Wars: Episode V - The Empire Strikes Back',
year: 1980,
},
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
{
title: 'The Lord of the Rings: The Two Towers',
year: 2002,
},
{ title: "One Flew Over the Cuckoo's Nest", year: 1975 },
{ title: 'Goodfellas', year: 1990 },
{ title: 'The Matrix', year: 1999 },
{ title: 'Seven Samurai', year: 1954 },
{
title: 'Star Wars: Episode IV - A New Hope',
year: 1977,
},
{ title: 'City of God', year: 2002 },
{ title: 'Se7en', year: 1995 },
{ title: 'The Silence of the Lambs', year: 1991 },
{ title: "It's a Wonderful Life", year: 1946 },
{ title: 'Life Is Beautiful', year: 1997 },
{ title: 'The Usual Suspects', year: 1995 },
{ title: 'LƩon: The Professional', year: 1994 },
{ title: 'Spirited Away', year: 2001 },
{ title: 'Saving Private Ryan', year: 1998 },
{ title: 'Once Upon a Time in the West', year: 1968 },
{ title: 'American History X', year: 1998 },
{ title: 'Interstellar', year: 2014 },
{ title: 'Casablanca', year: 1942 },
{ title: 'City Lights', year: 1931 },
{ title: 'Psycho', year: 1960 },
{ title: 'The Green Mile', year: 1999 },
{ title: 'The Intouchables', year: 2011 },
{ title: 'Modern Times', year: 1936 },
{ title: 'Raiders of the Lost Ark', year: 1981 },
{ title: 'Rear Window', year: 1954 },
{ title: 'The Pianist', year: 2002 },
{ title: 'The Departed', year: 2006 },
{ title: 'Terminator 2: Judgment Day', year: 1991 },
{ title: 'Back to the Future', year: 1985 },
{ title: 'Whiplash', year: 2014 },
{ title: 'Gladiator', year: 2000 },
{ title: 'Memento', year: 2000 },
{ title: 'The Prestige', year: 2006 },
{ title: 'The Lion King', year: 1994 },
{ title: 'Apocalypse Now', year: 1979 },
{ title: 'Alien', year: 1979 },
{ title: 'Sunset Boulevard', year: 1950 },
{
title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb',
year: 1964,
},
{ title: 'The Great Dictator', year: 1940 },
{ title: 'Cinema Paradiso', year: 1988 },
{ title: 'The Lives of Others', year: 2006 },
{ title: 'Grave of the Fireflies', year: 1988 },
{ title: 'Paths of Glory', year: 1957 },
{ title: 'Django Unchained', year: 2012 },
{ title: 'The Shining', year: 1980 },
{ title: 'WALLĀ·E', year: 2008 },
{ title: 'American Beauty', year: 1999 },
{ title: 'The Dark Knight Rises', year: 2012 },
{ title: 'Princess Mononoke', year: 1997 },
{ title: 'Aliens', year: 1986 },
{ title: 'Oldboy', year: 2003 },
{ title: 'Once Upon a Time in America', year: 1984 },
{ title: 'Witness for the Prosecution', year: 1957 },
{ title: 'Das Boot', year: 1981 },
{ title: 'Citizen Kane', year: 1941 },
{ title: 'North by Northwest', year: 1959 },
{ title: 'Vertigo', year: 1958 },
{
title: 'Star Wars: Episode VI - Return of the Jedi',
year: 1983,
},
{ title: 'Reservoir Dogs', year: 1992 },
{ title: 'Braveheart', year: 1995 },
{ title: 'M', year: 1931 },
{ title: 'Requiem for a Dream', year: 2000 },
{ title: 'AmƩlie', year: 2001 },
{ title: 'A Clockwork Orange', year: 1971 },
{ title: 'Like Stars on Earth', year: 2007 },
{ title: 'Taxi Driver', year: 1976 },
{ title: 'Lawrence of Arabia', year: 1962 },
{ title: 'Double Indemnity', year: 1944 },
{
title: 'Eternal Sunshine of the Spotless Mind',
year: 2004,
},
{ title: 'Amadeus', year: 1984 },
{ title: 'To Kill a Mockingbird', year: 1962 },
{ title: 'Toy Story 3', year: 2010 },
{ title: 'Logan', year: 2017 },
{ title: 'Full Metal Jacket', year: 1987 },
{ title: 'Dangal', year: 2016 },
{ title: 'The Sting', year: 1973 },
{ title: '2001: A Space Odyssey', year: 1968 },
{ title: "Singin' in the Rain", year: 1952 },
{ title: 'Toy Story', year: 1995 },
{ title: 'Bicycle Thieves', year: 1948 },
{ title: 'The Kid', year: 1921 },
{ title: 'Inglourious Basterds', year: 2009 },
{ title: 'Snatch', year: 2000 },
{ title: '3 Idiots', year: 2009 },
{ title: 'Monty Python and the Holy Grail', year: 1975 },
];
{{ādemoā: āCustomizedHook.jsā}}
Head to the customization section for an example with the Autocomplete
component instead of the hook.
The component supports two different asynchronous use-cases:
It displays a progress state as long as the network request is pending.
{{ādemoā: āAsynchronous.jsā}}
If your logic is fetching new options on each keystroke and using the current value of the textbox to filter on the server, you may want to consider throttling requests.
Additionally, you will need to disable the built-in filtering of the Autocomplete
component by
overriding the filterOptions
prop:
<Autocomplete filterOptions={(x) => x} />
A customized UI for Google Maps Places Autocomplete. For this demo, we need to load the Google Maps JavaScript and Google Places API.
The following demo relies on autosuggest-highlight, a small (1 kB) utility for highlighting text in autosuggest and autocomplete components.
{{ādemoā: āGoogleMaps.jsā}}
:::error Before you can start using the Google Maps JavaScript API and Places API, you need to get your own API key. :::
Also known as tags, the user is allowed to enter more than one value.
{{ādemoā: āTags.jsā}}
In the event that you need to lock certain tags so that they canāt be removed, you can set the chips disabled.
{{ādemoā: āFixedTags.jsā}}
{{ādemoā: āCheckboxesTags.jsā}}
You can use the limitTags
prop to limit the number of displayed options when not focused.
{{ādemoā: āLimitTags.jsā}}
Fancy smaller inputs? Use the size
prop.
{{ādemoā: āSizes.jsā}}
The renderInput
prop allows you to customize the rendered input.
The first argument of this render prop contains props that you need to forward.
Pay specific attention to the ref
and inputProps
keys.
If youāre using a custom input component inside the Autocomplete, make sure that you forward the ref to the underlying DOM element.
{{ādemoā: āCustomInputAutocomplete.jsā}}
To globally customize the Autocomplete options for all components in your app,
you can use the theme default props and set the renderOption
property in the defaultProps
key.
The renderOption
property takes the ownerState
as the fourth parameter, which includes props and internal component state.
To display the label, you can use the getOptionLabel
prop from the ownerState
.
This approach enables different options for each Autocomplete component while keeping the options styling consistent.
{{ādemoā: āGloballyCustomizedOptions.jsā}}
This demo reproduces GitHubās label picker:
{{ādemoā: āGitHubLabel.jsā}}
Head to the Customized hook section for a customization example with the useAutocomplete
hook instead of the component.
The following demo shows how to add a hint feature to the Autocomplete:
{{ādemoā: āAutocompleteHint.jsā}}
The following demo relies on autosuggest-highlight, a small (1 kB) utility for highlighting text in autosuggest and autocomplete components.
{{ādemoā: āHighlights.jsā}}
The component exposes a factory to create a filter method that can be provided to the filterOptions
prop.
You can use it to change the default option filter behavior.
import { createFilterOptions } from '@mui/material/Autocomplete';
createFilterOptions(config) => filterOptions
config
(object [optional]):config.ignoreAccents
(bool [optional]): Defaults to true
. Remove diacritics.config.ignoreCase
(bool [optional]): Defaults to true
. Lowercase everything.config.limit
(number [optional]): Default to null. Limit the number of suggested options to be shown. For example, if config.limit
is 100
, only the first 100
matching options are shown. It can be useful if a lot of options match and virtualization wasnāt set up.config.matchFrom
(āanyā | āstartā [optional]): Defaults to 'any'
.config.stringify
(func [optional]): Controls how an option is converted into a string so that it can be matched against the input text fragment.config.trim
(bool [optional]): Defaults to false
. Remove trailing spaces.filterOptions
: the returned filter method can be provided directly to the filterOptions
prop of the Autocomplete
component, or the parameter of the same name for the hook.
In the following demo, the options need to start with the query prefix:
const filterOptions = createFilterOptions({
matchFrom: 'start',
stringify: (option) => option.title,
});
<Autocomplete filterOptions={filterOptions} />;
import * as React from 'react';
import TextField from '@mui/material/TextField';
import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
const filterOptions = createFilterOptions({
matchFrom: 'start',
stringify: (option: FilmOptionType) => option.title,
});
export default function Filter() {
return (
<Autocomplete
options={top100Films}
getOptionLabel={(option) => option.title}
filterOptions={filterOptions}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Custom filter" />}
/>
);
}
interface FilmOptionType {
title: string;
year: number;
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: "Schindler's List", year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{
title: 'The Lord of the Rings: The Return of the King',
year: 2003,
},
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{
title: 'The Lord of the Rings: The Fellowship of the Ring',
year: 2001,
},
{
title: 'Star Wars: Episode V - The Empire Strikes Back',
year: 1980,
},
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
{
title: 'The Lord of the Rings: The Two Towers',
year: 2002,
},
{ title: "One Flew Over the Cuckoo's Nest", year: 1975 },
{ title: 'Goodfellas', year: 1990 },
{ title: 'The Matrix', year: 1999 },
{ title: 'Seven Samurai', year: 1954 },
{
title: 'Star Wars: Episode IV - A New Hope',
year: 1977,
},
{ title: 'City of God', year: 2002 },
{ title: 'Se7en', year: 1995 },
{ title: 'The Silence of the Lambs', year: 1991 },
{ title: "It's a Wonderful Life", year: 1946 },
{ title: 'Life Is Beautiful', year: 1997 },
{ title: 'The Usual Suspects', year: 1995 },
{ title: 'LƩon: The Professional', year: 1994 },
{ title: 'Spirited Away', year: 2001 },
{ title: 'Saving Private Ryan', year: 1998 },
{ title: 'Once Upon a Time in the West', year: 1968 },
{ title: 'American History X', year: 1998 },
{ title: 'Interstellar', year: 2014 },
{ title: 'Casablanca', year: 1942 },
{ title: 'City Lights', year: 1931 },
{ title: 'Psycho', year: 1960 },
{ title: 'The Green Mile', year: 1999 },
{ title: 'The Intouchables', year: 2011 },
{ title: 'Modern Times', year: 1936 },
{ title: 'Raiders of the Lost Ark', year: 1981 },
{ title: 'Rear Window', year: 1954 },
{ title: 'The Pianist', year: 2002 },
{ title: 'The Departed', year: 2006 },
{ title: 'Terminator 2: Judgment Day', year: 1991 },
{ title: 'Back to the Future', year: 1985 },
{ title: 'Whiplash', year: 2014 },
{ title: 'Gladiator', year: 2000 },
{ title: 'Memento', year: 2000 },
{ title: 'The Prestige', year: 2006 },
{ title: 'The Lion King', year: 1994 },
{ title: 'Apocalypse Now', year: 1979 },
{ title: 'Alien', year: 1979 },
{ title: 'Sunset Boulevard', year: 1950 },
{
title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb',
year: 1964,
},
{ title: 'The Great Dictator', year: 1940 },
{ title: 'Cinema Paradiso', year: 1988 },
{ title: 'The Lives of Others', year: 2006 },
{ title: 'Grave of the Fireflies', year: 1988 },
{ title: 'Paths of Glory', year: 1957 },
{ title: 'Django Unchained', year: 2012 },
{ title: 'The Shining', year: 1980 },
{ title: 'WALLĀ·E', year: 2008 },
{ title: 'American Beauty', year: 1999 },
{ title: 'The Dark Knight Rises', year: 2012 },
{ title: 'Princess Mononoke', year: 1997 },
{ title: 'Aliens', year: 1986 },
{ title: 'Oldboy', year: 2003 },
{ title: 'Once Upon a Time in America', year: 1984 },
{ title: 'Witness for the Prosecution', year: 1957 },
{ title: 'Das Boot', year: 1981 },
{ title: 'Citizen Kane', year: 1941 },
{ title: 'North by Northwest', year: 1959 },
{ title: 'Vertigo', year: 1958 },
{
title: 'Star Wars: Episode VI - Return of the Jedi',
year: 1983,
},
{ title: 'Reservoir Dogs', year: 1992 },
{ title: 'Braveheart', year: 1995 },
{ title: 'M', year: 1931 },
{ title: 'Requiem for a Dream', year: 2000 },
{ title: 'AmƩlie', year: 2001 },
{ title: 'A Clockwork Orange', year: 1971 },
{ title: 'Like Stars on Earth', year: 2007 },
{ title: 'Taxi Driver', year: 1976 },
{ title: 'Lawrence of Arabia', year: 1962 },
{ title: 'Double Indemnity', year: 1944 },
{
title: 'Eternal Sunshine of the Spotless Mind',
year: 2004,
},
{ title: 'Amadeus', year: 1984 },
{ title: 'To Kill a Mockingbird', year: 1962 },
{ title: 'Toy Story 3', year: 2010 },
{ title: 'Logan', year: 2017 },
{ title: 'Full Metal Jacket', year: 1987 },
{ title: 'Dangal', year: 2016 },
{ title: 'The Sting', year: 1973 },
{ title: '2001: A Space Odyssey', year: 1968 },
{ title: "Singin' in the Rain", year: 1952 },
{ title: 'Toy Story', year: 1995 },
{ title: 'Bicycle Thieves', year: 1948 },
{ title: 'The Kid', year: 1921 },
{ title: 'Inglourious Basterds', year: 2009 },
{ title: 'Snatch', year: 2000 },
{ title: '3 Idiots', year: 2009 },
{ title: 'Monty Python and the Holy Grail', year: 1975 },
];
For richer filtering mechanisms, like fuzzy matching, itās recommended to look at match-sorter. For instance:
import { matchSorter } from 'match-sorter';
const filterOptions = (options, { inputValue }) => matchSorter(options, inputValue);
<Autocomplete filterOptions={filterOptions} />;
Search within 10,000 randomly generated options. The list is virtualized thanks to react-window.
{{ādemoā: āVirtualize.jsā}}
If you would like to prevent the default key handler behavior, you can set the eventās defaultMuiPrevented
property to true
:
<Autocomplete
onKeyDown={(event) => {
if (event.key === 'Enter') {
// Prevent's default 'Enter' behavior.
event.defaultMuiPrevented = true;
// your handler code
}
}}
/>
Browsers have heuristics to help the user fill in form inputs. However, this can harm the UX of the component.
By default, the component disables the input autocomplete feature (remembering what the user has typed for a given field in a previous session) with the autoComplete="off"
attribute.
Google Chrome does not currently support this attribute setting (Issue 41239842).
A possible workaround is to remove the id
to have the component generate a random one.
In addition to remembering past entered values, the browser might also propose autofill suggestions (saved login, address, or payment details). In the event you want the avoid autofill, you can try the following:
Name the input without leaking any information the browser can use. For example id="field1"
instead of id="country"
. If you leave the id empty, the component uses a random id.
Set autoComplete="new-password"
(some browsers will suggest a strong password for inputs with this attribute setting):
<TextField
{...params}
inputProps={{
...params.inputProps,
autoComplete: 'new-password',
}}
/>
Read the guide on MDN for more details.
VoiceOver on iOS Safari doesnāt support the aria-owns
attribute very well.
You can work around the issue with the disablePortal
prop.
If you provide a custom ListboxComponent
prop, you need to make sure that the intended scroll container has the role
attribute set to listbox
. This ensures the correct behavior of the scroll, for example when using the keyboard to navigate.
(WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)
We encourage the usage of a label for the textbox. The component implements the WAI-ARIA authoring practices.