A hamburger menu -- the three lines that you can click on to expand into a nav menu -- is almost always a necessity on websites for mobile navigation. I have been putting this off for several months, but finally decided to add one. After trying some different strategies, I ended up using react-burger-menu to quickly add a mobile menu to my React website.
How to Build a Hamburger Menu in create-react-app
A sidebar menu that toggles on and off is standard for mobile. I needed all of the basic elements and was not looking to be fancy. My requirements for a hamburger menu were:
- "Hamburger" icon that shows up on screens under 600px
- Icon toggles a sidebar menu
- Users can tap on the "X" icon or tap anywhere outside the menu to close it
- The menu also closes when a user clicks on a link in it
This isn't the only way to deal with navigation on mobile. I was getting around adding a mobile menu on my site by simply only showing one or two menu items to those on smaller screens. But an expandable menu is the expected behavior for mobile design so I finally gave up on being lazy.
My first thought was to code out a hamburger menu myself and I made a few different components for it. Here is a nice article on how you can build your own hamburger menu in React. However, I was in a time crunch and did not have time to make it, style it, and add some smooth animations to get the UX I wanted.
Create a React Sidebar Menu with react-hamburger-menu
Enter react-burger-menu, a React component for a mobile sidebar. It offers a functional toggle menu, comes with the basic CSS, and has a huge variety of transition effects. Out of the box, it is almost exactly what I needed for my simpler use case, but it also has built in functionality that would make it suitable if I have a larger project. You can check out some of the features on their demo site.
Implementation is relatively simple. react-burger-menu gives you a React component that you can render in a parent component. Here's how to do it:
- Install the component in your project:
$ npm install react-burger-menu --save
- Create a component for the sidebar menu. You will need to import the animation style you want from
react-burger-menu
. This will be the component you render. I'm using the "slide" menu.
By default, the menu opens from the left side of the screen so I've also added right
prop to Menu
to switch the menu to the right side.
Finally, I added links for all of my menu items using the React Router package.
import { Link } from "react-router-dom";
import { slide as Menu } from 'react-burger-menu';
function MobileNav() {
return (
<Menu right> // pass in <right> to open menu on right side of screen
<div className="MobileNav">
<div className="MobileNav-item">
<Link to="/about">About</Link>
</div>
<div className="MobileNav-item">
<Link to="/portfolio">Portfolio</Link>
</div>
<div className="MobileNav-item">
<Link to="/blog">Blog</Link>
</div>
</div>
</Menu>
)
}
- To control the menu closing after a user clicks on a link, I also added the
isOpen
prop. This takes a boolean and renders the sidebar menu accordingly. To determine which value to pass toisOpen
, I added a piece of state to my component to managemenuOpen
. I also added click events to each of my links to callcloseMenu
:
import { useState } from "react";
...
function MobileNav() {
const [menuOpen, setMenuOpen] = useState(false);
/** Close menu. */
function closeMenu() {
setMenuOpen(false);
}
return (
<Menu
right
isOpen={menuOpen}
<div className="MobileNav">
<div className="MobileNav-item">
<Link to="/about" onClick={closeMenu}>About</Link>
</div>
<div className="MobileNav-item">
<Link to="/portfolio" onClick={closeMenu}>Portfolio</Link>
</div>
<div className="MobileNav-item">
<Link to="/blog" onClick={closeMenu}>Blog</Link>
</div>
</div>
</Menu>
(If you prefer not to handle the closing yourself, there is also a handleClose
prop you can pass in that will close the menu when the user clicks the close button, the overlay, or hits the escape key, but not when they click on a link.)
4. I also used the prop onStateChange
. This detects the current state of the menu to keep my menuOpen
value in sync with the realities of the page:
/** Keep menuOpen in sync with menu state. */
function toggleMenuState(state) {
setMenuOpen(state.isOpen);
}
return (
<Menu
right
isOpen={menuOpen}
onStateChange={(state) => toggleMenuState(state)}
>
- There are a range of other props you can pass in, including options to set the width, remove the overlay, and customize the icons. You can see the full list in the documentation.
Here is what my component looks like in its entirety:
import { useState } from "react";
import { Link } from "react-router-dom";
import { slide as Menu } from 'react-burger-menu';
import "./MobileNav.css";
/** Component for MobileNav
* UI element for mobile navigation pane.
*
* Props:
* - none
*
* State:
* - menuOpen: boolean for whether the menu should show
*
* App -> MobileNav
*/
function MobileNav() {
const [menuOpen, setMenuOpen] = useState(false);
// console.debug("MobileNav");
/** Keep menuOpen in sync with menu state. */
function toggleMenuState(state) {
setMenuOpen(state.isOpen);
}
/** Close menu. */
function closeMenu() {
setMenuOpen(false);
}
return (
<Menu
right
isOpen={menuOpen}
onStateChange={(state) => toggleMenuState(state)}
>
<div className="MobileNav">
<div className="MobileNav-item">
<Link to="/about" onClick={closeMenu}>About</Link>
</div>
<div className="MobileNav-item">
<Link to="/portfolio" onClick={closeMenu}>Portfolio</Link>
</div>
<div className="MobileNav-item">
<Link to="/blog" onClick={closeMenu}>Blog</Link>
</div>
</div>
</Menu>
)
}
export default MobileNav;
With the component built, I added a CSS file for styles and copied in the react-burger-menu default styles and updated them to match my theming.
The last step is render the MobileNav
component in my parent component. In my case, App
renders it. I also added in a CSS class in App
to set display: none
on MobileNav
for any screen sizes above my breakpoint of 600px.
Wrap Up
There are several other libraries out there for sidebar menus. Templates will often have a responsive menu already built in, but this is one I will keep around for my next React project.