DEV Community

Cover image for Hamburger Navbar Animation in React
Warisul Imam
Warisul Imam

Posted on • Updated on

Hamburger Navbar Animation in React

Summary: Unlike in jQuery, you can't toggle classes on your HTML elements directly because React uses JSX. You need to use React Refs in order to toggle classes to your JSX elements in order to animate them. That's the process I intend to walk the reader through in this article.
In the first part of the article, I show how it's done in regular HTML and VanillaJS. The second part shows how to do the exact same thing, but in React. And finally I've included a gif of the end product.


Let's face it, imperative animations in React is not intuitive. You can't simply select a HTML tag and add an event listener to it. Or can you? Not really. But there's a work around.

I came across YouTube video by Dev Ed where he shows a sweet and minimal hamburger navbar animation. I wanted to add something like it to a site I was working on. But the problem I faced was that Ed (the YouTube guy) implemented his animation with Javascript but my project was built in React. I could steal his CSS but I couldn't quite steal his Javascript.

I'm not going to bore you with my quest of finding a work around (although I'd love to share the story with you in another post if you're interested in it๐Ÿ™ƒ). Let's get down to the how-tos.

First let me show you how Ed did it. If you want to go straight to the React way of doing it, click here. I've extracted some parts of his HTML, CSS, and Javascript. If you don't want to look through he code, skip down below for a brief description of what he does.

Here's a part of Ed's HTML.

<div class="menu">
  <div class="line"></div>
  <div class="line"></div>
  <div class="line"></div>
</div>

<ul className="nav-links">
  <li>Home</li>
  <li>Projects</li>
  <li>About</li>
</ul>
Enter fullscreen mode Exit fullscreen mode

Here's a part of his CSS.

nav {
  background-color: #469fdf;
  height: 15vh;
  width: 100%;
  display: flex;
  align-items: center;
  padding: 5px;
  position: relative;
}

.nav-links {
  display: flex;
  list-style: none;
  width: 50%;
  height: 100%;
  justify-content: space-around;
  align-items: center;
  margin-left: auto;
}

@media screen and (max-width: 768px) {
  .line {
    width: 30px;
    height: 3px;
    background: white;
    margin: 5px;
  }

  nav {
    position: relative;
  }

  .menu {
    position: absolute;
    cursor: pointer;
    right: 5%;
    top: 50%;
    transform: translate(-5%, -50%);
    z-index: 1;
  }

  .nav-links {
    position: fixed;
    top: 0;
    background: #1484f5;
    height: 100vh;
    width: 100%;
    flex-direction: column;
    clip-path: circle(100px at 90% -30%);
    -webkit-clip-path: circle(100px at 90% -30%);
    transition: all 1s ease-out;
  }

  .nav-links.open {
    clip-path: circle(1000px);
    -webkit-clip-path: circle(1000px);
  }
}

.nav-links li {
  color: white;
}
Enter fullscreen mode Exit fullscreen mode

And finally, here's the magical javascript that Ed used to make the gorgeous animation.

const menu = document.querySelector(".menu");
const navLinks = document.querySelector(".nav-links");
const links = document.querySelector(".nav-links li");

menu.addEventListener("click", () => {
  navLinks.classList.toggle("open");
});

Enter fullscreen mode Exit fullscreen mode

What he did is he created a navbar, created a hamburger menu within it using three divs that made three straight lines, added some nav-links, and then masked them using CSS. And finally, he uses Javascript to unmask it when the hamburger menu is clicked. I'm not much of an expert in CSS and I blindly copy pasted the masking bit for the animation.

The tricky part comes next in the javascript. You'll notice he used an on-click event listener onto the hamburger menu by referencing the nav-links using a queryselector which triggers the animation (which simply toggles a css class to the nav-links). But if you know react basics you'd be able to tell that you can't simply copy paste that bit to your react application. But you can do something very similar using refs.

Without getting too deep as to what refs are, they let you reference the elements in the DOM just as a queryselector or the classic '.getElementBy...()' would. If you want to read more about refs, check out this medium article. Besides using refs, you'd also need to add an onClick prop to the div that wraps the hamburger menu, and would have to define a method that toggles the css class which executes the animation.

Here's how I did it.

First I had to create the refs in the constructor method of my class component (you can also use refs in functional components).

export default class Header extends React.Component {
  constructor() {
    super()
    this.navLinks = React.createRef();
  }
}
Enter fullscreen mode Exit fullscreen mode

Next up, I literally copy pasted the entire nav from Ed's HTML into the render() method, except I had to add classes in jsx rather than actual HTML.

render() {
  return (
    <nav>

      <div ref={this.menu} onClick={this.menuAnimation}>
        <div className="line" />
        <div className="line" />
        <div className="line" />
      </div>

        <ul className="nav-links" ref={this.navLinks}>
            <li onClick={this.menuAnimation}>Home</li>
            <li onClick={this.menuAnimation}>Projects</li>
            <li onClick={this.menuAnimation}>About</li>
        </ul>
      </nav>
    );
  }
Enter fullscreen mode Exit fullscreen mode

So I added an onClick event handler to the nav-links div and added the previously created this.navLinks ref to it. You might be wondering why I added the same event handlers to the nav-links. I'll explain in a bit.
Now we have to define the menuAnimation method. I'll put the entire class in now to avoid confusion.

import React from 'react';

export default class Header extends React.Component {
  constructor() {
    super()
    this.navLinks = React.createRef();
  }

  menuAnimation = () => {
    this.navLinks.current.classList.toggle("open");
  }

  render() {
    return (
      <nav>

        <div className="menu" onClick={this.menuAnimation}>
          <div className="line" />
          <div className="line" />
          <div className="line" />
        </div>

        <ul className="nav-links" ref={this.navLinks}>
          <li onClick={this.menuAnimation}>Home</li>
          <li onClick={this.menuAnimation}>Projects</li>
          <li onClick={this.menuAnimation}>About</li>
        </ul>
      </nav>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

The code that triggers the animation is pretty much the same. The only difference is how the nav-links div is referenced in Vanilla JS and React.

Here's the end result.

Alt Text

If you followed along by coding you must have realized why I added the menuAnimation method to the nav-linkst too. Because once you toggle the menu and choose one of the nav-links, the menu doesn't close itself (because nothing tells it to!). So you have to add the same toggle method to the nav-links so it closes the hamburger menu before it takes the user to the intended link or page.


P.S. This was my first ever post on Dev. Would really appreciate some support๐Ÿ˜Š. I'd be grateful if anyone pointed out the mistakes in my writings and hopefully I won't make them next time.
You can tweet me at @leadersheir_ and check out some of my projects on github and codepen.
Stay safe, ya'll...
HAPPY CODING!!!

Top comments (0)