How to Position a Material-UI Drawer Inside a Div

The Material-UI Drawer React component is a useful container component with a variety of positioning options. It can be anchored to the top, bottom, or sides of the viewport and opened or closed with ease.

However, the Material-UI Drawer is not an easy component to position inside a div or container. I was able to accomplish this through a few key settings:

  • Setting variant="persistent"
  • Styling the top element in the Drawer component with position="relative"
  • Styling an interior element with position="absolute"
  • Using a ref to get the parent container height and passing height as a prop to useStyles

***UPDATE: I packaged this up and it’s on npm! Check it out here.

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

Let’s dive into the code.

Drawer Props

I mentioned above that you need variant=""persistent". Here’s why: if variant is set to "temporary" then the Drawer component is not created as a child of any wrapping JSX elements.

Take a look at this dev tools snapshot to see what I mean:

Drawer isn’t inside it’s container

Notice the div with id="root"? Notice how the div with class="MuiDrawer-root" is a sibling of root instead of a child of it? That’s despite Drawer being a child of root and the div beneath it in the JSX. It’s lifted out by Material-UI when the DOM is created.

//root div added by Code Sandbox
<div ref={containerRef} style={{ position: "relative" }}>
  <AppBar position="static">
    <Toolbar style={{ display: "flex" }}>
      <IconButton
        style={{ marginLeft: "auto" }}
        color="inherit"
        aria-label="filterButton"
        onClick={handleFilterIconClick}
      >
        <FilterListIcon />
      </IconButton>
    </Toolbar>
  </AppBar>
  <Drawer
    open={open}
    className={classes.drawer}
    variant="temporary" //change to "persistent"
    anchor="right"

  >
  //more JSX...
</div>

Once variant is set to “persistent”, you will see the the DOM tree created as desired with the elements that compose the Drawer properly nested inside the expected parent elements.

CSS Positioning

Next you will need to properly set the position on the Drawer. This is a two-part process: first set the parent with position="relative", then set the proper child element with position="absolute".

const useStyles = makeStyles({

  drawer: {
    position: "relative",
    marginLeft: "auto",
    width: 200,
    "& .MuiBackdrop-root": {
      display: "none"
    },
    "& .MuiDrawer-paper": {
      width: 200,
      position: "absolute",
      height: (props: { height: number }) => props.height,
      transition: "none !important"
    }
  }
});

Take a look at the drawer class and the two selectors inside of it. Remember that an absolutely positioned element is positioned absolutely relative to the nearest parent that has position: "relative" applied.

That’s why I’ve applied position: "relative" at the top level, and position: "absolute" via a selector that targets the applicable child element, which is a div with class MuiDrawer-paper. This class is auto-applied by MUI.

The highlighted element is the child. Notice that position is absolute and also notice the "MuiDrawer-paper" class it has. The element immediately above is the parent that gets drawer class applied and is position: "relative".

Another important styling is marginLeft: "auto". This is what is anchoring the Drawer to the right. The anchor prop on the Drawer component is no longer sufficient due to our adjustments to the component position.

If you want to learn more about styling the Drawer, read here. If you want to learn about styling the AppBar, read here.

Also, notice the other selector I used, & .MuiBackdrop-root. I use this to set the Backdrop to display: none. The Backdrop is generated by default but often not wanted.

Ref Height and useStyles

If you want to position Drawer inside a div or container, then you need a reference to the container’s height. In this Drawer example, that means we need the height of the outermost div.

const containerRef = useRef();

useEffect(() => {
  if (open) {
    setHeight(containerRef.current.clientHeight - 64);
  } else {
    setHeight(0);
  }
}, [open]);

//first line in return statement
<div ref={containerRef} style={{ position: "relative" }}>

Then, pass the ref’s height to your classes and make use of it like you saw in the previous section:

const classes = useStyles({ height: height });

//inside drawer class
height: (props: { height: number }) => props.height

It is pretty handy to be able to pass props to your styles. This gives you access to dynamic values generated by your components or hooks.

Packaging for NPM

It was a good experience to package a component for public use. I’d packaged components at work for internal teams but never put a component on NPM. Below are my goals with the package and what it was like packaging for the first time.

I wanted to abstract the AppBar, Drawer, and containing div. I also wanted a user to simply pass a ‘left’ or ‘right’ prop for anchoring inside the div.

Furthermore, I wanted users to be able to pass components to the AppBar and Drawer as usual. Really, I wanted all props for AppBar and Drawer to be avaiable. Below are the props for the packaged component, DrawerInsideDiv:

  • appBarComponent: Use this to pass a component to the AppBar
  • appBarIcon: Custom AppBar icon
  • anchor: ‘left’ or ‘right’ to anchor to a side
  • drawerComponent: The child component to be injected into the Drawer
  • appBarProps: All the usual AppBar props
  • drawerProps: All the usual Drawer props

It was surprisingly difficult to find instructions for preparing and pushing a basic package. Here’s a quick checklist of what is necessary:

  • Proper settings in package.json
  • Proper exports/imports in index.js
  • Proper path in package.json to where built code will reside (for example, your build’s dist folder)
  • And of course, good code inside whatever component(s) you are actually packaging up

Here’s the link to the npm package.

Resources

The demo in the Sandbox below uses the Material-UI Table with Search.

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.

Test your JavaScript knowledge with these 50 difficult JavaScript questions.

MUI Drawer Docs

Share this post:

Leave a Comment

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