Slide-Out Mobile Menu Using React Hooks

Slide-Out Mobile Menu Using React Hooks

If you're a front-end developer, I can almost guarantee that you've worked with a mobile menu at some point in your career. If you have, you know first-hand that, sometimes, crafting mobile menus can be a little difficult.

Just this month, I was working on a website of mine, and I came across one of these issues. I wanted to create a mobile menu that would slide out from under the header.

As I found out, it wasn't as simple as setting the z-index of the menu to be less than that of the header. And trust me, I set the z-index to 999999 just to see what would happen (spoiler: nothing).

I ended up coming up with the solution below, which I am now sharing with you!

The Structure

For this tutorial, I'm using React with styled-components.

If you don't already have a React project set up, go ahead and do that.

If you don't already have one, create a header.js file.

To start, we're going to create a functional component and structure the header and mobile menu.

header.js

import  React, { useState } from  'react';

export const  Header = () => {
  return (
    <Wrapper>
      <HeaderWrapper id='header'>
        <Container>
          <Title>Menu Demo</Title>
          <MenuToggle>
            <RotateContainer>
              <span  />
              <span  />
              <span  />
            </RotateContainer>
          </MenuToggle>
        </Container>
      </HeaderWrapper>
      <MenuWrapper>
        <Menu>
          <MenuItem href='/'>Home</MenuItem>
          <MenuItem href='/'>About</MenuItem>
          <MenuItem href='/'>Contact</MenuItem>
        </Menu>
      </MenuWrapper>
    </Wrapper>
  );
};

Since we're using styled-components, each component is named accordingly.

Oh, and remember when I said I spent forever messing with the z-index of the menu, and it didn't work?

That's because the mobile menu must be a sibling of the header, not a child.

It doesn't work if it a child element.

Anyways. After we have the basic structure, we need to style each item.

Styling the menu

Styling the Header Wrapper

The header wrapper is optional. If you already have a header on your website (very important, you probably should), then you can keep your current styling.

If not, add a new HeaderWrapper styled-component to your file:

const  HeaderWrapper = styled.header`
  padding: 18px 0;
  color: white;
  position: fixed;
  background: tomato;
  left: 0;
  top: 0;
  right: 0;
  bottom: auto;
  z-index: 999;
`;

There's one specific line of CSS that is very important: z-index: 999;

To display the header over the top of the menu, you need to make sure the header component has a z-index higher than the mobile menu.

Styling the Menu Toggle

I took a little detour on the mobile menu toggle and added some pleasing transitions, using <span> tags.

At the bottom of your file, add two new styled-components: one for the toggle, and one to handle the rotation of the toggle. This is optional, so you can style it to your hearts content.

const  MenuToggle = styled.div`
  z-index: 9999;
  width: 30px;
  height: 30px;
  transform: rotate(0deg);
  transition: all 0.25s ease-in;
  cursor: pointer;
  margin-left: auto;
  span {
    display: block;
    position: absolute;
    height: 4px;
    width: 100%;
    background: white;
    border-radius: 9px;
    opacity: 1;
    left: 0;
    transform: rotate(0deg);
    transition: ${(props) =>
    props.open ? 'all 0.25s ease-in' : 'all 0.25s ease-out'};
  } 
  span:nth-child(1) {
    top: ${(props) => (props.open ? 'calc(50% - 2px)' : '10%')};
    transform-origin: left center;
  }
  span:nth-child(2) {
    top: ${(props) => (props.open ? 0 : 'calc(50% - 2px)')};
    left: ${(props) => (props.open ? 'calc(50% - 2px)' : null)};
    width: ${(props) => (props.open ? '4px' : null)};
    height: ${(props) => (props.open ? '100%' : null)};
    transform-origin: left center;
  }
  span:nth-child(3) {
    top: calc(90% - 4px);
    transform-origin: left center;
    width: ${(props) => (props.open ? 0 : null)};
    opacity: ${(props) => (props.open ? 0 : 1)};
  }
`;

const  RotateContainer = styled.div`
  height: 100%;
  width: 100%;
  transition: ${(props) => props.open ? 'all 0.25s ease-in-out' : 'all 0.25s ease-in-out'};
  transform: ${(props) => (props.open ? 'rotate(-45deg)' : 'none')};
`;

Styling the Menu

The menu styling also depends entirely on your preference, but, once again, there are some important lines of CSS that you need. You can style it however you want.

If you have your own styling, simply add in these 5 lines (the very important ones):

position: fixed;
overflow: hidden;
top: ${(props) => (props.open ? '0' : '-100%')};
left: 0;
z-index: 0;

Adding the functionality

Great! So far, we have nothing useful. We've got a menu that doesn't close, which is probably not the best thing for your website. Luckily, we're not done yet.

To open and close the menu, we need to set an open state that tells each component whether or not the menu is open. We'll be using React Hooks for this.

Inside your functional component, add the state, as well as a toggle function that will set the menu to open or closed:

const Header = () => {
  const [open, setOpen] = useState(false);

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

  ...
}

Now that we have our open state, we need to call the toggleMenu() function when we click on the menu toggle.

Update your <MenuToggle> component to look like this:

<MenuToggle onClick={toggleFunction} open={open}>
  <RotateContainer open={open}>
    ...
  </RotateContainer>
</MenuToggle>

Now, when you click on the menu toggle, it should switch from a hamburger menu to an X. The menu doesn't show up yet, because we haven't hooked it up to the open state yet. Let's do that now.

Update your <MenuWrapper> and <Menu> components:

<MenuWrapper open={open}>
  <Menu open={open}>
    ...
  </Menu>
</MenuWrapper>

And OPEN SESAME! A mobile menu now appears when you click the hamburger menu 🎉 (guess what happens when you click the X)

Conclusion

So, we now have a functioning mobile menu that slides out from underneath the header, as well as a menu toggle that looks pretty sweet!

Thanks for reading! If you liked this article, you can check me out on Twitter, or my website!

p.s For those of you wondering, the official reading time for this article is 6 minutes.

I know, I feel ashamed.

Written by

Jarod Peachey

Jarod Peachey is a front end developer and JAMstack enthusiast. He's the founder of The Five Minute Developer, and https://staticbox.io. It would make him happy if you followed him on Twitter. And maybe check out his website.