An Autocomplete is a component that allows for real-time suggestions from a
pre-determined list as the user types by filtering data based on the current
value. It can also be used to interact with an API that handles the sorting,
filtering, matching, etc as well.
An Autocomplete requires a list of options which can be strings or an object
with a label or name string so that the options can be filtered as the user
types. In addition, a listboxLabel or listboxLabelledby prop must be
defined for accessibility to provide a label for the listbox.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function SimpleExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={fruits}
/>
);
}
export const fruits = [
"Apple",
"Apricot",
"Banana",
"Blueberry",
"Cranberry",
"Kiwi",
"Mango",
"Orange",
"Peach",
"Plum",
"Strawberry",
];
This is the same example as above, but using options as { label: string }.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function SimpleLabelExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={options}
/>
);
}
const options = [
{ label: "Apple" },
{ label: "Apricot" },
{ label: "Banana" },
{ label: "Blueberry" },
{ label: "Cranberry" },
{ label: "Kiwi" },
{ label: "Mango" },
{ label: "Orange" },
{ label: "Peach" },
{ label: "Plum" },
{ label: "Strawberry" },
];
The Autocomplete can support any object option by also providing an
getOptionLabel prop that will return a string value for each option.
See Typescript Typing for type behavior.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function ObjectOptionsExample(): ReactElement {
return (
<Autocomplete label="State" options={desserts} listboxLabel="States" />
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
The Autocomplete will automatically pass through common Option props that
exist on each option.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { cssUtils } from "@react-md/core/cssUtils";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";
const options = [
{
label: "Favorite Left Addon",
leftAddon: <FavoriteIcon />,
},
{
label: "Favorite Right Addon",
// children will be shown in the Option by default and should normally
// contain the same text as the `label` since the label is the searchable
// part. the main usage of the `children` is to apply any custom styles or
// highlighting
children: (
<>
<span className={cssUtils({ fontWeight: "bold" })}>Favorite</span>{" "}
<span className={cssUtils({ textDecoration: "underline" })}>Right</span>{" "}
Addon
</>
),
rightAddon: <FavoriteIcon />,
},
{
label: "Multiline",
secondaryText: "Second line of text that is ignored in filtering",
multiline: true,
},
];
export default function OptionPropsExample(): ReactElement {
return (
<Autocomplete label="Label" options={options} listboxLabel="Options" />
);
}
Another way to pass props to each option is using the getOptionProps function.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { Avatar } from "@react-md/core/avatar/Avatar";
import { cssUtils } from "@react-md/core/cssUtils";
import { cnb } from "cnbuilder";
import { type ReactElement } from "react";
export default function GetOptionPropsExample(): ReactElement {
return (
<Autocomplete
label="Dessert"
options={desserts}
getOptionProps={({ index, option }) => ({
className: cnb(
index % 3 === 0 && cssUtils({ textDecoration: "line-through" }),
),
leftAddon: <Avatar size="icon">{option.type.charAt(0)}</Avatar>,
})}
listboxLabel="Desserts"
/>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
An option can be disabled by adding disabled: true to the option or using getOptionProps
to return disabled: true.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function DisabledOptionsExample(): ReactElement {
return (
<Autocomplete
label="Dessert"
options={desserts.map((dessert, i) => ({
...dessert,
disabled: i % 4 === 0,
}))}
// or try
// getOptionProps={({ index, option }) => ({
// disabled: option.disabled || index % 4 === 0 || option.type === "Other",
// })}
listboxLabel="Desserts"
/>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
If the current value is needed, provide a value and setValue prop.
Value: "null"
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { Box } from "@react-md/core/box/Box";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement, useState } from "react";
export default function ControllingTheValueExample(): ReactElement {
const [value, setValue] = useState<State | null>(null);
return (
<Box align="start" stacked>
<Autocomplete
label="State"
value={value}
setValue={setValue}
options={states}
listboxLabel="States"
/>
<Typography>Value: {`"${value?.name || null}"`}</Typography>
</Box>
);
}
export const states = [
{
name: "Alabama",
abbreviation: "AL",
},
{
name: "Alaska",
abbreviation: "AK",
},
{
name: "American Samoa",
abbreviation: "AS",
},
{
name: "Arizona",
abbreviation: "AZ",
},
{
name: "Arkansas",
abbreviation: "AR",
},
{
name: "California",
abbreviation: "CA",
},
{
name: "Colorado",
abbreviation: "CO",
},
{
name: "Connecticut",
abbreviation: "CT",
},
{
name: "Delaware",
abbreviation: "DE",
},
{
name: "District Of Columbia",
abbreviation: "DC",
},
{
name: "Federated States Of Micronesia",
abbreviation: "FM",
},
{
name: "Florida",
abbreviation: "FL",
},
{
name: "Georgia",
abbreviation: "GA",
},
{
name: "Guam",
abbreviation: "GU",
},
{
name: "Hawaii",
abbreviation: "HI",
},
{
name: "Idaho",
abbreviation: "ID",
},
{
name: "Illinois",
abbreviation: "IL",
},
{
name: "Indiana",
abbreviation: "IN",
},
{
name: "Iowa",
abbreviation: "IA",
},
{
name: "Kansas",
abbreviation: "KS",
},
{
name: "Kentucky",
abbreviation: "KY",
},
{
name: "Louisiana",
abbreviation: "LA",
},
{
name: "Maine",
abbreviation: "ME",
},
{
name: "Marshall Islands",
abbreviation: "MH",
},
{
name: "Maryland",
abbreviation: "MD",
},
{
name: "Massachusetts",
abbreviation: "MA",
},
{
name: "Michigan",
abbreviation: "MI",
},
{
name: "Minnesota",
abbreviation: "MN",
},
{
name: "Mississippi",
abbreviation: "MS",
},
{
name: "Missouri",
abbreviation: "MO",
},
{
name: "Montana",
abbreviation: "MT",
},
{
name: "Nebraska",
abbreviation: "NE",
},
{
name: "Nevada",
abbreviation: "NV",
},
{
name: "New Hampshire",
abbreviation: "NH",
},
{
name: "New Jersey",
abbreviation: "NJ",
},
{
name: "New Mexico",
abbreviation: "NM",
},
{
name: "New York",
abbreviation: "NY",
},
{
name: "North Carolina",
abbreviation: "NC",
},
{
name: "North Dakota",
abbreviation: "ND",
},
{
name: "Northern Mariana Islands",
abbreviation: "MP",
},
{
name: "Ohio",
abbreviation: "OH",
},
{
name: "Oklahoma",
abbreviation: "OK",
},
{
name: "Oregon",
abbreviation: "OR",
},
{
name: "Pennsylvania",
abbreviation: "PA",
},
{
name: "Puerto Rico",
abbreviation: "PR",
},
{
name: "Rhode Island",
abbreviation: "RI",
},
{
name: "South Carolina",
abbreviation: "SC",
},
{
name: "South Dakota",
abbreviation: "SD",
},
{
name: "Tennessee",
abbreviation: "TN",
},
{
name: "Texas",
abbreviation: "TX",
},
{
name: "Utah",
abbreviation: "UT",
},
{
name: "Vermont",
abbreviation: "VT",
},
{
name: "Virgin Islands",
abbreviation: "VI",
},
{
name: "Virginia",
abbreviation: "VA",
},
{
name: "Washington",
abbreviation: "WA",
},
{
name: "West Virginia",
abbreviation: "WV",
},
{
name: "Wisconsin",
abbreviation: "WI",
},
{
name: "Wyoming",
abbreviation: "WY",
},
] as const;
export type StateName = (typeof states)[number]["name"];
export type StateAbbreviation = (typeof states)[number]["abbreviation"];
export interface State {
name: StateName;
abbreviation: StateAbbreviation;
}
If the value does not need to be controlled but one of the options should
be selected by default, set the defaultValue prop instead.
See Typescript Typing for type behavior.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function SettingADefaultValueExample(): ReactElement {
const defaultValue: State = states[9];
return (
<Autocomplete
label="State"
options={states}
defaultValue={defaultValue}
listboxLabel="States"
/>
);
}
export const states = [
{
name: "Alabama",
abbreviation: "AL",
},
{
name: "Alaska",
abbreviation: "AK",
},
{
name: "American Samoa",
abbreviation: "AS",
},
{
name: "Arizona",
abbreviation: "AZ",
},
{
name: "Arkansas",
abbreviation: "AR",
},
{
name: "California",
abbreviation: "CA",
},
{
name: "Colorado",
abbreviation: "CO",
},
{
name: "Connecticut",
abbreviation: "CT",
},
{
name: "Delaware",
abbreviation: "DE",
},
{
name: "District Of Columbia",
abbreviation: "DC",
},
{
name: "Federated States Of Micronesia",
abbreviation: "FM",
},
{
name: "Florida",
abbreviation: "FL",
},
{
name: "Georgia",
abbreviation: "GA",
},
{
name: "Guam",
abbreviation: "GU",
},
{
name: "Hawaii",
abbreviation: "HI",
},
{
name: "Idaho",
abbreviation: "ID",
},
{
name: "Illinois",
abbreviation: "IL",
},
{
name: "Indiana",
abbreviation: "IN",
},
{
name: "Iowa",
abbreviation: "IA",
},
{
name: "Kansas",
abbreviation: "KS",
},
{
name: "Kentucky",
abbreviation: "KY",
},
{
name: "Louisiana",
abbreviation: "LA",
},
{
name: "Maine",
abbreviation: "ME",
},
{
name: "Marshall Islands",
abbreviation: "MH",
},
{
name: "Maryland",
abbreviation: "MD",
},
{
name: "Massachusetts",
abbreviation: "MA",
},
{
name: "Michigan",
abbreviation: "MI",
},
{
name: "Minnesota",
abbreviation: "MN",
},
{
name: "Mississippi",
abbreviation: "MS",
},
{
name: "Missouri",
abbreviation: "MO",
},
{
name: "Montana",
abbreviation: "MT",
},
{
name: "Nebraska",
abbreviation: "NE",
},
{
name: "Nevada",
abbreviation: "NV",
},
{
name: "New Hampshire",
abbreviation: "NH",
},
{
name: "New Jersey",
abbreviation: "NJ",
},
{
name: "New Mexico",
abbreviation: "NM",
},
{
name: "New York",
abbreviation: "NY",
},
{
name: "North Carolina",
abbreviation: "NC",
},
{
name: "North Dakota",
abbreviation: "ND",
},
{
name: "Northern Mariana Islands",
abbreviation: "MP",
},
{
name: "Ohio",
abbreviation: "OH",
},
{
name: "Oklahoma",
abbreviation: "OK",
},
{
name: "Oregon",
abbreviation: "OR",
},
{
name: "Pennsylvania",
abbreviation: "PA",
},
{
name: "Puerto Rico",
abbreviation: "PR",
},
{
name: "Rhode Island",
abbreviation: "RI",
},
{
name: "South Carolina",
abbreviation: "SC",
},
{
name: "South Dakota",
abbreviation: "SD",
},
{
name: "Tennessee",
abbreviation: "TN",
},
{
name: "Texas",
abbreviation: "TX",
},
{
name: "Utah",
abbreviation: "UT",
},
{
name: "Vermont",
abbreviation: "VT",
},
{
name: "Virgin Islands",
abbreviation: "VI",
},
{
name: "Virginia",
abbreviation: "VA",
},
{
name: "Washington",
abbreviation: "WA",
},
{
name: "West Virginia",
abbreviation: "WV",
},
{
name: "Wisconsin",
abbreviation: "WI",
},
{
name: "Wyoming",
abbreviation: "WY",
},
] as const;
export type StateName = (typeof states)[number]["name"];
export type StateAbbreviation = (typeof states)[number]["abbreviation"];
export interface State {
name: StateName;
abbreviation: StateAbbreviation;
}
It is recommended to control the value instead of using this option.
When the value does not need to be stored anywhere but can be used for other
actions, the onValueChange prop can be used.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function GettingTheCurrentValueExample(): ReactElement {
const defaultValue: State = states[9];
return (
<Autocomplete
label="State"
options={states}
defaultValue={defaultValue}
listboxLabel="States"
onValueChange={(value) => {
// Do something with the value. Should generally not call `setState`
// eslint-disable-next-line no-console
console.log("value:", value);
}}
/>
);
}
export const states = [
{
name: "Alabama",
abbreviation: "AL",
},
{
name: "Alaska",
abbreviation: "AK",
},
{
name: "American Samoa",
abbreviation: "AS",
},
{
name: "Arizona",
abbreviation: "AZ",
},
{
name: "Arkansas",
abbreviation: "AR",
},
{
name: "California",
abbreviation: "CA",
},
{
name: "Colorado",
abbreviation: "CO",
},
{
name: "Connecticut",
abbreviation: "CT",
},
{
name: "Delaware",
abbreviation: "DE",
},
{
name: "District Of Columbia",
abbreviation: "DC",
},
{
name: "Federated States Of Micronesia",
abbreviation: "FM",
},
{
name: "Florida",
abbreviation: "FL",
},
{
name: "Georgia",
abbreviation: "GA",
},
{
name: "Guam",
abbreviation: "GU",
},
{
name: "Hawaii",
abbreviation: "HI",
},
{
name: "Idaho",
abbreviation: "ID",
},
{
name: "Illinois",
abbreviation: "IL",
},
{
name: "Indiana",
abbreviation: "IN",
},
{
name: "Iowa",
abbreviation: "IA",
},
{
name: "Kansas",
abbreviation: "KS",
},
{
name: "Kentucky",
abbreviation: "KY",
},
{
name: "Louisiana",
abbreviation: "LA",
},
{
name: "Maine",
abbreviation: "ME",
},
{
name: "Marshall Islands",
abbreviation: "MH",
},
{
name: "Maryland",
abbreviation: "MD",
},
{
name: "Massachusetts",
abbreviation: "MA",
},
{
name: "Michigan",
abbreviation: "MI",
},
{
name: "Minnesota",
abbreviation: "MN",
},
{
name: "Mississippi",
abbreviation: "MS",
},
{
name: "Missouri",
abbreviation: "MO",
},
{
name: "Montana",
abbreviation: "MT",
},
{
name: "Nebraska",
abbreviation: "NE",
},
{
name: "Nevada",
abbreviation: "NV",
},
{
name: "New Hampshire",
abbreviation: "NH",
},
{
name: "New Jersey",
abbreviation: "NJ",
},
{
name: "New Mexico",
abbreviation: "NM",
},
{
name: "New York",
abbreviation: "NY",
},
{
name: "North Carolina",
abbreviation: "NC",
},
{
name: "North Dakota",
abbreviation: "ND",
},
{
name: "Northern Mariana Islands",
abbreviation: "MP",
},
{
name: "Ohio",
abbreviation: "OH",
},
{
name: "Oklahoma",
abbreviation: "OK",
},
{
name: "Oregon",
abbreviation: "OR",
},
{
name: "Pennsylvania",
abbreviation: "PA",
},
{
name: "Puerto Rico",
abbreviation: "PR",
},
{
name: "Rhode Island",
abbreviation: "RI",
},
{
name: "South Carolina",
abbreviation: "SC",
},
{
name: "South Dakota",
abbreviation: "SD",
},
{
name: "Tennessee",
abbreviation: "TN",
},
{
name: "Texas",
abbreviation: "TX",
},
{
name: "Utah",
abbreviation: "UT",
},
{
name: "Vermont",
abbreviation: "VT",
},
{
name: "Virgin Islands",
abbreviation: "VI",
},
{
name: "Virginia",
abbreviation: "VA",
},
{
name: "Washington",
abbreviation: "WA",
},
{
name: "West Virginia",
abbreviation: "WV",
},
{
name: "Wisconsin",
abbreviation: "WI",
},
{
name: "Wyoming",
abbreviation: "WY",
},
] as const;
export type StateName = (typeof states)[number]["name"];
export type StateAbbreviation = (typeof states)[number]["abbreviation"];
export interface State {
name: StateName;
abbreviation: StateAbbreviation;
}
If the input value needs to be controlled, provide a query and setQuery prop.
Query: ""
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { Box } from "@react-md/core/box/Box";
import { Button } from "@react-md/core/button/Button";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement, useState } from "react";
export default function ControllingTheInputValueExample(): ReactElement {
const [query, setQuery] = useState("");
return (
<Box align="start" stacked>
<Autocomplete
label="State"
query={query}
setQuery={setQuery}
options={states}
listboxLabel="States"
/>
<Button
onClick={() => {
setQuery("some other value");
}}
themeType="outline"
>
Set query
</Button>
<Typography>Query: {`"${query}"`}</Typography>
</Box>
);
}
export const states = [
{
name: "Alabama",
abbreviation: "AL",
},
{
name: "Alaska",
abbreviation: "AK",
},
{
name: "American Samoa",
abbreviation: "AS",
},
{
name: "Arizona",
abbreviation: "AZ",
},
{
name: "Arkansas",
abbreviation: "AR",
},
{
name: "California",
abbreviation: "CA",
},
{
name: "Colorado",
abbreviation: "CO",
},
{
name: "Connecticut",
abbreviation: "CT",
},
{
name: "Delaware",
abbreviation: "DE",
},
{
name: "District Of Columbia",
abbreviation: "DC",
},
{
name: "Federated States Of Micronesia",
abbreviation: "FM",
},
{
name: "Florida",
abbreviation: "FL",
},
{
name: "Georgia",
abbreviation: "GA",
},
{
name: "Guam",
abbreviation: "GU",
},
{
name: "Hawaii",
abbreviation: "HI",
},
{
name: "Idaho",
abbreviation: "ID",
},
{
name: "Illinois",
abbreviation: "IL",
},
{
name: "Indiana",
abbreviation: "IN",
},
{
name: "Iowa",
abbreviation: "IA",
},
{
name: "Kansas",
abbreviation: "KS",
},
{
name: "Kentucky",
abbreviation: "KY",
},
{
name: "Louisiana",
abbreviation: "LA",
},
{
name: "Maine",
abbreviation: "ME",
},
{
name: "Marshall Islands",
abbreviation: "MH",
},
{
name: "Maryland",
abbreviation: "MD",
},
{
name: "Massachusetts",
abbreviation: "MA",
},
{
name: "Michigan",
abbreviation: "MI",
},
{
name: "Minnesota",
abbreviation: "MN",
},
{
name: "Mississippi",
abbreviation: "MS",
},
{
name: "Missouri",
abbreviation: "MO",
},
{
name: "Montana",
abbreviation: "MT",
},
{
name: "Nebraska",
abbreviation: "NE",
},
{
name: "Nevada",
abbreviation: "NV",
},
{
name: "New Hampshire",
abbreviation: "NH",
},
{
name: "New Jersey",
abbreviation: "NJ",
},
{
name: "New Mexico",
abbreviation: "NM",
},
{
name: "New York",
abbreviation: "NY",
},
{
name: "North Carolina",
abbreviation: "NC",
},
{
name: "North Dakota",
abbreviation: "ND",
},
{
name: "Northern Mariana Islands",
abbreviation: "MP",
},
{
name: "Ohio",
abbreviation: "OH",
},
{
name: "Oklahoma",
abbreviation: "OK",
},
{
name: "Oregon",
abbreviation: "OR",
},
{
name: "Pennsylvania",
abbreviation: "PA",
},
{
name: "Puerto Rico",
abbreviation: "PR",
},
{
name: "Rhode Island",
abbreviation: "RI",
},
{
name: "South Carolina",
abbreviation: "SC",
},
{
name: "South Dakota",
abbreviation: "SD",
},
{
name: "Tennessee",
abbreviation: "TN",
},
{
name: "Texas",
abbreviation: "TX",
},
{
name: "Utah",
abbreviation: "UT",
},
{
name: "Vermont",
abbreviation: "VT",
},
{
name: "Virgin Islands",
abbreviation: "VI",
},
{
name: "Virginia",
abbreviation: "VA",
},
{
name: "Washington",
abbreviation: "WA",
},
{
name: "West Virginia",
abbreviation: "WV",
},
{
name: "Wisconsin",
abbreviation: "WI",
},
{
name: "Wyoming",
abbreviation: "WY",
},
] as const;
export type StateName = (typeof states)[number]["name"];
export type StateAbbreviation = (typeof states)[number]["abbreviation"];
export interface State {
name: StateName;
abbreviation: StateAbbreviation;
}
If the input value doesn't need to be controlled, the value can be retrieved
with the normal onChange event handler.
"use client";
/* eslint-disable no-console */
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function GettingTheCurrentInputValueExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={fruits}
onChange={(event) => {
// do something with the current value
const { value } = event.currentTarget;
console.log("value:", value);
}}
/>
);
}
export const fruits = [
"Apple",
"Apricot",
"Banana",
"Blueberry",
"Cranberry",
"Kiwi",
"Mango",
"Orange",
"Peach",
"Plum",
"Strawberry",
];
There are some cases where the available options should only be suggestions and
not required value such as displaying the most recent searches. Set the filter
prop to noopAutocompleteFilter to enable this behavior which also enables the
allowAnyValue prop.
This is the default behavior when type="search" is set.
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { noopAutocompleteFilter } from "@react-md/core/autocomplete/defaults";
import { type ReactElement } from "react";
export default function DisableFilteringExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={fruits}
filter={noopAutocompleteFilter}
/>
);
}
export const fruits = [
"Apple",
"Apricot",
"Banana",
"Blueberry",
"Cranberry",
"Kiwi",
"Mango",
"Orange",
"Peach",
"Plum",
"Strawberry",
];
The Autocomplete uses the
caseInsensitiveSearch filter function by
default but can be changed using the filter prop. The filter function must
return the filtered list and is called with an object containing:
list - The list of options to filterquery - The current text field valueextractor - The extractor function to get a string for each optionThis example will show how the filter function could be swapped out for the fuzzySearch.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type AutocompleteFilterFunction } from "@react-md/core/autocomplete/types";
import { caseInsensitiveSearch } from "@react-md/core/searching/caseInsensitive";
import { fuzzySearch } from "@react-md/core/searching/fuzzy";
import { type ReactElement } from "react";
const NO_STARTS_WITH = false;
const filter: AutocompleteFilterFunction<State> = (options) => {
// these are all provided
// const { list, query, extractor } = options
// The default filter behavior is to require each option to start with
// the query to match, so here's an example allow matching anywhere
if (NO_STARTS_WITH) {
return caseInsensitiveSearch(options);
}
// this is about the same as the caseInsensitiveSearch, but the letters
// do not need to appear next to each other to match
return fuzzySearch(options);
};
export default function CustomFilterFunctionExample(): ReactElement {
return (
<Autocomplete
label="State"
options={states}
listboxLabel="States"
filter={filter}
/>
);
}
export const states = [
{
name: "Alabama",
abbreviation: "AL",
},
{
name: "Alaska",
abbreviation: "AK",
},
{
name: "American Samoa",
abbreviation: "AS",
},
{
name: "Arizona",
abbreviation: "AZ",
},
{
name: "Arkansas",
abbreviation: "AR",
},
{
name: "California",
abbreviation: "CA",
},
{
name: "Colorado",
abbreviation: "CO",
},
{
name: "Connecticut",
abbreviation: "CT",
},
{
name: "Delaware",
abbreviation: "DE",
},
{
name: "District Of Columbia",
abbreviation: "DC",
},
{
name: "Federated States Of Micronesia",
abbreviation: "FM",
},
{
name: "Florida",
abbreviation: "FL",
},
{
name: "Georgia",
abbreviation: "GA",
},
{
name: "Guam",
abbreviation: "GU",
},
{
name: "Hawaii",
abbreviation: "HI",
},
{
name: "Idaho",
abbreviation: "ID",
},
{
name: "Illinois",
abbreviation: "IL",
},
{
name: "Indiana",
abbreviation: "IN",
},
{
name: "Iowa",
abbreviation: "IA",
},
{
name: "Kansas",
abbreviation: "KS",
},
{
name: "Kentucky",
abbreviation: "KY",
},
{
name: "Louisiana",
abbreviation: "LA",
},
{
name: "Maine",
abbreviation: "ME",
},
{
name: "Marshall Islands",
abbreviation: "MH",
},
{
name: "Maryland",
abbreviation: "MD",
},
{
name: "Massachusetts",
abbreviation: "MA",
},
{
name: "Michigan",
abbreviation: "MI",
},
{
name: "Minnesota",
abbreviation: "MN",
},
{
name: "Mississippi",
abbreviation: "MS",
},
{
name: "Missouri",
abbreviation: "MO",
},
{
name: "Montana",
abbreviation: "MT",
},
{
name: "Nebraska",
abbreviation: "NE",
},
{
name: "Nevada",
abbreviation: "NV",
},
{
name: "New Hampshire",
abbreviation: "NH",
},
{
name: "New Jersey",
abbreviation: "NJ",
},
{
name: "New Mexico",
abbreviation: "NM",
},
{
name: "New York",
abbreviation: "NY",
},
{
name: "North Carolina",
abbreviation: "NC",
},
{
name: "North Dakota",
abbreviation: "ND",
},
{
name: "Northern Mariana Islands",
abbreviation: "MP",
},
{
name: "Ohio",
abbreviation: "OH",
},
{
name: "Oklahoma",
abbreviation: "OK",
},
{
name: "Oregon",
abbreviation: "OR",
},
{
name: "Pennsylvania",
abbreviation: "PA",
},
{
name: "Puerto Rico",
abbreviation: "PR",
},
{
name: "Rhode Island",
abbreviation: "RI",
},
{
name: "South Carolina",
abbreviation: "SC",
},
{
name: "South Dakota",
abbreviation: "SD",
},
{
name: "Tennessee",
abbreviation: "TN",
},
{
name: "Texas",
abbreviation: "TX",
},
{
name: "Utah",
abbreviation: "UT",
},
{
name: "Vermont",
abbreviation: "VT",
},
{
name: "Virgin Islands",
abbreviation: "VI",
},
{
name: "Virginia",
abbreviation: "VA",
},
{
name: "Washington",
abbreviation: "WA",
},
{
name: "West Virginia",
abbreviation: "WV",
},
{
name: "Wisconsin",
abbreviation: "WI",
},
{
name: "Wyoming",
abbreviation: "WY",
},
] as const;
export type StateName = (typeof states)[number]["name"];
export type StateAbbreviation = (typeof states)[number]["abbreviation"];
export interface State {
name: StateName;
abbreviation: StateAbbreviation;
}
The Autocomplete normally behave like a searchable select component where it
requires a specific option to be selected and will reject all other values. If
any value should be allowed, enable the allowAnyValue prop.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function AllowAnyValueExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={fruits}
allowAnyValue
/>
);
}
export const fruits = [
"Apple",
"Apricot",
"Banana",
"Blueberry",
"Cranberry",
"Kiwi",
"Mango",
"Orange",
"Peach",
"Plum",
"Strawberry",
];
An alternative to the allowAnyValue prop is to allow the user to create a new
option with their typed value by manually selecting the dynamically generated
option. There is nothing built-in to the Autocomplete component itself, but
can be easily coded with a few changes:
options are a list of objectsfilter function that inserts a new option at the end (or
beginning) of the filtered itemsgetOptionLabel function to handle the
creatable itemThis example will show how to insert a Add: "${query}" option with the list of
fruits. The Add: "${query}" is set as the children so that when the option
is selected, only the query will be inserted into the input.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { defaultAutocompleteFilter } from "@react-md/core/autocomplete/defaults";
import { type ReactElement } from "react";
export default function CreatableAutocompleteExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={fruits.map((fruit) => ({
label: fruit,
value: fruit,
}))}
filter={(options) => {
const { list, extractor, query } = options;
const filtered = defaultAutocompleteFilter(options);
if (query && !list.some((option) => extractor(option) === query)) {
return [
...filtered,
{
label: query,
value: query,
children: `Add: "${query}"`,
},
];
}
return filtered;
}}
/>
);
}
export const fruits = [
"Apple",
"Apricot",
"Banana",
"Blueberry",
"Cranberry",
"Kiwi",
"Mango",
"Orange",
"Peach",
"Plum",
"Strawberry",
];
The Autocomplete supports rendering a CircularProgress after the input field to show
async behavior. The CircularProgress will be shown while the loading prop is true.
This example below will "load" the options each time the autocomplete is opened.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { useUnmounted } from "@react-md/core/useUnmounted";
import { wait } from "@react-md/core/utils/wait";
import { type ReactElement, useState } from "react";
interface AsyncState {
loading: boolean;
options: readonly State[];
}
export default function AsyncExample(): ReactElement {
const [{ loading, options }, setState] = useState<AsyncState>({
loading: false,
options: [],
});
const unmounted = useUnmounted();
return (
<Autocomplete
label="State"
options={options}
listboxLabel="States"
loading={loading}
onOpen={async () => {
setState({ loading: true, options: [] });
await wait(800);
if (!unmounted.current) {
setState({
loading: false,
options: states,
});
}
}}
style={{ width: "18rem" }}
/>
);
}
export const states = [
{
name: "Alabama",
abbreviation: "AL",
},
{
name: "Alaska",
abbreviation: "AK",
},
{
name: "American Samoa",
abbreviation: "AS",
},
{
name: "Arizona",
abbreviation: "AZ",
},
{
name: "Arkansas",
abbreviation: "AR",
},
{
name: "California",
abbreviation: "CA",
},
{
name: "Colorado",
abbreviation: "CO",
},
{
name: "Connecticut",
abbreviation: "CT",
},
{
name: "Delaware",
abbreviation: "DE",
},
{
name: "District Of Columbia",
abbreviation: "DC",
},
{
name: "Federated States Of Micronesia",
abbreviation: "FM",
},
{
name: "Florida",
abbreviation: "FL",
},
{
name: "Georgia",
abbreviation: "GA",
},
{
name: "Guam",
abbreviation: "GU",
},
{
name: "Hawaii",
abbreviation: "HI",
},
{
name: "Idaho",
abbreviation: "ID",
},
{
name: "Illinois",
abbreviation: "IL",
},
{
name: "Indiana",
abbreviation: "IN",
},
{
name: "Iowa",
abbreviation: "IA",
},
{
name: "Kansas",
abbreviation: "KS",
},
{
name: "Kentucky",
abbreviation: "KY",
},
{
name: "Louisiana",
abbreviation: "LA",
},
{
name: "Maine",
abbreviation: "ME",
},
{
name: "Marshall Islands",
abbreviation: "MH",
},
{
name: "Maryland",
abbreviation: "MD",
},
{
name: "Massachusetts",
abbreviation: "MA",
},
{
name: "Michigan",
abbreviation: "MI",
},
{
name: "Minnesota",
abbreviation: "MN",
},
{
name: "Mississippi",
abbreviation: "MS",
},
{
name: "Missouri",
abbreviation: "MO",
},
{
name: "Montana",
abbreviation: "MT",
},
{
name: "Nebraska",
abbreviation: "NE",
},
{
name: "Nevada",
abbreviation: "NV",
},
{
name: "New Hampshire",
abbreviation: "NH",
},
{
name: "New Jersey",
abbreviation: "NJ",
},
{
name: "New Mexico",
abbreviation: "NM",
},
{
name: "New York",
abbreviation: "NY",
},
{
name: "North Carolina",
abbreviation: "NC",
},
{
name: "North Dakota",
abbreviation: "ND",
},
{
name: "Northern Mariana Islands",
abbreviation: "MP",
},
{
name: "Ohio",
abbreviation: "OH",
},
{
name: "Oklahoma",
abbreviation: "OK",
},
{
name: "Oregon",
abbreviation: "OR",
},
{
name: "Pennsylvania",
abbreviation: "PA",
},
{
name: "Puerto Rico",
abbreviation: "PR",
},
{
name: "Rhode Island",
abbreviation: "RI",
},
{
name: "South Carolina",
abbreviation: "SC",
},
{
name: "South Dakota",
abbreviation: "SD",
},
{
name: "Tennessee",
abbreviation: "TN",
},
{
name: "Texas",
abbreviation: "TX",
},
{
name: "Utah",
abbreviation: "UT",
},
{
name: "Vermont",
abbreviation: "VT",
},
{
name: "Virgin Islands",
abbreviation: "VI",
},
{
name: "Virginia",
abbreviation: "VA",
},
{
name: "Washington",
abbreviation: "WA",
},
{
name: "West Virginia",
abbreviation: "WV",
},
{
name: "Wisconsin",
abbreviation: "WI",
},
{
name: "Wyoming",
abbreviation: "WY",
},
] as const;
export type StateName = (typeof states)[number]["name"];
export type StateAbbreviation = (typeof states)[number]["abbreviation"];
export interface State {
name: StateName;
abbreviation: StateAbbreviation;
}
When the Autocomplete should be used as a <input type="search" /> to send
requests to an API as the user types to get the options, set the type="search"
and add a custom onChange handler to send the current value to the search API.
It is recommended to either debounce or throttle these search requests so that
each keystroke is not a new request.
react-md provides two hooks to help with debouncing and throttling:
This example will show a debounced implementation while the next example will show a throttled implementation.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { useDebouncedFunction } from "@react-md/core/useDebouncedFunction";
import { useUnmounted } from "@react-md/core/useUnmounted";
import { wait } from "@react-md/core/utils/wait";
import { type ReactElement, useRef, useState } from "react";
export default function DebouncedSearchExample(): ReactElement {
const [loading, setLoading] = useState(false);
const [options, setOptions] = useState<readonly string[]>([]);
const query = useRef("");
const unmounted = useUnmounted();
const search = useDebouncedFunction(async function load(
value: string,
): Promise<void> {
query.current = value;
await wait(1000);
if (!unmounted.current) {
setLoading(query.current !== value);
setOptions(
value.trim()
? Array.from({ length: 10 }, (_, i) => `${value} ${i + 1}`)
: [],
);
}
}, 500);
return (
<Autocomplete
type="search"
label="Search"
placeholder="Search..."
listboxLabel="Resultsk"
options={options}
loading={loading}
onChange={(event) => {
setLoading(true);
search(event.currentTarget.value);
}}
style={{ width: "18rem" }}
/>
);
}
This is the same example as above, but using useThrottledFunction instead.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { useThrottledFunction } from "@react-md/core/useThrottledFunction";
import { useUnmounted } from "@react-md/core/useUnmounted";
import { wait } from "@react-md/core/utils/wait";
import { type ReactElement, useRef, useState } from "react";
export default function ThrottledSearchExample(): ReactElement {
const [loading, setLoading] = useState(false);
const [options, setOptions] = useState<readonly string[]>([]);
const query = useRef("");
const unmounted = useUnmounted();
const search = useThrottledFunction(async function load(
value: string,
): Promise<void> {
query.current = value;
await wait(1000);
if (!unmounted.current) {
setLoading(query.current !== value);
setOptions(
value.trim()
? Array.from({ length: 10 }, (_, i) => `${value} ${i + 1}`)
: [],
);
}
}, 300);
return (
<Autocomplete
type="search"
label="Search"
placeholder="Search..."
listboxLabel="Resultsk"
options={options}
loading={loading}
onChange={(event) => {
setLoading(true);
void search(event.currentTarget.value);
}}
style={{ width: "18rem" }}
/>
);
}
The Autocomplete supports multiple values by either:
defaultValue to a listvalue and setting it to a listWhen multiple values are allowed, the Autocomplete will display inline chips
representing all the selected values and automatically clear the input when a
new option is selected.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement, useState } from "react";
export default function MultipleValuesExample(): ReactElement {
const [value, setValue] = useState<readonly Dessert[]>([]);
return (
<>
<Autocomplete
label="Dessert"
placeholder="Ice cream"
value={value}
setValue={setValue}
listboxLabel="Desserts"
options={desserts}
/>
</>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
To prevent the listbox from closing when an option is selected, enable the
disableCloseOnSelect prop.
This is also supported for the single select Autocomplete
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement, useState } from "react";
export default function DisableCloseOnSelectExample(): ReactElement {
const [value, setValue] = useState<readonly Dessert[]>([]);
return (
<>
<Autocomplete
label="Dessert"
placeholder="Ice cream"
value={value}
setValue={setValue}
listboxLabel="Desserts"
options={desserts}
disableCloseOnSelect
/>
</>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
The multiselect Autocomplete also supports updating all the options to use
checkbox icons by enabling the checkboxes prop. This also enables the
disableCloseOnSelect by default.
The Option will use the ICON_CONFIG.checkbox and
ICON_CONFIG.checkboxChecked icons for the unselected and selected states.
These icons can be overridden using the getOptionProps and returning
a custom selectedIcon / unselectedIcon.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement, useState } from "react";
export default function MultiselectCheckboxesExample(): ReactElement {
const [value, setValue] = useState<readonly Dessert[]>([]);
return (
<>
<Autocomplete
label="Dessert"
placeholder="Ice cream"
value={value}
setValue={setValue}
listboxLabel="Desserts"
checkboxes
options={desserts}
/>
</>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
If the selected options should not be shown as inline chips, enable the
disableInlineChips prop.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { AutocompleteChip } from "@react-md/core/autocomplete/AutocompleteChip";
import { Box } from "@react-md/core/box/Box";
import { type ReactElement, useState } from "react";
export default function DisableInlineChipsExample(): ReactElement {
const [value, setValue] = useState<readonly Dessert[]>([
desserts[1],
desserts[2],
]);
return (
<>
<Box
role="status"
aria-live="polite"
aria-label="Selected Desserts"
align="start"
fullWidth
disablePadding
>
{value.map((dessert) => (
<AutocompleteChip
key={dessert.name}
theme="outline"
onClick={() => {
setValue((prevValue) => prevValue.filter((d) => d !== dessert));
}}
>
{dessert.name}
</AutocompleteChip>
))}
</Box>
<Autocomplete
label="Dessert"
placeholder="Ice cream"
value={value}
setValue={setValue}
listboxLabel="Desserts"
options={desserts}
disableInlineChips
/>
</>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
The selected options can also be removed from the listbox by enabling the
filterSelected prop. It is only recommended to enable this prop when the
disableInlineChips is enabled.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { AutocompleteChip } from "@react-md/core/autocomplete/AutocompleteChip";
import { Box } from "@react-md/core/box/Box";
import { type ReactElement, useState } from "react";
export default function FilterSelectedExample(): ReactElement {
const [value, setValue] = useState<readonly Dessert[]>([
desserts[1],
desserts[2],
]);
return (
<>
<Box
role="status"
aria-live="polite"
aria-label="Selected Desserts"
align="start"
fullWidth
disablePadding
>
{value.map((dessert) => (
<AutocompleteChip
key={dessert.name}
theme="outline"
onClick={() => {
setValue((prevValue) => prevValue.filter((d) => d !== dessert));
}}
>
{dessert.name}
</AutocompleteChip>
))}
</Box>
<Autocomplete
label="Dessert"
placeholder="Ice cream"
value={value}
setValue={setValue}
listboxLabel="Desserts"
options={desserts}
filterSelected
disableInlineChips
/>
</>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
The inline chips can be customized using the getChipProps which provides the
index and option being rendered. This can be used to disable chips, add
custom styles, change the theme, etc.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";
export default function CustomizingInlineChipsExample(): ReactElement {
const isDisabled = (option: Dessert): boolean =>
option === desserts[0] || option === desserts[1] || option === desserts[4];
return (
<>
<Autocomplete
label="Dessert"
placeholder="Ice cream"
defaultValue={[desserts[0], desserts[1], desserts[4]]}
listboxLabel="Desserts"
options={desserts}
getOptionProps={({ option }) => {
return {
disabled: isDisabled(option),
};
}}
getChipProps={({ option, index }) => {
return {
theme: "outline",
leftAddon: <FavoriteIcon />,
disabled: isDisabled(option),
children: `${option.name} ${index + 1}`,
};
}}
/>
</>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
The search query can be highlighted using the
HighlightText component and the getOptionProps
to provide highlighted children to each option.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { caseInsensitiveSearch } from "@react-md/core/searching/caseInsensitive";
import { HighlightText } from "@react-md/core/typography/HighlightText";
import { type ReactElement } from "react";
export default function HighlightsExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={desserts}
filter={(options) => caseInsensitiveSearch(options)}
getOptionProps={(options) => {
const { option, query } = options;
return {
children: <HighlightText query={query}>{option.name}</HighlightText>,
};
}}
/>
);
}
export interface Dessert {
name: string;
calories: number;
fat: number;
carbs: number;
protein: number;
sodium: number;
calcium: number;
iron: number;
type: "Ice cream" | "Pastry" | "Other";
}
export type DessertKey = keyof Dessert;
export const desserts: readonly Dessert[] = [
{
name: "Frozen yogurt",
type: "Ice cream",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
sodium: 87,
calcium: 14,
iron: 1,
},
{
name: "Ice cream sandwich",
type: "Ice cream",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: 8,
iron: 1,
},
{
name: "Eclair",
type: "Pastry",
calories: 262,
fat: 16,
carbs: 37,
protein: 6,
sodium: 337,
calcium: 6,
iron: 7,
},
{
name: "Cupcake",
type: "Pastry",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: 3,
iron: 8,
},
{
name: "Gingerbread",
type: "Pastry",
calories: 356,
fat: 16,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: 7,
iron: 16,
},
{
name: "Jelly bean",
type: "Other",
calories: 375,
fat: 0,
carbs: 94,
protein: 0,
sodium: 50,
calcium: 0,
iron: 0,
},
{
name: "Lollipop",
type: "Other",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: 0,
iron: 2,
},
{
name: "Honeycomb",
type: "Other",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: 0,
iron: 45,
},
{
name: "Donut",
type: "Pastry",
calories: 52,
fat: 25,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: 2,
iron: 22,
},
{
name: "KitKat",
type: "Other",
calories: 16,
fat: 6,
carbs: 65,
protein: 7,
sodium: 54,
calcium: 12,
iron: 6,
},
];
export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];
The clear button and dropdown button can be removed by enabling the
disableClearButton and disableDropdownButton props respectively. They do not
need to be used in tandem.
The keyboard shortcuts for clearing the input and showing/hiding the menu will still be enabled if these props are enabled.
"use client";
import { Autocomplete } from "@react-md/core/autocomplete/Autocomplete";
import { type ReactElement } from "react";
export default function DisablingTheClearAndDropdownButtonsExample(): ReactElement {
return (
<Autocomplete
label="Fruit"
placeholder="Apple"
listboxLabel="Fruits"
options={fruits}
disableClearButton
disableDropdownButton
/>
);
}
export const fruits = [
"Apple",
"Apricot",
"Banana",
"Blueberry",
"Cranberry",
"Kiwi",
"Mango",
"Orange",
"Peach",
"Plum",
"Strawberry",
];
This is a more complex example that will showcase most of the
customization options available in the Autocomplete component and other
child components.
This demo shows how the Github label picker could be created with react-md
with most of the same behavior. Here's a breakdown of how this was created:
Button + FixedDialog combination to display the
AutocompleteAutocomplete with most of the default UI disabled to
better match Github
disableClearButtondisableDropdownButtondisableInlineChipslistboxProps.disableElevationAutocomplete so that the listbox renders inline with the other
content and within the dialog
listboxProps.disablePortallistboxProps.disableFixedPositioningvisible prop and
providing a noop for the setVisible prop. Also enable the
disableCloseOnSelect prop to ensure items are still filtered after selecting
an new valueupdateQueryOnSelect to "as-is" so that selecting a new value doesn't
change the value in the AutocompletenextValue ref that is mutated with the onValueChange propdefaultValue to the labels state which ensures the latest
values are selected each time the dialog is openedonExited handler to the Dialog that sets the state with the
nextValue reffilter={(options) => caseInsensitiveSearch(options)}options prop for the Autocomplete so that the selected labels
appear firstonKeyDown handler for the Dialog, Listbox, and input that
closes the dialog when the Escape key is pressedBox with Chip componentsNone yet
The Autocomplete option is defined as:
type AutomaticTextExtraction = string | { label: string } | { name: string };
type AutocompleteOption = AutomaticTextExtraction | object;
Each option should extend this type and will automatically be inferred for almost all use cases.
// ✅ Inferred as `string[]`
<Autocomplete {...props} options={["One", "Two", "Three"]} />;
// ✅ Inferred as `string[]`
const options = ["One", "Two", "Three"];
<Autocomplete {...props} options={options} />;
// ✅ Inferred as `("One" | "Two" | "Three")[]`
const options = ["One", "Two", "Three"] as const;
<Autocomplete {...props} options={options} />;
// ✅ Inferred as `{ name: string, value: number }`
const options = [
{ name: "Hello", value: 1 },
{ name: "World", value: 2 },
];
<Autocomplete
{...props}
options={options}
/>;
// ✅ Inferred as `State`
interface State {
name: string;
abbreviation: string;
}
const options: readonly State[] = [
{ name: "Virginia", abbreviation: "VA" },
{ name: "Whyoming", abbreviation: "WY" },
];
<Autocomplete
{...props}
options={options}
/>;
The only time it appears to have an issue is when the list of options are
defined with as const:
const options = [
{ fullName: "Virginia", abbreviation: "VA" },
{ fullName: "Whyoming", abbreviation: "WY" },
] as const;
<Autocomplete
{...props}
options={options}
// ❌ Parameter 'state' implicity has an `any` type.
getOptionLabel={(state) => state.fullName}
/>;
The type issue can be resolved by one of the following:
const options = [
{ fullName: "Virginia", abbreviation: "VA" },
{ fullName: "Whyoming", abbreviation: "WY" },
] as const;
type State = (typeof options)[number];
// 1. Provide the type definition to the `getOptionLabel` parameter
<Autocomplete
{...props}
// ✅ `state` and `options` are set to the `State` type
options={options}
getOptionLabel={(state: State) => state.fullName}
/>;
// 2. Provide the type definition as the `Autocomplete` type parameter
<Autocomplete<State>
{...props}
// ✅ `state` and `options` are set to the `State` type
options={options}
getOptionLabel={(state) => state.fullName}
/>;
The Autocomplete component implements the
combobox
so the <input /> will remain focused while the options gain styles to show
they are focused.
aria-label/aria-labelledby by the
listboxLabel/listboxLabelledby props respectivelyaria-label/aria-labelledby based on the
listboxLabel/listboxLabelledby props respectivelyaria-label="Loading"The following keyboard movement has been implemented: