Framer Motion - beautiful animations and interactions for React. 🤤
Tags: react, javascript, tutorial, webdev
Published: 2020-10-16 12:43:45

[Previously, I covered React Spring](https://dev.to/vaibhavkhulbe/spring-it-on-a-complete-guide-to-react-spring-1om9), a spring-physics based animation library. We made some easy to make toggle and then a somewhat complex animation/transition on hover.

Well, that was all good and cool until I came up with Framer Motion! This is a really great library to make your prototypes come to life with double the ease of use from React Spring.

Let's take a look at what it has to offer and why you should be using it in your next React project.

---

# What is Framer Motion? 🤔

> **[Framer Motion](https://www.framer.com/motion/)** is a production-ready motion library for React. It utilizes the power of the [Framer](https://www.framer.com/) prototyping tool and is fully [open-source](https://github.com/framer/motion).

There are some out-of-the-box features or selling points:

1. Animations ([CodeSandbox demo](https://codesandbox.io/s/xlrx1?module=%2Fsrc%2FExample.tsx))
2. Variants. ([CodeSandbox demo](https://codesandbox.io/s/uotor?module=%2Fsrc%2FExample.tsx))
3. Gestures. ([CodeSandbox demo](https://codesandbox.io/s/81jzd?module=%2Fsrc%2FExample.tsx))
4. Drag. ([CodeSandbox demo](https://codesandbox.io/s/t9uoz?module=%2Fsrc%2FExample.tsx))
5. Scroll. ([CodeSandbox demo](https://codesandbox.io/s/c4ww4?module=%2Fsrc%2FExample.tsx))
6. Path. ([CodeSandbox demo](https://codesandbox.io/s/rutrh?module=%2Fsrc%2FExample.tsx))

My favorite one is the Variants, click below to interact:

{% codesandbox uotor runonclick=1 %}

Also,

1. It uses server-side rendering.
2. It has CSS variables support.
3. You can unmount animations easily.
4. It has great accessibility options.
5. You can handoff designs from Framer to Framer Motion.

Before we do any of this we need to understand some basics, most probably its API.

## The Motion API 🔌

This is what the heart of Framer Motion is. It provides us with a variety of options including those which you saw in the above points.

### ➡ The [`motion`](https://www.framer.com/api/motion/component) component.

It's a React component built-into the library and is available for almost any of the HTML or SVG element you'll be using in your UI. These are DOM optimized for 60fps animation and gesture support. 

What's good is that we can easily convert a static HTML/SVG element to a motion component. For example, if we have a usual `div`, then just add `motion.` in front of that HTML tag and you have a `motion` component! Hence `<div>` becomes `<motion.div>`.

It allows you to:

- Declaratively or imperatively animate components.
- Animate throughout React trees via _variants_.
- Respond to gestures with animations.
- Add drag, pan, hover, and tap gestures.

Here's an example:

```jsx
<motion.div
    animate={{ rotate: 360 }}
    transition={{ duration: 2 }}
 />
```

### ➡ The [`animation`](https://www.framer.com/api/motion/animation) prop.

As you saw in the above code snippet, `motion` components are animated via the `animate` prop. When any value in animate changes, the component will automatically animate to the updated target.

If you use `x` or `scale` values then they will be animated via a spring simulation. Whereas values like `opacity` or `color` will be animated with a tween.

You can set different types of animation by passing a [`transition`](https://www.framer.com/api/motion/types#transition) prop.

Here's an example:

```jsx
<motion.div
  animate={{ x: 100 }}
  transition={{ ease: "easeOut", duration: 2 }}
/>
```

### ➡ [Gestures](https://www.framer.com/api/motion/gestures).

All the `motion` components can detect _hover_, _tap_, _pan_, and _drag_ gestures. Each of these has event listeners you can attach.

Two of the commonly used gesture props provides by the `motion` component are `whileHover` and `whileTap`.

Here's an example:

```jsx
motion.button
  whileHover={{
    scale: 1.2,
    transition: { duration: 1 },
  }}
  whileTap={{ scale: 0.9 }}
/>
```

### ➡ [MotionValue](https://www.framer.com/api/motion/motionvalue).

This is used to track the _state_ and _velocity_ of animating values. These are created automatically. But for advanced use-cases, it is possible to create them manually.

It allows you to:

- Set and get the state.
- Chain MotionValues via the `useTransform` hook.
- Pass to multiple components to synchronize motion across them.

Here's an example:

```jsx
export function MyComponent() {
  const x = useMotionValue(0)
  return <motion.div style={{ x }} />
}
```

# What we'll be making? 😏

![Tutorial demo](https://dev-to-uploads.s3.amazonaws.com/i/r36cwy8hi0xnlyyho800.gif)

Yes! We're taking the boilerplate interface that comes when we create a React app and adding a little bit of interaction fun to it. As you can see, these are a few of the things happening:

1. First, when the page loads, it _fades in_. The only animation happening.
2. Next comes the interactions. When the React logo is clicked, we see it acts as a button. It _pushes back_ on mouse press and when released, it comes to its normal state.
3. We can also _click and drag_ the React logo horizontally and it keeps _fading_ as it moves away from the center.
4. When hovered, the text below the logo _scales up_.
5. To move the text from its _position horizontally_, we have a slider from which it can be controlled.
6. Finally, we can _fade in and out_ the same text with the toggle button.

There's so much to cover. Let's dive straight into the development!

#### Step 1: Create a React project and add Framer Motion

After you're done with [creating a React app](https://reactjs.org/docs/create-a-new-react-app.html), simply install the Framer Motion dependency with the following command:

```
npm i framer-motion
```

#### Step 2: Import the library and convert the elements!

For this demo, we need to `import` these three API functions: `motion`, `useMotionValue`, `useTransform`. 

```js
import { motion, useMotionValue, useTransform } from 'framer-motion';
```

We already talked about the first two. Now the `useTransform` is a hook through which we can pass the latest MotionValue through an update function that takes the latest parent value and transforms it.

After the import, we need to change some of the default HTML tags that come with React boilerplate to the new Framer Motion ones. Do the following changes in _App.js_:

- Parent `<div>` element to `<motion.div>`.
- Wrap the React logo `<img>` tag inside a newly created `<motion.div>`.
- The `<p>` tag to `<motion.p>`.
- Add a new `<input>` element which will be our range slider with `min` and `max` values as `-100` and `100` respectively.
- Next to this create a new `<motion.button>` with the text as "Toggle Fade".

Here's what we did so far:

```jsx
<motion.div className='App'>

      <header className='App-header'>

        <motion.div>
          <img src={logo} className='App-logo' alt='logo' />
        </motion.div>

        <motion.p>
          Edit <code>src/App.js</code> and save to reload.
        </motion.p>

        <input
          type='range'
          name='range'
          min='-100'
          max='100'
        />

        <motion.button className='toggle-button'>
          Toggle Fade
        </motion.button>

      </header>

</motion.div>
```

Nothing will happen yet as we haven't written any props and the Motion code doesn't have its properties to work on.

#### Step 3: Add the animations and transitions!

### The page fade animation:

For the initial fade animation, we use the `initial`, `animate`, and `transition` properties over the new `motion.div`.

- The [`initial`](https://www.framer.com/api/motion/component/#motionprops.initial) initializes a value for the `animate`.
- The [`animate`](https://www.framer.com/api/motion/component/#animationprops.animate) has the actual values to animate to 
- The [`transition`](https://www.framer.com/api/motion/component/#animationprops.transition) is used to add a default transition of one frame change to another.

As we need a simple fade animation where the animation takes place half a second, we give the following properties to the `motion.div`:

```jsx
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
```
Now the entire page fades!

![Page fade animation](https://dev-to-uploads.s3.amazonaws.com/i/lxa835q5ixn3ikjmkj58.gif)

### The tap and drag interaction on React logo:

This is achieved by the `whileTap` and `drag` helper animation props on the `motion` component.

- The [`whileTap`](https://www.framer.com/api/motion/gestures#taphandlers.whiletap) animates while the element is pressed/clicked.
- The [`drag`](https://www.framer.com/api/motion/gestures#draggableprops.drag) enables the dragging gesture of an element and is set to `false` by default.

So, on tapping the logo, we first need it to scale a bit, hence we add the `scale` property to the `whileTap` prop and for the drag, we need to pass on which axis dragging is to be done. Here, we do it horizontally so it's the `x` axis.

To achieve the actual value of the `x` we passed on the `drag` prop, we'll be using the [`useMotionValue`](https://www.framer.com/api/motion/motionvalue/#usemotionvalue) hook which tracks the state and the velocity of the dragged element. Initially, we don't want the drag to enable, hence we pass in `0`.

As for defining the coordinates of how much drag is to be done, the [`useTransform`](https://www.framer.com/api/motion/motionvalue/#usetransform) hook will help us. Through this, we can pass in the latest value of the `x` axis. It can be any number you like depending on the expensiveness of the drag you want to achieve.

```jsx
const x = useMotionValue(0);
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
```

Now, for both of these to work, we need to pass the `style` prop which takes in the different constants we provided above. Hence out image drag and tap interaction code looks like this:

```jsx
const x = useMotionValue(0);
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);

.
.
.
<motion.div whileTap={{ scale: 0.9 }} drag='x' style={{ x, opacity }}>
    <img src={logo} className='App-logo' alt='logo' />
</motion.div>
.
.
.
```
Now this interaction works!

![React logo interaction](https://dev-to-uploads.s3.amazonaws.com/i/1m9xui8wjmxu85fkckbk.gif)


### Interactions on the text:

We have a scale on hover and tap, a drag on moving the slider, and finally a fade toggle using a button to finish off.

The scale is done exactly like we did the tap, it's just that here the interactions are both tap and hover so for the new hover interaction we use the [`whileHover`](https://www.framer.com/api/motion/gestures/#hoverhandlers.whilehover) prop.

The `x` variable is used for the horizontal drag as we need the same values. Now, to constraint its values we can fine-tune it using the [`dragConstraints`](https://www.framer.com/api/motion/gestures/#draggableprops.dragconstraints) prop which allows us to pass the `left` and `right` constraints of the drag gesture.

For the final interactions, we need to use the [`useState`](https://reactjs.org/docs/hooks-state.html) React Hook as we are changing the drag and fade states of the text. Hence, we define the following two variables for states:

```jsx
const [value, setValue] = useState(0);
const [toggle, setToggle] = useState(1);
```

On the `<input />` element we created at the beginning, its `onChange` event uses the `setValue()` method from the `useState` Hook and we pass in the current value chosen by the user when they drag the slider. A similar event is fired on the `<motion.button>`'s `onClick` but here we're toggling by interchanging the states from `0` to `1` or vice-versa.

For the actual fade to kick in, we simply get the `value` from the created state (adding the `'px'` string so that it works as an actual pixel unit) and use the `opacity` value equal to the `toggle` we created. 


```jsx
const [value, setValue] = useState(0);
const [toggle, setToggle] = useState(1);
.
.
.
<motion.p animate={{ x: value + 'px', opacity: toggle }}
          drag='x'
          dragConstraints={{ left: -100, right: 100 }}
          whileHover={{ scale: 1.1 }}
          whileTap={{ scale: 0.9 }}>
          Edit <code>src/App.js</code> and save to reload
</motion.p>

<input type='range' name='range' min='-100' max='100'
       value={value}
       onChange={(e) => setValue(e.target.value)} />

<motion.button onClick={() => setToggle((prevValue) 
                           => (prevValue ? 0 : 1))} 
               className='toggle-button'>Toggle Fade
</motion.button>
.
.
.
```

The button styles are simple in CSS to look better from the default one:

```css
.toggle-button {
  margin-top: 1.5em;
  width: 10em;
  border: 0;
  outline: none;
  padding: 1em;
  border-radius: 10em;
  font-weight: bold;
}

.toggle-button:hover {
  color: #282c34;
  background-color: #61dafb;
}
```

And now our final interaction also works!

![Text interactions](https://dev-to-uploads.s3.amazonaws.com/i/v24nkngzo1ipyddravbr.gif)

---
If you're not impressed by this library then you can check out React Spring. I wrote a tutorial regarding the same:

{% post https://dev.to/vaibhavkhulbe/spring-it-on-a-complete-guide-to-react-spring-1om9 %}

---

_Thanks for reading, I appreciate it! Have a good day. (✿◕‿◕✿)_

---
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">And if you want to impress developers, you can try the new open-source Windows Terminal. 😎 Download here: <a href="https://t.co/bZQ78xBBr7">https://t.co/bZQ78xBBr7</a><br><br>Image source: <a href="https://t.co/GsUA5a7mlf">https://t.co/GsUA5a7mlf</a><a href="https://twitter.com/hashtag/DevHumour?src=hash&amp;ref_src=twsrc%5Etfw">#DevHumour</a> <a href="https://t.co/WQfKOWTjfa">pic.twitter.com/WQfKOWTjfa</a></p>&mdash; Microsoft Developer UK (@msdevUK) <a href="https://twitter.com/msdevUK/status/1316645994809954304?ref_src=twsrc%5Etfw">October 15, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
---

#### [📫 Subscribe to my weekly developer newsletter 📫](https://mailchi.mp/f59beeac6b9b/devupdates)

##### PS: From this year, I've decided to write here on DEV Community. Previously, I wrote on Medium. If anyone wants to take a look at my articles, [here](https://medium.com/@vaibhavkhulbe)'s my Medium profile.