MAL's Logo

MUI - Autocomplete

💡 A reusable component made from Material UI’s Autocomplete where a user can insert tags that are suggested by the dropdown or create their own.

Introduction

This intricate solution of a multi-faceted Autocomplete component was a bigger obstacle than I first expected, therefore I decided to document it and share it online for further documentation.

Code

import React from "react";
import { Autocomplete, TextField, Box, Stack, Chip } from "@mui/material";
import TransitEnterexitIcon from "@mui/icons-material/TransitEnterexit";
import CloseIcon from "@mui/icons-material/Close";

const apiTags = [
  { title: "The Shawshank Redemption", year: 1994, group: "Scenes" },
  { title: "The Godfather", year: 1972, group: "Scenes" },
  { title: "The Godfather: Part II", year: 1974, group: "Exlusives" },
  { title: "The Dark Knight", year: 2008, group: "Exlusives" }
];

const AutoCompleteField = () => {
  const [tags, setTags] = React.useState([]);

  const handleChangeAutoComplete = (event, value) => {
    const newTag = (() => {
      const tempTag = value.at(-1);
      if (typeof tempTag === "string") {
        return tempTag.trim();
      }
      return undefined;
    })();

    const oldTags = value.slice(0, newTag === undefined ? undefined : -1);
    if (newTag === undefined) {
      return setTags(oldTags);
    }

    const obj = {
      title: newTag,
      year: 1990,
      group: "Recently added"
    };

    return setTags([...oldTags, obj]);
  };

  return (
    <Autocomplete
      disablePortal
      renderGroup={(params) => (
        <Box key={params.group} sx={{ padding: "10px 20px" }}>
          <p>{params.group}</p>
          <Stack direction="row" flexWrap="wrap" gap="8px">
            {React.Children.map(params.children, (element, index) => (
              <Box key={index} sx={{ border: "1px solid grey" }}>
                {element}
              </Box>
            ))}
          </Stack>
        </Box>
      )}
      multiple
      fullWidth
      options={apiTags}
      groupBy={(tag) => tag.group}
      value={tags}
      freeSolo
      onChange={handleChangeAutoComplete}
      getOptionLabel={(option) => {
        if (typeof option === "string") {
          return option;
        }
        return option.title;
      }}
      renderInput={(params) => (
        <TextField
          {...params}
          name="tags"
          placeholder="Start adding tags here"
          size="small"
          margin="none"
          variant="outlined"
          fullWidth
          required
          InputProps={{
            ...params.InputProps,
            endAdornment: <TransitEnterexitIcon />,
            onKeyDown: (e) => {
              if (e.key === "Enter") {
                e.preventDefault();
              }
            },
            style: {
              color: "grey",
              fontWeight: "500",
              fontSize: "16px"
            }
          }}
        />
      )}
      renderTags={(value, getTagProps) =>
        value?.map((option, index) => (
          <Chip
            key={index}
            deleteIcon={
              <CloseIcon
                sx={{
                  fill: "blue",
                  width: "16px"
                }}
              />
            }
            label={option.title}
            {...getTagProps({ index })}
          />
        ))
      }
    />
  );
};

export default AutoCompleteField;

Breakdown

Most of the explanation of the parameters passed down to the component can be found in MUI’s documentation. But, let’s see closer how to set up this component to accept pre-defined tags (given to the user in a dropdown) and tags created by the user on the fly.

  • renderGroup - if, like me, you’re not satisfied with the default styling of the dropdown list, then passing this prop allows you complete freedom of choice
  • multiple - allows the user to insert more than one tag
  • freesolo - required to allow pre-defined tags and arbitrary tags
  • groupBy - used to group tags in the dropdown with a defined title
  • onChange - the underlying logic of where the different inputs are handled
  • InputProps - used to add an icon to the right of the field with endAdornment and to add the logic of what happens when the user presses enter on the keyboard
  • renderTags - to overwrite the default tags styling
  • Resources

  • https://mui.com/material-ui/react-autocomplete/
  • MAL's Logo© 2021 – 2026 MAL. All rights reserved.