Andy Van Slaars

Andy Van Slaars

moon indicating dark mode
sun indicating light mode

Page transitions with Gatsby and react-spring πŸ“œ ➑️ πŸ“ƒ

November 25, 2018

UPDATE: I’ve migrated to use the Gatsby blog and notes themes for this site and decided that the animated page transitions needed to go as part of that effort. It was a fun experiment, but added nothing to the reading experience. It was a fun experiment, but had to go as part of the overall cleanup.

I’m working on an internal React component library at work. Right before I left for an extended Thanksgiving holiday, I thought it would be fun to add some page transitions to our Gatsby based documentation site for the project. It didn’t go as well as I had hoped and I ended up leaving without page transitions being implemented πŸ˜•.

Since I’m apparently bad at properly using vacation time, this minor, unnecessary piece of functionality was bugging me 🐞. So, instead of putting it out of my mind, I decided to experiment on this site to see if I could work it out. Spoiler alert: I got it working! You may have noticed the subtle fade when you loaded this page. If not, navigating around the site looks like this now:

I’m doing this with react-spring and it was really straightforward to put in place (my original, failed attempt was based on a different approach).

Since this is a Gatsby site and I wanted to transition the content on page changes, I imported Transition from react-spring into my layout component.

import { Transition, config } from "react-spring"

Then I used the Transition component to wrap my content like this:

<Transition
config={config.slow}
keys={location.pathname}
from={{ opacity: 0 }}
enter={{ opacity: 1 }}
leave={{ opacity: 0 }}
>
{() => style => (
<article role="main" style={style}>
{children}
</article>
)}
</Transition>

Transition uses the render prop pattern, so as a child, you pass in a function, that returns a function component. The props for that function component map to updated versions of the { opacity: 0 } object. That value is updated to new values between 0 and 1 at an interval, based on the config={config.slow} prop passed into Transition. So the function component that does the rendering receives a series of values that look like:

{ opacity: 0 }
{ opacity: 0.025651759521222013 }
{ opacity: 0.10005537011441921 }
{ opacity: 0.28246904943682244 }
...
{ opacity: 0.997946502806949 }
{ opacity: 1 }

All that needs to be done to apply those opacity values, is to pass that object along as the style property for the element you want to fade in/out, which in my case is the article element that wraps my markdown content. Using location.pathname as the keys prop for Transition is what triggers the transition to occur on client route changes. Gatsby makes this nice and easy by exposing the location object from @reach/router as a prop on pages and that can be passed to the layout component as a prop. If you’d rather not pass location down, @reach/router also exposes a Location component to easily get access to the location object from wherever you need it.

This transition could be much fancier, and I’m barely scratching the surface on what can be done with react-spring, but it’s a start. Now that the pieces are all in place, it’s just a matter of experimenting a bit with transitioning different values.

P.S. react-spring already has a React hooks API with some more on the way πŸŽ‰πŸ‘!