The hamburger menu is a UI as old as time.
Ok, maybe not time, but it seems like it's been around for the entirety of the modern internet age. They're not for everyone but they're certainly a handy way to show and hide content. I wanted to share a quick tutorial on how I build one using React Hooks.
Steps to create a hamburger menu
- Component Setup
Take a look at the component I have set up and you'll notice a few things.
import React, { useState, useContext } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import styled from 'styled-components'
const NavContainer = styled.nav`
`
const NavigationBar = styled.button`
`
const NavName = styled.div`
`
const Navigation = ({ siteTitle }) => {
const [nav, showNav] = useState(false)
function close() {
showNav(false)
}
return (
<div>
<div>
<NavigationBar nav={nav} onClick={() => showNav(!nav)}>
<div />
<div />
<div />
</NavigationBar>
<NavContainer nav={nav}>
<NavName onClick={close} key="tops">
<Link to="/collections/">Collections</Link>
</NavName>
<NavName onClick={close} key="about">
<Link to="/about">About Us</Link>
</NavName>
</NavContainer>
</div>
</div>
)
}
Navigation.propTypes = {
siteTitle: PropTypes.string,
}
Navigation.defaultProps = {
siteTitle: ``,
}
export default Navigation
- I'm declaring a new state variable called nav/showNav using the useState hook from React.
- I've called showNav inside a separate function, which will close the menu upon a click event.
- The menu icon will be displayed inside the
styled div, as three separate divs with css properties to display them as lines. - The menu navigation is within the
styled div, and I've wrapped each nav link within a separate div.
The hooks state is managed by clicking the NavigationBar div - when that is clicked, it will display the menu and menu items.
Here is my finished component - I've added in CSS properties to give it a slide in/out animation with translateX:
import React, { useState, useContext } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import StoreContext from '../../context/StoreContext'
import styled from 'styled-components'
const NavContainer = styled.nav`
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 30%;
height: 100vh;
min-width: 320px;
background-color: white;
z-index: 999;
padding: 5em 1em;
transition: transform 300ms;
transform: ${({ nav }) => (nav ? 'translateX(0%)' : 'translateX(-100%)')};
div {
margin-top: 1em;
}
`
const NavigationBar = styled.button`
color: white;
text-decoration: none;
font-size: 2rem;
cursor: pointer;
background: none;
outline: none;
justify-self: left;
border: none;
div {
width: 1.8rem;
height: 2px;
margin: 7px 0;
background: ${({ nav }) => (nav ? '#4c30e8' : 'white')};
transform-origin: 1px;
position: relative;
z-index: 1000;
transition: transform 300ms;
:first-child {
transform: ${({ nav }) => (nav ? 'rotate(41deg)' : 'rotate(0)')};
}
:nth-child(2) {
opacity: ${({ nav }) => (nav ? '0' : '1')};
}
:nth-child(3) {
transform: ${({ nav }) => (nav ? 'rotate(-45deg)' : 'rotate(0)')};
}
}
`
const NavName = styled.div`
a {
font-weight: bold;
font-size: 1.3em;
}
`
const Navigation = ({ siteTitle }) => {
const [hasItems, quantity] = useQuantity()
const [showText] = useState(false)
const [nav, showNav] = useState(false)
function close() {
showNav(false)
}
return (
<div>
<div>
<NavigationBar nav={nav} onClick={() => showNav(!nav)}>
<div />
<div />
<div />
</NavigationBar>
<NavContainer nav={nav}>
<NavName onClick={close} key="accessories">
<Link to="/collections/accessories">Accessories</Link>
</NavName>
<NavName onClick={close} key="tops">
<Link to="/collections/tops">Tops</Link>
</NavName>
<NavName onClick={close} key="about">
<Link to="/about">About Us</Link>
</NavName>
<NavName onClick={close} key="articles">
<Link to="/blog">Articles</Link>
</NavName>
<NavName onClick={close} key="dashboard">
<Link to="/dashboard">Dashboard</Link>
</NavName>
</NavContainer>
</div>
</div>
)
}
Navigation.propTypes = {
siteTitle: PropTypes.string,
}
Navigation.defaultProps = {
siteTitle: ``,
}
export default Navigation