DevelopmentGatsbyJS | 7 Min Read

How to Add Page Transition Animations to GatsbyJS Using Framer Motion

Framer Motion is a powerful JavaScript package for adding animations to a website. Here's how to pair it with GatsbyJS to do page transition animations.

While redesigning my website I decided to add page transition animations to improve the user experience. In turn this led me down the rabbit hole of web-based animations, of which I came back with the idea of using Framer Motion.

Framer Motion, for the uninitiated, is a JavaScript animation library that has many powerful features. In fact, it has so many you could make an entire course on it. But, that's not what we're covering today.

Today, we are going to be looking at using the `AnimatePresence` component. And, more specifically how it can help us with page transition animations in GatsbyJS.

Before we jump in, let's take a look at what we'll be covering:

Page Transitions Example

Let's get started.

AnimatePresence

React itself has no lifecycle method for:

  • Notifying components when they're going to be unmounted.
  • Allows for the defering of the unmounting until an operation is complete.

This is what `AnimatePresence` aims to solve.

By wrapping all our components in an `AnimatePresence` component, we can provide exit animations to components that will be completed once a component is going to be unmounted.

In other words, this means that if we remove a component from the DOM tree, it will fire its exit animation, wait for it to complete and then be removed. This is unlike normal React where it would be removed without waiting for the animation to complete.

Read more about AnimatePresence here.

exitBeforeEnter

While `AnimatePresence` provides the capabilities for us to use exit animations. If we want to ensure the exit animation of the leaving component is complete before the entering component starts animating, we need to add a prop.

This prop is `exitBeforeEnter`, read more here.

`exitBeforeEnter` forces only one component to render at a given moment. This means that the exit animation of the leaving component will complete before the entering component starts animating.

With the theory covered, let's jump into some code.

Page Transition Animations with GatsbyJS

As mentioned earlier to use `AnimatePresence` we need to wrap all our components in it. For page transitions, this means that we need to wrap each page in it.

This is easy to do in GatsbyJS.

With GatsbyJS, when you create a new file in the pages directory it will automatically become a new route on the website to navigate to.

For example, create a file called `blog.js` it will be available at the URL `yourdomain.com/blog`, pretty nifty.

A good method of ensuring each page looks the same and shares the same common elements is to wrap each page in a `Layout` component.

This way we can add our common components and styles to one component and they'll apply to each page automatically.

This also provides us with a great place to add our `AnimatePresence` component.

If we went into our `Layout` component and added `AnimatePresence` like so:

1export default function Layout({ children, path }) {
2 return (
3 <>
4 <Nav path={path} />
5 <AnimatePresence exitBeforeEnter>
6 <main>{children}</main>
7 </AnimatePresence>
8 <Footer />
9 </>
10 );
11}
jsx

You would think this would now allow us to use exit animations on our pages but we can't... Why?!?

The reason this doesn't work is that although every page shares the `Layout` component, it doesn't stay mounted all the time.

Because of how GatsbyJS works it actually unmounts and remounts the `Layout` component every time you change a page. In effect, this means that each time you change the page you're getting a new `AnimatePresence` component that has no memory of the previous components.

For `AnimatePresence` to be able to monitor each component as they mount and unmount, we need to keep it mounted at all times.

Don't worry we have a solution for this.

wrapPageElement

Gatsby has exposed several APIs in the `gatsby-browser.js` file for us to use. One of these is `wrapPageElement`, which solves our problem perfectly.

The `wrapPageElement` API allows us to wrap every page in a component like the `Layout` component from before. But, this time `wrapPageElement` prevents the component from being unmounted on page changes.

So we can add our `AnimatePresence` component here and because it won't unmount like before, it can monitor each mount and unmount.

Here's what it looks like:

1export function wrapPageElement({ element, props }) {
2 return <AnimatePresence exitBeforeEnter>{element}</AnimatePresence>;
3}
jsx

With this in place, we are now free to add entry and exit animations to our pages, let's do that now.

Read more on Gatsby Browser APIs here.

Adding our Animations

Because we now have `AnimatePresence` wrapping each of our pages, the only thing we have to do is provide the animation.

Going back to our `Layout` component from earlier, we can substitute out our original `main` element for a `motion.main` element. This allows us to apply animations to the element.

For our page transition animation to work smoothly, we need to add 5 props to our new `motion.main` element, these are:

  • key
  • initial
  • animate
  • exit
  • transition

Let's cover each of these now.

Key

Like normal React when you create components by mapping over data you need to add a `key` prop to them. This is for React to keep track of each of the components and aid in unmounting them as required.

The same goes for Framer Motion. So it knows when to fire an exit or entry animation, it needs to be able to keep track of each animated component. This is done by providing a unique `key` prop to each element.

Initial

The `initial` prop is the state the component will start in when it is first mounted.

In our case, I want the component to fade in when it's entering. So we set the `initial` prop to `initial={{ opacity: 0 }}`.

Animate

The `animate` prop is the state of the component that it will animate to from the state defined in the `initial` prop.

So for us to complete the fade-in animation we want, we set the `animate` prop to be: `animate={{ opacity: 1 }}`.

This means that when the component first mounts, it starts at `opacity: 0` and then works up to `opacity: 1`.

Exit

When the component unmounts it needs a state to animate to from the state we defined in the `animate` prop. This is what we define in the `exit` prop.

For the exit animation, I want the component to fade out again. So it starts at `opacity: 1` as defined by the `animate` prop and it will finish at `opacity: 0`, defined in the `exit` prop like `exit={{ opacity: 0 }}`

Transition

Finally, we have the `transition` prop. This prop is the settings of the entire animation. Here, we can define the type of animation, the duration and a bunch of other things.

I won't cover all the settings here but the settings I used for my animation was:

1transition={{
2 type: 'spring',
3 mass: 0.35,
4 stiffness: 75,
5 duration: 0.3,
6}}
jsx

If you're interested you can read about the `transition` prop here.

Finished Component

That's all the props covered for our animation, let's take a look at the finished component:

1export default function Layout({ children, path }) {
2 return (
3 <>
4 <Nav path={path} />
5 <motion.main
6 key={path}
7 initial={{ opacity: 0 }}
8 animate={{ opacity: 1 }}
9 exit={{ opacity: 0 }}
10 transition={{
11 type: "spring",
12 mass: 0.35,
13 stiffness: 75,
14 duration: 0.3,
15 }}
16 >
17 {children}
18 </motion.main>
19 <Footer />
20 </>
21 );
22}
jsx

We these props now added in, our page transition animations should now be working.

Summing Up

As with many things; once you know-how; it's not too difficult. Even so, adding page transition animations with Framer Motion was a lot less challenging than I expected it to be.

If for some reason these steps did not work for you, please feel free to DM me on Twitter and I'll be happy to help.

If you found this post helpful, I would greatly appreciate it if you could share this post with others who you think may find it useful.

If you want to support me and see more of my content, please consider following me on Twitter where I post more daily web development content.

Until next time, thanks for reading. 😊



Content

Latest Blog Posts

Below is my latest blog post and a link to all of my posts.

View All Posts

Content

Latest Video

Here is my YouTube Channel and latest video for your enjoyment.

View All Videos
AWS Bedrock Text and Image Generation Tutorial (AWS SDK)

AWS Bedrock Text and Image Generation Tutorial (AWS SDK)

Contact

Join My Newsletter

Subscribe to my weekly newsletter by filling in the form.

Get my latest content every week and 0 spam!