How to Create a Material-UI Select Component with Styled Checkboxes

The Material-UI Select component creates a slick menu for users to choose from a list of options. The standard Select uses a dropdown (created from a Popover component) and the native Select uses the html option element.

Both of these variations of the component can have checkboxes enabled. The standard Select can easily have checkboxes enabled, and I will discuss styling the Material-UI Select background color, border, and border radius.

Creating the Select native with checkboxes is quite challenging. In fact, I had to do it using CSS instead of using mui Checkbox.

Here’s what we will be creating. I like to use the ugliest colors I can think of ;). It’s really about exploring the MUI global classes API.

Left: standard multi-select with checkboxes. Right: native select with checkboxes.

Code Sandbox with full React code is in the Resources sections.

If you want a video based course for learning Material-UI, Implement High Fidelity Designs with Material-UI and ReactJS on Udemy is an incredible resource. It has 1400+ reviews, averages 4.6 stars, and has a 30 Day Money Back Guarantee.

The course was recently updated and covers a huge amount of MUI components and designs. At 40+ hours of content and 243 lectures across 16 sections, I believe it lays an excellent foundation for using Material-UI.

Udemy runs lots of sales, check the price below.

This image has an empty alt attribute; its file name is image-7.png

Material-UI Select With Checkbox Code

Targeting the proper global classes can be pretty challenging. The styling is all original code. The JSX is is still the same as this example in the docs that I forked my Code Sandbox from.

//styling
selectOptions: {
  "& .MuiListItem-root": {
    borderTop: "1px solid rgb(3,15,252)",
    borderRadius: 8
  },
  "& .MuiListItem-root.Mui-selected, .MuiListItem-root.Mui-selected:hover": {
    backgroundColor: "transparent"
  },
  backgroundColor: "rgb(3,252,244)",
  "& .MuiCheckbox-root": {
    color: "green"
  },
  "& .MuiCheckbox-colorSecondary": {
    "&.Mui-checked": {
      color: "orange"
    }
  }
}

const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 5 + ITEM_PADDING_TOP,
      width: 240
    }
  }
};

//inside the component
MenuProps.PaperProps.className = classes.selectOptions;

//in the return JSX
<Select
  multiple
  value={personName}
  onChange={handleChange}
  input={<Input />}
  renderValue={(selected) => selected.join(", ")}
    MenuProps={MenuProps}
  >
    {names.map((name) => (
      <MenuItem key={name} value={name}>
        <Checkbox checked={personName.indexOf(name) > -1} />
        <ListItemText primary={name} />
      </MenuItem>
    ))}
</Select>

Something important to notice is that I had to apply the selectOptions class to the Menu via renderProps. This is because the Select creates a Popover when the menu is opened, and the Popover is not a child of the Select component in the DOM. The JSS syntax above only works if the class is applied to the Popover or a DOM parent.

Select height and width are applied via MenuProps also. They are applied to the Paper component inside the Popover, and the Popover (the menu) takes its height and width from its child.

The Checkbox is actually a MUI component. This means we can take advantage of the MUI global class API.

This image has an empty alt attribute; its file name is image-10.png

Take a look at the selector for the color in the DOM before I changed the color. We can see that in our selectOptions class, we need to target a child component that has both the .MuiCheckbox-colorSecondary and the .Mui-checked class applied. Explore the JSS syntax I used above to accomplish this.

The Select border and border radius were simpler. They only required targeting .MuiListItem-root in the Popover. The Select menu background color required targeting the .Mui-selected class, which is automatically applied by MUI when the checkbox is selected.

Take a look at the DOM for the Popover generated by the Select component:

This image has an empty alt attribute; its file name is image-11.png

Material-UI Select Native With Checkbox Code

“Native” Select is created by setting the native prop to true. This creates a component that theoretically has a better user experience on mobile devices.

Once again, the JSX is is still the same as this example in the docs that I forked my Code Sandbox from. The styling for native Select requires targeting DOM elements and pseudo elements instead of MUI global classses.

//styling
nativeSelect: {
  "& .MuiSelect-root": {
    backgroundColor: "rgba(0, 0, 0, 0.08)",
    "& option": {
      padding: "10px 4px",
      //backgroundColor: "rgba(0, 0, 0, 0.08) !important", This had strange DOM effects
      "&::before": {
        content: '"\\2610"',
        width: "1.4em",
        textAlign: "center",
        display: "inline-block",
        fontSize: 24
      }
    },
    "& option:checked": {
      "&::before": {
        content: '"\\2611"',
        fontSize: 24
      }
    }
  }
}

//in the return JSX
<Select
  className={classes.nativeSelect}
  multiple
  native
  value={personName}
  onChange={handleChangeMultiple}
  inputProps={{
    id: "select-multiple-native"
  }}
>
  {names.map((name) => (
    <option key={name} value={name}>
      {name}
    </option>
  ))}
</Select>

I was not able to inject checkbox (input) elements or components into the native Select. However, this SO answer was very helpful in creating the checkboxes through a pure CSS approach.

Also, styling the option elements was challenging. While background color can be set on the Select component (I commented it out in the code above), this created some strange UI looks. The option element cannot have background color directly applied when a focus event happens, which means we are stuck with a blue highlight. However, you may be able to set focus color by setting a background image on focus. Let me know in the comments if you have success with this.

Take a look at the DOM screenshot below of the CSS selectors. The -internal-list-box pseudo element/class was generated by the browser and I was unable to target it.

This image has an empty alt attribute; its file name is image-9.png

My recommendation is simply to push back on a requirement for checkboxes in the native select. It isn’t really designed for such a use case.

Resources

Think you’re a JavaScript expert? Test yourself on these 50 difficult JavaScript questions.

Code Sandbox:

Docs

Share this post:

1 thought on “How to Create a Material-UI Select Component with Styled Checkboxes”

Leave a Comment

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