Animate Burger Menu with React-Spring
Tags: javascript, tutorial, react, webdev
Published: 2023-03-26 16:20:43



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prdkr2q17p0d1zg3q3hd.gif)


## Introduction

This post will describe just another way to create a burger menu animation that looks super natural and physically accurate.

There are many ways to make burger animations using different tools. For example, you can create an animation using basic HTML and CSS or by using JavaScript libraries like Velocity.js. If you're working with React, you can use Framer Motion or React-Spring.

In this post, we will use React-spring, which enables the creation of animations based on real physics principles. Let's dive right in!

## Step-by-Step Guide

Burger menu animation is a type of "micro animation”. While almost invisible, these animations are important because they can enhance the user experience by adding visually pleasing effects to user interfaces.

A few words about React Spring. It’s a simple but very powerful tool. In short, it interpolates a value from ***a*** to ***b***. But it's important to note that this interpolation is not a simple linear calculation. It's based on real-world physics principles.


| Linear Animation | Spring Animation |
| --------- |:---------:|
| ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yfuct19087y0u0jopei7.gif)| ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn6uq7tit18zy3wxi2dz.gif)|

### Prepare Icon Component

To begin, we need to prepare our icon, which will consist of three rounded rectangles that will be transformed into two crossed lines when the burger menu opens.

To animate an SVG icon, it should be transformed into a JSX component. Here is an example of a simple burger menu icon component:

To begin, we need to prepare our icon, which will consist of three rounded rectangles that will be transformed into two crossed lines when the burger menu opens. 
To animate an SVG icon, it should be transformed into a JSX component. Here is an example of a simple burger menu icon component:

```jsx
    const BurgerMenuIcon = () => {
    	return (
    		<svg
    		  width="24"
    		  height="20"
    		  viewBox="0 0 24 20"
    		  fill="none"
    		  xmlns="http://www.w3.org/2000/svg">
    		  <rect
    		    y={"0"}
    		    width="24"
    		    height="2"
    		    rx="1"
    		    fill={"#000025"}
    		  />
    		  <rect
    		    y="9"
    		    width="24"
    		    height="2"
    		    rx="1"
    		    fill={"#000025"}
    		  />
    		  <rect
    		    y="18"
    		    width="24"
    		    height="2"
    		    rx="1"
    		    fill={"#000025"}
    		  />
    		</svg>
    	)
    }
```
    

### Decompose Animation

This is the most important part of any animation. We should know how each element of the animation will move. To make the animation look better, the elements should move in a way that looks natural and lifelike. For example, we can break down our icon into the following stages:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7f8zki54siik5xy1szxd.png)


### Let's move it!

To animate an HTML element using React-spring, it should be wrapped in the [animated](https://react-spring.dev/docs/concepts/animated-elements) component from the react-spring library.

As we will be animating our `rect` elements, let's rewrite them as `animated.rect`. Then we can provide prop `style` to it. 

Let’s try to rotate first rect of burger icon:

```jsx
// Here, we will configure our animation using the useSpring hook. This hook returns a special object that generates a sequence of transitioned values.
const style = useSpring({
        to: {
			transform: isOpen ? "rotate(-45deg)" : "rotate(0deg)",
            config: config.wobbly,
		}
});

...
// apply a style to our element that is wrapped in the animated component
<animated.rect
  style={style}
  y={"0"}
  width="24"
  height="2"
  rx="1"
  fill={"#000025"}
/>

...
```

But we have two problems with the SVG element. First, if the rect element extends beyond the parent boundaries, it gets clipped. This can be resolved by setting the overflow: visible property for our SVG element. Second, the element rotates around a point other than its center. To address this, we need to establish a rule for the rect element on how to apply transform properties to it.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ry2i4apxq92t3pbo9xnu.gif)


We can fix these issues by adding a few lines of CSS to our SVG element:

```css
.burgerIcon{
	overflow: visible;
}

.burgerIcon>rect{
	transform-origin: center;
    transform-box: fill-box;
}
```

### Bringing it all together

Now that we have defined each step of the animation and know how to apply it to an element, we can animate the top, medium, and bottom parts of the burger menu using the following algorithm:

- **Stage 1**
    - Move the *top* and *bottom* rectangles to the center.
    - At the same time, change the opacity of the *medium* rectangle to zero.
- **Stage 2**
    - Rotate *top* rectangle to -45 degrees and the *bottom* to -135 degrees.

Note that we don't need to use spring animation in Stage 1 because it's just an intermediate stage of a complex animation. To change it to linear, set the `duration` in the config as follows:

```jsx
const style = useSpring({
    to: {
			transform: isOpen ? "rotate(-45deg)" : "rotate(0deg)",
      config: {
				duration: 100 // in milliseconds
			},
		}
});
```

Here is the resulting code of the component:

**See code**
    
```jsx
    import { animated, config, useSprings } from "react-spring";
    import { memo } from "react";
    
    const linearConfig = {
      duration: 100
    };
    
    const topRect = async (next, isOpen) => {
      // Stage 1
      await next({
        transform: isOpen
          ? "translate(0px, 9px) rotate(0deg)"
          : "translate(0px, 9px) rotate(0deg)",
        config: linearConfig
      });
    
      // Stage 2
      await next({
        transform: isOpen
          ? "translate(0px, 9px) rotate(-45deg)"
          : "translate(0px, 0px) rotate(0deg)",
        config: config.wobbly
      });
    };
    
    const mediumRect = async (next, isOpen) => {
      // Medium rect has only Stage 1
      await next({
        to: {
          opacity: isOpen ? 0 : 1
        },
        // Make delay when isOpen changed from true to false
        delay: !isOpen && 100,
        config: linearConfig
      });
    };
    
    const bottomRect = async (next, isOpen) => {
      // Stage 1
      await next({
        transform: isOpen
          ? "translate(0px, -9px) rotate(0deg)"
          : "translate(0px, -9px) rotate(0deg)",
        config: linearConfig
      });
    
      // Stage 2
      await next({
        transform: isOpen
          ? "translate(0px, -9px) rotate(-135deg)"
          : "translate(0px, 0px) rotate(0deg)",
        config: config.wobbly
      });
    };
    
    const rects = [topRect, mediumRect, bottomRect];
    
    const BurgerMenu = memo(({ isOpen, onClick }) => {
      const [springs] = useSprings(
        3,
        (index) => ({
          to: async (next) => {
            await rects[index](next, isOpen);
          }
        }),
        [isOpen]
      );
    
      return (
        <svg
          onClick={onClick}
          width="24"
          height="20"
          viewBox="0 0 24 20"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
          className="burgerIcon"
        >
          {springs.map((props, index, args) => {
            return (
              <animated.rect
                key={index}
                y={index * 9}
                width="24"
                height="2"
                rx="1"
                fill={"#4F4F4F"}
                style={props}
              />
            );
          })}
        </svg>
      );
    });
    
    export default BurgerMenu;
```
    

### Sandbox

You can experiment with different options in the config to see how they affect the animation. Click on the burger icon to run the animation.



{% codesandbox https://codesandbox.io/embed/lively-grass-u2co4i?fontsize=14&hidenavigation=1&theme=light&view=preview %}

In this sandbox, you can choose a configuration from presets. However, you can also define your own configuration by using the parameters of mass, friction, and tension. For more information on Spring configurations, refer to [this page](https://www.react-spring.dev/docs/advanced/config).

## Conclusion

With React Spring, you can create eye-catching animations and configure them with real physical parameters like mass, friction, and tension.

This is just one of thousands of ways to create a burger menu animation, and you can create your own using the basic principles from this example. When creating animations for web elements, it is important to think of them as real-life objects subject to real physics.

Thank you for reading this post! I would appreciate your feedback and comments.