How to create a sliding navigation with React Hooks and Style Components

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

  1. 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