Multiselect in Material UI is an extended feature to the regular select or dropdown menu that enables users to select multiple values within the same input field.
MUI provides a couple of convenient options to get a feature-rich multiselect component included in our project.
In this tutorial, we’ll learn how to create a multiselect component using Select
and Autocomplete
with a customized menu list and removable tags/chips as values.
Here is what the final code will look like:
If this is your first time working with MUI, please follow the installation instructions from the official documentation to get started.
Using Select Component
Multiselect at a basic level is similar to the regular Select
component syntax except for adding multiple
prop to enable multiple selections.
import React, { useState } from "react";
import {
OutlinedInput,
InputLabel,
MenuItem,
Select,
FormControl
} from "@mui/material";
const names = [
"Humaira Sims",
"Santiago Solis",
"Dawid Floyd",
"Mateo Barlow",
"Samia Navarro",
"Kaden Fields",
"Genevieve Watkins",
"Mariah Hickman",
"Rocco Richardson",
"Harris Glenn"
];
export default function MultiSelect() {
const [selectedNames, setSelectedNames] = useState([]);
return (
<FormControl sx={{ m: 1, width: 500 }}>
<InputLabel>Multiple Select</InputLabel>
<Select
multiple
value={selectedNames}
onChange={(e) => setSelectedNames(e.target.value)}
input={<OutlinedInput label="Multiple Select" />}
>
{names.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
);
}
Customize Selected Values
To customize the selected values, we need to use the renderValue
prop and loop through the selected values to insert them into the component of our choice. We use Chip
in this example to make it look like tags.
import React, { useState } from "react";
import {
OutlinedInput,
InputLabel,
MenuItem,
Select,
FormControl,
Stack,
Chip
} from "@mui/material";
const names = [
"Humaira Sims",
"Santiago Solis",
"Dawid Floyd",
"Mateo Barlow",
"Samia Navarro",
"Kaden Fields",
"Genevieve Watkins",
"Mariah Hickman",
"Rocco Richardson",
"Harris Glenn"
];
export default function MultiSelect() {
const [selectedNames, setSelectedNames] = useState([]);
return (
<FormControl sx={{ m: 1, width: 500 }}>
<InputLabel>Multiple Select</InputLabel>
<Select
multiple
value={selectedNames}
onChange={(e) => setSelectedNames(e.target.value)}
input={<OutlinedInput label="Multiple Select" />}
renderValue={(selected) => (
<Stack gap={1} direction="row" flexWrap="wrap">
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Stack>
)}
>
{names.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
);
}
We also added a Stack
wrapper to align tags horizontally.
Remove Selected Values
To delete selected values, we need to define an onDelete
handler and add a deleteIcon
to display on the Chip
component.
import React, { useState } from "react";
import {
OutlinedInput,
InputLabel,
MenuItem,
Select,
FormControl,
Stack,
Chip
} from "@mui/material";
import CancelIcon from "@mui/icons-material/Cancel";
const names = [
"Humaira Sims",
"Santiago Solis",
"Dawid Floyd",
"Mateo Barlow",
"Samia Navarro",
"Kaden Fields",
"Genevieve Watkins",
"Mariah Hickman",
"Rocco Richardson",
"Harris Glenn"
];
export default function MultiSelect() {
const [selectedNames, setSelectedNames] = useState([]);
return (
<FormControl sx={{ m: 1, width: 500 }}>
<InputLabel>Multiple Select</InputLabel>
<Select
multiple
value={selectedNames}
onChange={(e) => setSelectedNames(e.target.value)}
input={<OutlinedInput label="Multiple Select" />}
renderValue={(selected) => (
<Stack gap={1} direction="row" flexWrap="wrap">
{selected.map((value) => (
<Chip
key={value}
label={value}
onDelete={() =>
setSelectedNames(
selectedNames.filter((item) => item !== value)
)
}
deleteIcon={
<CancelIcon
onMouseDown={(event) => event.stopPropagation()}
/>
}
/>
))}
</Stack>
)}
>
{names.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
);
}
Notice that we added an onMouseDown
handler to stop the Select
from opening the list as the user clicks the “Cancel” icon. It’s a small required hack because of some limitations on the Select
component.
Customize Menu Item
MenuItem
is the component used to display all available options, and currently, any selected value has a light background color. Below, we will enhance it by adding a checkmark icon to make selections more visible.
import React, { useState } from "react";
import {
OutlinedInput,
InputLabel,
MenuItem,
Select,
FormControl,
Stack,
Chip
} from "@mui/material";
import CancelIcon from "@mui/icons-material/Cancel";
import CheckIcon from "@mui/icons-material/Check";
const names = [
"Humaira Sims",
"Santiago Solis",
"Dawid Floyd",
"Mateo Barlow",
"Samia Navarro",
"Kaden Fields",
"Genevieve Watkins",
"Mariah Hickman",
"Rocco Richardson",
"Harris Glenn"
];
export default function MultiSelect() {
const [selectedNames, setSelectedNames] = useState([]);
return (
<FormControl sx={{ m: 1, width: 500 }}>
<InputLabel>Multiple Select</InputLabel>
<Select
multiple
value={selectedNames}
onChange={(e) => setSelectedNames(e.target.value)}
input={<OutlinedInput label="Multiple Select" />}
renderValue={(selected) => (
<Stack gap={1} direction="row" flexWrap="wrap">
{selected.map((value) => (
<Chip
key={value}
label={value}
onDelete={() =>
setSelectedNames(
selectedNames.filter((item) => item !== value)
)
}
deleteIcon={
<CancelIcon
onMouseDown={(event) => event.stopPropagation()}
/>
}
/>
))}
</Stack>
)}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
sx={{ justifyContent: "space-between" }}
>
{name}
{selectedNames.includes(name) ? <CheckIcon color="info" /> : null}
</MenuItem>
))}
</Select>
</FormControl>
);
}
Using Autocomplete Component
Autocomplete
is similar to the Select
component in terms of providing a list of options with a multiselect feature. However, it’s a regular text input enhanced by a panel of suggested options and can be an excellent alternative for more advanced features.
For example, the basic usage below will provide us with all the features we customized in the previous section and more!
import React from "react";
import { TextField, Autocomplete } from "@mui/material";
const names = [
"Humaira Sims",
"Santiago Solis",
"Dawid Floyd",
"Mateo Barlow",
"Samia Navarro",
"Kaden Fields",
"Genevieve Watkins",
"Mariah Hickman",
"Rocco Richardson",
"Harris Glenn"
];
export default function MultiSelect() {
return (
<Autocomplete
sx={{ m: 1, width: 500 }}
multiple
options={names}
getOptionLabel={(option) => option}
disableCloseOnSelect
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Multiple Autocomplete"
placeholder="Multiple Autocomplete"
/>
)}
/>
);
}
As we can see above, the code is shorter than the Select
component, and we get the following functionalities out of the box:
- Autocomplete on user input.
Chip
component for selected values by default.- Multiselect with adding and removing all values option.
Additionally, we are no longer required to add useState
to the manage list. Instead, we can use the onChange
prop to detect any value changes in the list.
For reference, this link has the official Autocomplete
component API from Material UI
The only thing we are missing is adding a check mark on selected values. We can use similar code from the previous section using the MenuItem
component, but we no longer need to loop through the list. Instead, Autocomplete
provides a renderOption
prop fired on every item.
import React from "react";
import { TextField, Autocomplete, MenuItem } from "@mui/material";
import CheckIcon from "@mui/icons-material/Check";
const names = [
"Humaira Sims",
"Santiago Solis",
"Dawid Floyd",
"Mateo Barlow",
"Samia Navarro",
"Kaden Fields",
"Genevieve Watkins",
"Mariah Hickman",
"Rocco Richardson",
"Harris Glenn"
];
export default function MultiSelect() {
return (
<Autocomplete
sx={{ m: 1, width: 500 }}
multiple
options={names}
getOptionLabel={(option) => option}
disableCloseOnSelect
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Multiple Autocomplete"
placeholder="Multiple Autocomplete"
/>
)}
renderOption={(props, option, { selected }) => (
<MenuItem
{...props}
key={option}
value={option}
sx={{ justifyContent: "space-between" }}
>
{option}
{selected ? <CheckIcon color="info" /> : null}
</MenuItem>
)}
/>
);
}
Summary
Both components are great for implementing a multiselect feature. However, the Select
component is sufficient if we are looking for a simple solution without adding, removing, and auto-completing. Otherwise, Autocomplete
is recommended for more advanced features.
Bye for now 👋
If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!