How to Customize Material-UI Drawer Width, Color, Position Under AppBar, and More

The Material-UI Drawer component is an essential layout component for everything from navigation links to informational sidebars.

In this React Material-UI Drawer example, we will make a mobile responsive Drawer component that is always visible on screen sizes > 375px, and on smaller screens it opens and closes with the click of a menu icon.

Additionally, we will discuss the following features for Material-UI Drawer:

  • background color
  • width
  • shadow/elevation
  • fitting Drawer under the Appbar
  • removing Backdrop when using the Drawer
  • using Paper as an internal component

Code Sandbox is in the Resources section.

Creating a Mobile Responsive Drawer

Drawer is hidden by default at < 375px

There are three primary considerations with the design of the mobile responsive drawer in this demo:

  • Showing/hiding the Drawer component
  • Showing/hiding the Menu Icon
  • Full/reduced width of the main content section

We’re going to get deep into the JSX and hooks:

export default function ClippedDrawer() {
  const classes = useStyles();
  const greaterThan375 = useMediaQuery("(min-width:375px)");
  const [open, setOpen] = React.useState(greaterThan375);

  useEffect(() => {
    setOpen(greaterThan375);
  }, [greaterThan375]);

  const handleMenuClick = () => {
    setOpen(!open);
  };

  return (
    <div className={classes.root}>
      <AppBar position="fixed" className={classes.appBar}>
        <Toolbar>
          <IconButton //hide on desktop
            color="inherit"
            onClick={handleMenuClick}
            edge="start"
            className={clsx(classes.menuButton, greaterThan375 && classes.hide)}
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            Responsive Drawer
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer
        className={classes.drawer}
        variant="persistent"
        //elevation={3} only works with variant="temporary"
        open={open}
        transitionDuration={{
          enter: transitionDuration,
          exit: transitionDuration
        }}
        classes={{
          paper: classes.drawerPaper
        }}
        PaperProps={{ elevation: 9 }}
      >
        <Toolbar />
        <div className={classes.drawerContainer}>
          <List>
            {["Home", "Page 1", "Page 2", "Page 3"].map((text, index) => (
              <ListItem button key={text}>
                <ListItemIcon>
                  <AppsIcon />
                </ListItemIcon>
                <ListItemText primary={text} />
              </ListItem>
            ))}
          </List>
        </div>
      </Drawer>
      <main
        className={clsx(classes.content, {
          [classes.contentShift]: open
        })}
      >
        <Toolbar />
        <Typography>
          Resize the screen above/below 375px to see responsiveness
        </Typography>
      </main>
    </div>
  );
}

The most important code for the Drawer itself to be responsive is the Boolean greaterThan375 created with useMediaQuery. This hook updates the Boolean dynamically. greaterThan375 can then be used in the useEffect hook to open or close the Drawer by updating an open Boolean.

The menu icon gets an extra class applied when greaterThan375 is true. The class simply flips the icon to display: "none“. As info, clsx simply allows conditional logic to be passed to the className prop.

The main content requires a bit more fancy css. My code is actually very similar to the example in the MUI docs, which I forked for my Code Sandbox.

To give the effect of expanding or contracting content, margin is transitioned in and out when the open Boolean is toggled. Instead of using a constant, another option is to use the theme.transitions.duration object, which has some values for transition durations. However, I also used the transitionDuration constant to control the Drawer component transition, so I used it for main content margin as well.

const transitionDuration = 1000; //can also use theme.transitions.duration

content: {
  flexGrow: 1,
  padding: theme.spacing(3),
  transition: theme.transitions.create("margin", {
    easing: theme.transitions.easing.sharp,
    duration: transitionDuration
  }),
  marginLeft: -drawerWidth
},
contentShift: {
  transition: theme.transitions.create("margin", {
    easing: theme.transitions.easing.easeOut,
    duration: transitionDuration
  }),
  marginLeft: 0
}

Styling Background Color, Width, and Shadow

Custom styling MUI components can be challenging, but the Drawer component was surprisingly easy.

(What’s not so easy is how to position the Drawer inside a container)

const drawerWidth = 240;

drawer: {
  width: drawerWidth,
  flexShrink: 0
  //backgroundColor: "rgba(0,0,0,0.6)" Don't target here
},
drawerPaper: {
  width: drawerWidth,
  backgroundColor: "rgba(120,120,120,0.2)" //target here
}

Simply place a class on the Drawer and set the width. However, Material-UI Drawer backgroundColor needs to be set on the inner component, in this case the Paper component. If you place backgroundColor on the Drawer itself, the background color will be mostly covered by the inner component.

If you are using the “persistent” or “permanent” variants, you will need to pass any shadow (also known as elevation) to the inner Paper component using this prop: PaperProps={{ elevation: 9 }}. The “temporary” variant can actually directly receive elevation directly: elevation={9}. Elevation ranges from 1 to 16. The higher the number, the more box-shadow is applied (more grey and ‘elevated’ look).

Nesting Under the Appbar and Removing Backdrop

The Appbar is a common component to have across the top of the screen. It comes with some shadow underneath it and should appear to be ‘over’ the Drawer. We need two things to accomplish this:

  • A <Toolbar> that acts as a gutter above the content of the Drawer
  • A higher z-index on the Appbar than the Drawer

These are both standard in the examples in the Material-UI docs. However, it is useful to explain them.

First, the Appbar cleverly can be guaranteed to be above the Drawer with a simple bit of code:

appBar: {
  zIndex: theme.zIndex.drawer + 1
}

Second, the <Toolbar> works as a gutter because of the css applied in the DOM. See below:

If you are using the ‘temporary’ Drawer variant and you want to remove the Backdrop, it’s possible with CSS. Add the following to our existing class:

drawer: {
  "& .MuiBackdrop-root": {
    display: "none"
  }
}

An alternative solution may be to use the ModalProps, but I prefer the above approach.

Resources

Are you looking for training in Material-UI? Check out my review of the 3 best Material-UI courses in Udemy, including my top pick with 40+ hours of training content.

Link to Code Sandbox with full React code.

See this article for an example of React-Router Links inside the Drawer component.

Docs

Share this post:

Leave a Comment

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