# How to Animate SVG Paths with Framer Motion

If you're looking for some flair when fading in a logo or want to give loading animations some spice, SVG path animations are just the thing! Framer motion makes it easy for you to create great looking path animations for your React project.

Let's see how path animations work!

## SVG Paths

Paths are by far the most powerful SVG element. They can create arcs, curves, lines and more. If you used Photoshop, Illustrator or Inkscape, you are likely already familiar with paths. It is the SVG equivalent of the pen tool.

It draws a line on the canvas, moving from point to point. The `d` attribute controls where that line goes. It uses commands and parameters passed as a string. The path can move straight or curved. It can move relative to its current position, and it can move in absolute terms. The coordinates in the `d` parameter are always unitless. That means they are in the user coordinate system established in the parent `<svg>` component. If you don't know what that is, I wrote a [blog post with interactive examples](https://blog.noelcserepy.com/3-concepts-to-master-svgs) that covers this in detail.

I won't go into further detail on this because the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths) on paths are genuinely excellent.

Beziér curves are part of what makes paths so potent. If you find them just as fascinating as I do, check out [Freya Holmér's video on Bézier Curves](https://youtu.be/aVwxzDHniEw).

## How Path Animations Work

One can use a clever trick to get the effect of the path being *drawn*. To animate the path, we need to control two attributes of the `<path>` element.

The first is `stroke-dasharray`, which turns our path into a dashed line. We can define a repeating pattern that describes how large the dashes and gaps between them are. Setting a value of `"10 10"` would represent a dash that is ten units long, followed by a gap of 10 units long. This pattern repeats for the length of the path.

The second is `stroke-dashoffset`, which pushes our pattern by a given amount along the path.

**Now for our trick:** We can make the dashes and gaps as long as the path itself. If we then use `stroke-dashoffset` to move the pattern, we can completely hide the dash and only show the gap.

`stroke-dashoffset` can then be animated to make the path *draw itself*. Click on the interactive window below to see for yourself.

<iframe src="https://examples.noelcserepy.com/svg/pathanimation" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden"></iframe>

To accurately hide the path, we usually need to get the total length of the path using something like this:

```js
var path = document.querySelector('.path');
var length = path.getTotalLength();
```

But this is not necessary when we use Framer Motion. Although you can animate all these values, motion components expose unique attributes that allow us to do all of the above with a single value.

Let's get into it!

## Installing Framer Motion

Navigate to the root directory of your React project (React 18 or greater). Run the following command.

```shell
npm install framer-motion
```

After installation, import Framer Motion wherever you want to use it.

```js
import { motion } from "framer-motion"
```

Check out the [installation docs](https://www.framer.com/docs/introduction/##installation) for more information.

## Convert `<path>` to a Motion Component

Framer Motion gives us some valuable shortcuts for animating SVG paths. We need to convert our existing components into *motion components*to use them. We can do this by importing `motion` at the top of the document and adding `motion.` to the HTML element we want to animate. Don't forget to add `motion.` to the closing tags.

```jsx
import { motion } from "framer-motion";

<motion.svg>
	<motion.path />
</motion.svg>
```

Now that we have motion components, we can start animating!

## Animating the motion component

Luckily, Framer Motion makes this very simple. The `<motion.path>` motion component has an attribute called `pathLength`. As the name suggests, it controls the length of the visible path. A value of `0` completely hides the path, and a value of `1` shows the whole path.

Let's see how that looks in the following example. Press "Refresh Preview" on the CodeSandbox below to trigger the animation again. If you are unfamiliar with CodeSandbox, you can view the code by dragging the slider on the left side of the window.

<iframe src="https://codesandbox.io/embed/path-example-blob-ciuq2m?fontsize=14&hidenavigation=1&theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

By setting the `initial={{pathLength: 0}}` and the a `animate={{pathLength: 1}}`, the path animates from completely hidden to completely visible. Framer motion animates this **every time the component is rendered**.

The `initial` attribute defines the animation *target* (state) **before** the animation. The `animate` attribute defines the animation *target* that the component should animate **to** (the destination). This includes the final resting state of the motion component as well as the transition to get there.

All of the `stroke-dasharray` and `stroke-dashoffset` shenanigans we discussed earlier are handled under the hood by Framer Motion.

## Transitions

Framer Motion has excellent default transitions, making it very fast to slap an animation on an unsuspecting HTML element 👏. But if we want more control, the `transition` attribute is where we make that happen.

Most importantly, `duration` allows us to define how long the animation takes from start to end (in seconds). We can let the animation play at a later time using `delay`.

In the example, we are animating `pathLength` from an initial value of `0` to a value of `1`. You may have noticed that the animation starts slowly, speeds up, and ends slowly. In animation, this is called *easing* and can control it via the `ease` option on the `transition` attribute. For this animation, Framer Motion defaults to `"easeInOut"`. In contrast, the `"linear"` value would keep the same speed between `0` and `1`. Framer Motion covers most common cases with their [built-in easing functions](https://www.framer.com/docs/transition/###ease). You can even define custom easing functions, but that is for another time.

There are also other transition *types* controlled by the `type` option. These include [tween](https://www.framer.com/docs/transition/#tween), [spring](https://www.framer.com/docs/transition/#spring) and [inertia](https://www.framer.com/docs/transition/#inertia). Framer Motion will default to different *types* for different animations. The example above and any other animation that includes an easing function is of type *tween*. *Spring* and *inertia* work by simulating physics and thus have different controls.

You don't need to know all of this to make beautiful animations. I wanted to mention it so that you know what's possible and where to go from here. Let's look at examples of how we can use SVG path animations on the web!

## Basic Examples

Hint: Click "Refresh preview" in the bottom left to see the "Inertia" example animate again.

<iframe src="https://codesandbox.io/embed/path-examples-2-qzpy35?fontsize=14&hidenavigation=1&theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

### Explanations

**Forward**  
The same animation we did above.

**Back**  
Instead of animating `pathLength` from `0` to `1`, we do it from `1` to `0`.

**Back to front**  
We leave `pathLength` at `1` and instead animate `pathOffset` from `1` to `0`.

**Front to back**  
Same as back to front but moving `pathOffset` from `0` to `1`.

**Dashed**  
Just like with `stroke-dasharray`, we can change the size of the gaps using Framer Motion's `pathSpacing` attribute. It works the same as `pathLength`. A number between `0` and `1` will determine how large the gap is.

**Spring**  
Here we use the transition type `spring` instead of `tween`. There are two ways to configure it, but the easy one includes setting `bounce` and `duration` values. You can find out more [here](https://www.framer.com/docs/transition/#spring).

**Inertia**  
The `inertia` transition type is used mainly on drag animations and momentum scrolling. There are a lot of values to tweak, and if you are interested, I suggest you read more [here](https://www.framer.com/docs/transition/#inertia).

**Looping**  
Typically, the animation runs one time when the component renders. However, we can repeat this animation. Looping an animation is handled in the `transition` attribute. You can set the `repeat` option to the number of times you want the animation to play. I set it to `Infinity` in the examples, so they loop forever. You can also change the `repeatType` to affect the repeat behaviour. `"loop"` will start the animation from the start. That is also what I am using above. `repeatType: "mirror"` will switch the "to" and "from". `repeatType: "reverse"` alternates forward and backwards playback.

## Variants

So far, we have looked at `initial` and `animate` as our two animation *targets*. An animation *target* defines what the component should animate **to** and what **transition** it should use to get there.

With variants, we can set `animate` to one of many targets, depending on the state of our application. Think of a menu that can be open or closed.

Variants allow us to bundle all of these targets into a single object. We can then choose which target to display by setting the `animate` value to the key of the desired target within the variants object.

```jsx
import { motion } from "framer-motion";

const menuVariants = {
	open: {
		opacity: 1,
		transition: {
			duration: 1
		}
	}
	closed: {
		opacity: 0,
		transition: {
			duration: 1
		}
	}
}

export default function Menu(){
	return(
		<motion.svg
		variants={menuVariants}
		initial="closed"
		animate="open"
		/>
	)
}
```

To change the target, we can use state and conditionals:

```jsx
// Using state with variant name
export default function Menu(){
	const [showMenu, setShowMenu] = useState("closed")
	return(
		<motion.SVG
		variants={menuVariants}
		initial="closed"
		animate={showMenu}
		/>
	)
}

// Using boolean state
export default function Menu(){
	const [showMenu, setShowMenu] = useState(false)
	return(
		<motion.svg
		variants={menuVariants}
		initial="closed"
		animate={showMenu ? "open" : "closed"}
		/>
	)
}
```

There are many use cases for variants, including [orchestration](https://www.framer.com/docs/animation/##orchestration), that I will not be getting into here. Let's look at advanced examples to see what we can do with variants.

## Advanced Examples

<iframe src="https://codesandbox.io/embed/path-examples-advanced-jwe9t3?fontsize=14&hidenavigation=1&theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

### Explanations

**Logo**  
Believe it or not, this is the same animation as the "Forward" example from the "basic" section. No fancy variants or anything. I put it here to show you that animations don't need to be complicated to be impactful.

It's only a simple `pathLength` animation going from `0` to `1`. The only difference is the `<path>` I was using. The more interesting the shape of the `<path>`, the more interesting the animation. In this case, I applied the animation to `<motion.line>` , `<motion.rect>`, `<motion.circle>` and `<motion.polygon>` elements.

You might be saying, "but these are not paths!". You are right, but it doesn't matter. It works just the same. All SVG lines are just vectors going from one coordinate of the viewBox to the next and can therefore be animated this way. If you want to know more about how SVGs use coordinates, check out my [guide to SVG scaling](https://blog.noelcserepy.com/3-concepts-to-master-svgs).

**Toggle**  
There are two `<path>`'s here. One is for the larger pill shape acting as the background. The other one is the moving pill shape in the foreground. Both `<path>` elements are just a single line. A single **fat** line. To make them look pill-shaped, I increased the `strokeWidth` and set `strokeLinecap="rounded"`. I used state to switch between *on* and *off* variants. When *on*, I increase `pathOffset` and change the colour to orange. Note that I am also using a new easing function.

**Hover Button**  
Okay, this one is a bit more complex. I am using three elements:

* a `<div>` that acts as a container
    
* a `<path>` forming a rectangle and spanning the size of the container
    
* a `<p>` that displays the text
    

I used `whileHover` to detect the hover event. `whileHover` is one of Framer Motion's [*gestures*](https://www.framer.com/docs/gestures/). Gestures allow you to define animation targets for one of several events (like hover, focus, scroll etc.). However, applying the gesture to the `<path>` directly will not work because the `pathLength` is `0` and, therefore, is hard to hover over.

To work around this, I applied `whileHover` to the parent `<div>` element. Motion components **inherit** `initial` and `animate` attributes from their parent components. Thus if we do **not** set those attributes on our `<path>` element, the animation target from `<div>` will be inherited.

Using variants, I defined the animation targets `"hover"` and `"default"`. While hovered, the `pathLength` moves to `1`, the `strokeWidth` increases and the `color` changes to orange.

Although the `<p>` element uses a different variants object, I used the same names for the targets (`"hover"` and `"default"`). Because `<p>` is also a child of our parent `<div>`, it can also inherit the `animate` attribute.

Hovering the parent `<div>` will activate different animations in the text and the path using the same `animate` values. Isn't that cool?

## A quick note on SSR

If you plan on server-side rendering your application, add `strokeDasharray="0 1"` to your motion component. Framer Motion needs JavaScript to measure the total length of the path for most path animations. Because of that, the element is loaded onto the DOM before the animation can hide the element. This causes undesirable flickering on page loads. Setting `strokeDasharray="0 1"` ensures that the path is hidden before the first render.

## Conclusion

We've covered a lot of ground in this article. We discovered how path animations work, how to implement path animations with framer motion, transitions, motion components, variants and some advanced techniques with inheritance. Play around with what you have learned, and share with me what you came up with!

If you want to support me, the best way is to check out my new app at [https://www.covercraft.io/](https://www.covercraft.io/). If you're struggling to find the right words for your cover letter or get tired of re-formulating how great your experience matches the job description, look no further. I appreciate your support!
