How to Style the Material-UI Autocomplete Component

The Material-UI Autocomplete component and its Popper component can be styled by targeting built-in MUI styling APIs.

The Popper is also called a dropdown or listbox. The difference is that Popper is the positioning element while Listbox is the visible list element. That’s why MUI Autocomplete has a prop for customizing the Popper (PopperComponent) and a Prop for customizing the Listbox (ListboxComponent).

In this demo I’ll show the selectors required for styling the Autocomplete component and Popper. An important characteristic of the Autocomplete component is that it has a Popper that is not a DOM child of Autocomplete. This means we need to create a custom Popper in order to properly target it with a class selector (I’ll show this in the code later in the article).

Here’s the final product after targeting the label, the selected text (with a renderInput component), the Popper, and adding hover. Normally you wouldn’t want this wild color scheme, but I’ve set the colors so that each piece stands out for learning purposes.

Material-UI Autocomplete with hover, color in the renderInput and Popper, borders and more
Autocomplete with hover, color in the renderInput and Popper, borders and more

If you need to add custom components or modify the layout of the label and option text, read about Autocomplete’s getOptionLabel, renderInput, and renderOption props here.

A link to the Code Sandbox with full React code is in the Resources section.

MUI Autocomplete Popper (Listbox) DOM

I attempted to style the border and background color of the Autocomplete component Popper <li> elements using the common technique below. As info, many people call the Autocomplete dropdown a Popper but it has class .MuiAutocomplete-listbox at its root element, so it might also be called a Listbox.

Use the renderOption prop to style individual Popper items.

const useStyles = makeStyles((theme) =>
  createStyles({
    root: {
      "& .MuiAutocomplete-listbox": {
        "& li:nth-child(even)": { backgroundColor: "#CCC" },
        "& li:nth-child(odd)": { backgroundColor: "#FFF" }
      }
    }
  })
);

However, the popper in the DOM is not a child of the Autocomplete parent element.

MUI Autocomplete Popper DOM
MUI Autocomplete Popper DOM

This meant there were two options for styling a custom Popper:

  • Pass MUI v5 sx styling to ListboxProps
  • Create a custom Popper and pass classes to it in the v4 style

I will show both techniques below.

MUI v5 Autocomplete ListboxProps

ListboxProps allows props to be passed directly to the Popper component. In this example I passed nth-child even and odd selectors directly to the root element of the Popper (aka Listbox).

ListboxProps: {{
  sx: {
    "& li:nth-child(even)": { backgroundColor: "#CCC" },
    "& li:nth-child(odd)": { backgroundColor: "#FFF" }
  }
}}

This syntax is an improvement over v4 syntax for two reasons:

  • It allows targeting of Listbox directly, so we need less selector code
  • It doesn’t require makeStyles hook boilerplate syntax

MUI v4 Autocomplete Custom Popper and Classes

Here’s the custom Popper I created, with the v4 “makeStyles syntax” root class (seen above) passed to it.

const CustomPopper = function (props) {
const classes = useStyles();
return <Popper {...props} className={classes.root} placement="bottom" />;
};

Now the root class is properly applied and targeting the Listbox.

Let’s dive deeper into the syntax for targeting the Listbox: root: {“& .MuiAutocomplete-listbox”: { } } . This is the class generated on the Listbox DOM <ul> . Using this, I was able to add border styling and target children of the Listbox. The Listbox contains <li> elements; within these elements are whatever elements specified in the renderOption prop (in my case, <h4>). To style every other <li> within Listbox, I targeted “& li:nth-child” .

If you want to style the Autocomplete’s text color, text decoration, font, or control a variety of other styling options, the Popper component is really what you need to target.

MUI Autocomplete Min and Max Height

If you want to set the height of the box containing the Autocomplete options, make sure to set minHeight instead of setting height. The minHeight is set by default and needs to be overridden.

If you want to control the height and ensure there is a scroll bar, set maxHeight.

The best way to set Autocomplete height in MUI v5 is with ListboxProps:

ListboxProps: {{
  sx: {
    minHeight: 200,
    maxHeight: 400
  }
}}

MUI Autocomplete Width

There are two Autocomplete subcomponents that can have custom width:

  • Input field width
  • Popper component width

MUI Autocomplete Input Width

The Input field can be given a width if you pass a custom component to renderInput. Add a width to the custom components sx prop:

renderInput={(params) => {
  return (
    <TextField
      {...params}
      variant="outlined"
      label="Name: Manufacturer"
      sx={{width: 600}}
    />
  );
}}

This only works for adding fixed width. If you need a dynamic or full width, see below.

MUI Autocomplete Full Width

If you want full width for the Autocomplete Input, you need to set width to 100%. However, it won’t work to set width on the renderInput component because this component is wrapped in a root div. We need to target that root div.

Here’s the code for a full width Autocomplete:

<Autocomplete
  sx={{width: "100%"}}
/>

Anything that can be done at the renderInput component sx can also be done with the root Autocomplete sx. However, you might need additional selectors in the sx.

MUI Autocomplete Popper Width

Setting Popper width requires a custom Paper component. We can make use of the well-named PaperComponent prop:

const CustomPaper = (props) => {
  return <Paper {...props} sx={{ width: 300 }} />;
};

//JSX
<Autocomplete
  PaperComponent={CustomPaper}
/>

Notice that I added width in the custom Paper sx prop. This sets a fixed width on the root div of the Popper.

It’s necessary to make a new component because the Popper is not a child of the Autocomplete in the DOM, it’s actually completely separate, so we can’t target it with nested selectors.

If you want a dynamic Popper width based on Autocomplete’s width (for example, 50% width), you need to use a ref and get the Autocomplete div’s width. I did something similar for the Select component here.

MUI Autocomplete Hover Action

The Popper component is composed of a div which wraps a ul element and all children li elements. It’s separate in the DOM from the Autocomplete elements which makes it tricky to target.

For MUI v4 I created a custom Popper component to get around this difficulty. I was then able to apply a class to the CustomPopper, and inside the class I targeted the MUI class .MuiAutocomplete-listbox that gets applied to Autocomplete Poppers.

Inside of this class, I added the & :hover psuedo-class selector.

//styles
root: {
  "& .MuiAutocomplete-listbox": {
    "& :hover": {
      color: "brown"
    }
  }
}

//Custom Popper
const CustomPopper = function (props) {
  const classes = useStyles();
  return <Popper {...props} className={classes.root} placement="bottom" />;
};

//return JSX
<Autocomplete
  //...skipping a few props
  PopperComponent={CustomPopper}
/>

I go into more detail about the Popper component here, including a DOM screenshot.

For MUI v5, add hover styling with ListboxProps:

ListboxProps: {{
  sx: {
    "& :hover": {
      color: "brown"
    }
  }
}}

MUI Autocomplete Font Size, Border, Border Radius, and Text Color

We have two areas of text that we may want to style: the renderInput and label, and the Popper.

In the previous section, I discussed why I created a custom Popper. Now that we can style the Popper, adding font size, border, and text color is simple.

//style
root: {
  "& .MuiAutocomplete-listbox": {
    border: "2px solid grey",
    minHeight: 400,
    color: "green",
    fontSize: 18,
    //hover discussed above
    "& li": {
      //list item specific styling
      border: "2px solid green",
      borderRadius: 4
    }
  }
}

The .MuiAutocomplete-listbox selector targets the ul in the DOM. We can apply li specific styling with the & li selector. I added a border with this method. It could also be used to apply nth-child styling to the list items.

The renderInput is used to specify a component that contains the selected item’s text. In this example I used a TextField. This impacts the selectors that we need to use for styling the label and Input text.

//styling
textfield: {
  "& .MuiInputBase-input.MuiAutocomplete-input": {
    color: "blue",
    fontSize: 18
  },
  "& #custom-autocomplete-label": {
    //or could be targeted through a class
    color: "brown"
  }

}

//return JSX
<Autocomplete
  id="custom-autocomplete"
  renderInput={(params) => {
    return (
      <TextField
        //A few other params...
        className={classes.textfield}
      />
      );
  }}
/>

I created a class called textfield and inside it I targeted .MuiInputBase-input.MuiAutocomplete-input. The first part of this selector is specific to TextFields (they are composed of Input components) and the second is specific to components passed as the renderInput for Autocomplete. This allowed me to style the color and font size of the selected text.

I then noticed in the DOM that the label had an id that was derived from the Autocomplete component’s id: custom-autocomplete-label. I used this to select the label.

MUI Autocomplete Label Style
MUI Autocomplete Label Style

Resources

An alternative method of styling the Popper would be to attempt an override in the theme overrides object. Using this method, there would be no need to create a custom Popper.

Here’s how to Use and Remove the MUI Autocomplete Clear Button

Material-UI v4 Code Sandbox Link

MUI v5 Code Sandbox Link

Share this post:

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.