How to implement Hamburger Menu in React

Whether you are working on a React project or a framework based on it like the Gatsby JS, you would often want to implement the hamburger menu (sometimes called menu button), especially on mobile view.

In this part of the series, you’ll learn how to implement the functionality that allows you to toggle sidebar navigation. Once you grab the principle, you can use it to implement some other things like toggling an accordion.

So let’s see how to do this.

In our todos app, we want to toggle the sidebar nav when a menu button is clicked. And if you remember, we mentioned earlier in the series that one of the reasons we declare a state in a component is if it will handle a toggle.

So let’s head over to the Navbar.js file and import the useState hook.

import React, { useState } from "react"

Then, add this in the function component:

const [navbarOpen, setNavbarOpen] = useState(false)

In the code, we set the initial state to false. By default, we want to hide the sidebar navigation. Then, when the menu button is clicked, we can then call the updater function, setNavbarOpen, to update the state.

Next, we need to create a button for this toggle. And then, we will pass the state to it.

Add a button element inside the nav element (but above the <ul>):

return (
  <nav className="navBar">
    <button>{navbarOpen ? "Close" : "Open"}</button>
    <ul>...</ul>
  </nav>
)

For the meantime, we are dynamically displaying a button text based on the value of the state variable.

If the value is true, we display "Close", else we display "Open".

For now, the default value is false. So you’ll only see a button with the "Open" text. Later in this part, we will replace the text with a react-icon.

Save the file and see the button displayed in the frontend.

Next, we need to update the state so that we can toggle the button text. You already know how to do that. Yes, we will use the updater function. But first, we will add an event handler to the button that will trigger the updater function.

Update the button element so you have:

<button onClick={handleToggle}>{navbarOpen ? "Close" : "Open"}</button>

Then, add this handler above the return statement:

const handleToggle = () => {
  setNavbarOpen(prev => !prev)
}

Save the file and test your work. You’ll see that the button text changes as you click on it.

In the code, we are updating the state using the updater function. Though, you can simply do this and move on:

const handleToggle = () => {
  setNavbarOpen(!navbarOpen)
}

It will work.

But we should not use this approach if the state depends on the previous. Remember, we are toggling the button text. And this depends on the Boolean value (true or false) of the state variable.

If the code was not clear to you, please visit the react hooks part of the series.

Now, let’s take advantage of the state variable to dynamically add a class name to the side drawer element.

Still in the Navbar.js file, update the <ul> opening tag to include the class name:

<ul className={`menuNav ${navbarOpen ? " showMenu" : ""}`}>...</ul>

The code is self-explanatory. We are simply adding a showMenu class to the ul only if the state variable, navbarOpen is true. When it is false, we remove the class.

Save the file.

Go to the frontend and inspect the ul element. If you now click on the menu button, the class name, showMenu gets toggled.

Let’s add styles to capture this event.

Head over to the App.css file and add the following styles in the bottom:

/* sidebar Nav */

.navBar {
  position: relative;
}

.navBar button {
  position: fixed;
  left: 40px;
  top: 40px;
  z-index: 10;
  cursor: pointer;
}

.menuNav {
  overflow-y: scroll;
  list-style: none;
  position: fixed;
  top: 0;
  background: darkcyan;
  left: 0;
  bottom: 0;
  height: 100vh;
  width: 0;
  overflow: hidden;
  max-width: 290px;
  z-index: 9;
}

.menuNav.showMenu {
  width: 100%;
}

a {
  display: block;
  padding: 10px 40px;
  text-decoration: none;
  color: #3fffd2;
  text-transform: uppercase;
  font-weight: bold;
}

.menuNav li:first-child {
  margin-top: 7rem;
}

Save the file and see your styles applied in the frontend.

At the moment, if you click on the link to navigate to another page, the page will be rendered but the sidebar will remain open. You can only close it by clicking on the menu button or you reload the page.

The fix is very simple!

We will add an onClick event to the nav links. Its only job is to set the state variable to false. So once you click the nav links, the state goes back to the default value, i.e false.

Update the NavLink component so you have:

<NavLink
  to={link.path}
  activeClassName="active-link"
  onClick={() => closeMenu()}
  exact
>
  {link.text}
</NavLink>

Then add closeMenu function above the return statement:

const closeMenu = () => {
  setNavbarOpen(false)
}

Save your file and test your app. It should now work as expected.

Replacing the Button Text with Menu Icons

We will be getting our icons from the react-icons library. This is pretty straight forward as we’ve done it before. Please revisit the react icons part if you need a refresher.

In the Navbar.js file, import these icons from their respective icon set.

import { MdClose } from "react-icons/md"
import { FiMenu } from "react-icons/fi"

Then replace the text in the button with these icons.

<button onClick={handleToggle}>
  {navbarOpen ? (
    <MdClose style={{ color: "#fff", width: "40px", height: "40px" }} />
  ) : (
    <FiMenu style={{ color: "#7b7b7b", width: "40px", height: "40px" }} />
  )}
</button>

Notice, we’ve added style to each of the buttons. Save the file and check the frontend to see your changes.

Simple right?

Before we round up, let’s simply style the button element and the About page.

In the App.css, update the .navbar button styles to include the background and border.

.navBar button {
  ... /* addition */ background: transparent;;
  border: none;
}

Good.

For the about page, let’s add class names to style the component.

In the About.js file, add class names so you have:

const About = () => {
  const { url, path } = useRouteMatch()
  return (
    <div className="about__content">
      <ul className="about__list">...</ul>
      ...
    </div>
  )
}

Then in the SinglePage.js file.

const SinglePage = () => {
  ...
  return (
    <div className="main__content">
      ...
    </div>
  )
}

Now, update the App.css file so you have:

/* about page */

.about__content {
  padding: 8rem 40px;
  display: flex;
}

.about__list {
  list-style: none;
  flex: 2;
  margin-right: 15px;
}

.about__list a {
  color: #9f9f9f;
  text-transform: capitalize;
  padding: 20px 0;
  font-weight: 600;
}

.about__list a:hover {
  color: #000;
}

.main__content {
  flex: 7;
}

.main__content h1 {
  margin-bottom: 15px;
  line-height: 30px;
  font-size: 30px;
}

.main__content p {
  line-height: 25px;
}

Good.

Save and test your app.

Congratulations. You now have a fully functional React app.

Next part: Deploying React App to GitHub Pages

continue