Scotch.io's Real-World Vue Book is launching! Get 50% Off
We're live-coding on YouTube! Join us!

Animated Page Transitions in Gatsby Websites

Animated Page Transitions in Gatsby Websites

GatsbyJS is a React-based static site generator powered by GraphQL. It makes it possible for you to code and develop your site, while Gatsby transforms it into a directory with a single HTML file with all your static assets. On it’s own Gatsby is fast and comes already preconfigured with service workers, code splitting, server-side rendering, intelligent image loading, asset optimization, and data prefetching. But you already know all of that so let’s get to why we are here.

Sometimes, all of what we just mentioned is still not enough to give your users an amazing experience while visiting your site. At the moment, users demand perfection, and achieving it is almost impossible and comes at incredible costs. In this tutorial, we are going to look at a few through which we can add page transitions to our Gatsby applications to make the site a little bit more interesting and fun.

So far, Gatsby does not do transitions out of the box, hence, the need to take a look at the few things we can do to achieve page transitions in Gatsby applications.

Prerequisites

To get started with this tutorial, you need preliminary knowledge of React.js. This will help you get around with the codes we’ll use here. How best to demonstrate and explain how something works than to actually build it? There’s no better way, as a result, we’ll set up a new Gatsby application and with it, explain the few ways to animate our pages.

Create new Gatsby app

If you haven’t already, go ahead and install the Gatsby CLI tool with the command below:

npm install -g gatsby-cli

With that installed, we can now run Gatsby specific commands to create, build and deploy our applications. To create a new Gatsby project, open a terminal window in your preferred directory and run:

gatsby new transitions-demo

This will create a new Gatsby project for you. You can navigate into the project folder and run the server command to launch the development server.

cd transitions-demo && gatsby develop

This will launch the project on localhost:8000, if you open that up in your browser, you should see your Gatsby project live!

Essential Reading: Learn React from Scratch! (2019 Edition)

Option 1: Adding page transitions with React transition group

The React transition group is the first and most popular way of adding transitions to Gatsby applications. It is worthy to note that it is not an animation library by design so it doesn’t have the ability to animate styles by itself. Instead it exposes transition stages, manages classes and group elements and manipulates the DOM in useful ways, making the implementation of actual visual transitions much easier.

Install the dependencies

Let’s go ahead and install it and demonstrate how we can leverage it to transition between the pages in our application.

npm install react-transition-group

It will monitor the entery and exit state of elements in the DOM and then apply transitions to them accordingly with respect to our custom transition styles.

The next package we’ll need to install is the Gatsby layout plugin. It gives us the ability to provide a location property required for transitions to work and injects our layout on every page.

npm install gatsby-plugin-layout

Configure Gatsby config

The first we’ll need to do after installing the dependencies is to configure the gatsby-config.js file to use the layout we just installed. Open up the file and add the layout plugin in the plugins array:

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `My new Gatsby site`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`, 
    {
      resolve: `gatsby-plugin-layout`,
      options: {
        component: require.resolve(`./src/layouts/index.js`),
      },
    },
  ],
}

For this plugin to work correctly, we need to move the layout file from inside the components folder to the root of our project file src folder and rename the layout.js file to index.js. Then within the components folder, create a new file called transition.js to host our transition logic implementations. Once done, our application structure should look more like this.

Great, now that we have sorted that out, let’s go ahead and implement these transitions in our pages. Open the transition.js file we created in the components folder and update it with this code.

import React from "react"
import {
  TransitionGroup,
  Transition as ReactTransition,
} from "react-transition-group"
const timeout = 500
const getTransitionStyles = {
  entering: {
    position: `absolute`,
    opacity: 0,
  },
  entered: {
    transition: `opacity ${timeout}ms ease-in-out`,
    opacity: 1,
  },
  exiting: {
    transition: `opacity ${timeout}ms ease-in-out`,
    opacity: 0,
  },
}
class Transition extends React.PureComponent {
  render() {
    const { children, location } = this.props
    return (
      <TransitionGroup>
        <ReactTransition
          key={location.pathname}
          timeout={{
            enter: timeout,
            exit: timeout,
          }}
        >
          {status => (
            <div
              style={{
                ...getTransitionStyles[status],
              }}
            >
              {children}
            </div>
          )}
        </ReactTransition>
      </TransitionGroup>
    )
  }
}
export default Transition

Let’s walk through the code in bits. First, we imported TransitionGroup, and ReactTransition from the react-transition-group package we installed earlier. TransitionGroup helps us manage the mounting and unmounting of components in the DOM while the ReactTransition tracks the entry and exit states of elements passed to it.

Next, we declared a timeout variable that will be responsible for our animation durations. Then we defined the getTransitionStyles object that will contain the CSS styles for our animation. Finally, we destructured children and location from props for ease of usage.

Notice that ReactTransition accepts a key *key*``={``*location.*``pathname} which is how it tracks the entry and exit of elements in the DOM. With that, we apply the styles depending on the status of the page/element (entering, exiting, entered) in the DOM.

That’s it for our transition component. Next thing we need to do is to actually use this component to wrap all our pages. The way it works is that we can wrap all the pages in our application as children in the layout component and then wrap the entire layout component with the transition component.

This way, the location prop we will defined in the the transition component will take effect and animate our pages accordingly as they enter and exit the DOM. Open the index.js file in the layouts folder and update it with the code below.

//src/layouts/index.js

import React from "react"
import PropTypes from "prop-types"
import { StaticQuery, graphql } from "gatsby"
import Header from "../components/header"
import "./layout.css"
import Transition from '../components/transition'

const Layout = ({ children, location }) => (
  <StaticQuery
    query={graphql`query SiteTitleQuery {
        site {
          siteMetadata {
            title
          }
        }
      }`}
    render={data => (
      <>
        <Header siteTitle={data.site.siteMetadata.title} />
        <div
          style={{
            margin: `0 auto`,
            maxWidth: 960,
            padding: `0px 1.0875rem 1.45rem`,
            paddingTop: 0,
          }}
        >
        <Transition location = {location}>
          {children}
        </Transition>
        </div>
      </>
    )}
  />
)
Layout.propTypes = {
  children: PropTypes.node.isRequired,
}
export default Layout

Here, we imported the Transition component that we created a moment ago, then like we explained earlier, we used it to wrap the children which represents all the pages in our app. We also passed the location prop to the Transition component to track the locations of the pages as they enter and exit the DOM.

Finally, let’s modify the index.js file in our pages folder to add more pages on the homepage so we can test our transitions. Open it and update it with the code below:

// src/pages/index.js

import React from "react"
import { Link } from "gatsby"
import SEO from "../components/seo"

const IndexPage = () => (
  <div>
    <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
     <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>

    <Link to="/blog/">Go to my blog</Link> <br />
    <Link to="/about/">Go to my about page</Link> <br />
    <Link to="/contact/">Go to my contact page</Link> <br />
    <Link to="/404/">404</Link> <br/>
    <Link to="/page-2/">Go to page 2</Link>
  </div>
)
export default IndexPage

Great, now when we run our app, we should see all our pages rendered on the homepage and when we click on any of them, it will transition into the new page as expected.

Option 2: Adding page transitions with Gatsby page transition plugin

What we’ve seen just now is one way to transition pages in Gatsby. Let’s take a look at an entirely different way to do this with a page transition plugin. The gatsby-plugin-page-transitions is a plugin that allows you to declaratively add page transitions, as well as specify custom page transitions for any page on your project.

Just like in the last example, we’ll install the necessary dependencies and try to demonstrate how to add this plugin to our Gatsby app and use it to transition our application’s pages.

Install dependencies

npm install --save gatsby-plugin-page-transitions

Configure Gatsby config

Having installed the plugin, let’s add it to our project through the Gatsby config file in the root of our project. Open the gatsby-config.js and update the plugins array with the code below:

plugins: [
`gatsby-plugin-react-helmet`, 
`gatsby-plugin-page-transitions`,
{
  resolve: 'gatsby-plugin-page-transitions',
  options: {
    transitionTime: 500
  }
}
]

Transition types

The gatsby-plugin-page-transitions plugin _**_provides us with different transition types. There’s

  • Default transition
  • Custom Transition
  • No Transition and
  • Multiple Transitions

Default transition Let’s start with the default transition, which is actually the same as what we saw in the last example. All we need to do to get this transition working in our pages is wrap all the pages where we need the transition with the plugin.

To demonstrate this, let’s import the plugin into our Index page and a few other pages in our app and run the app.

import React from "react"
import { Link } from "gatsby"
import SEO from "../components/seo"
import Header from "../components/header"
import PageTransition from 'gatsby-plugin-page-transitions';

const IndexPage = () => (
  <PageTransition>
    <Header siteTitle="Gatsby Default Starter" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>

    <Link to="/blog/">Go to my blog</Link> <br />
    <Link to="/about/">Go to my about page</Link> <br />
    <Link to="/contact/">Go to my contact page</Link> <br />
  </PageTransition>
)
export default IndexPage

Now when we run the app, we get exactly the same transitions we had in our last example since we are using the same transition time of 500ms.

The transition seems a bit too fast according to the demo above, that was intended. The reason is, i wanted to point out the fact that you can set your preferred transition duration for your transitions, the higher the number you set, the slower the transition speed.

Custom transition The custom transition type gives you the flexibility to determine how you want your pages to transition. The default transition style only performs a kind of reveal animation to transition in any new page, however, there’s so much more you can do with the plugin. For instance you can decide to transition-in pages from the side of the browser, or from the top, zoom pages in and out, swirl pages across the screen and so on.

To do this, you define your custom transition styles with CSS and pass it into the PageTransition element as prop. Here’s how we can change the default transition behaviour in the Index and About pages to a custom sliding transition.

import React from "react"
import { Link } from "gatsby"
import Header from "../components/header"
import PageTransition from 'gatsby-plugin-page-transitions';

const IndexPage = () => (
  <PageTransition
    defaultStyle={{
      transition: 'left 500ms cubic-bezier(0.47, 0, 0.75, 0.72)',
      left: '100%',
      position: 'absolute',
      width: '100%',
    }}
    transitionStyles={{
      entering: { left: '0%' },
      entered: { left: '0%' },
      exiting: { left: '100%' },
    }}
    transitionTime={500}
  >
    <Header siteTitle="Gatsby Default Starter" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>

    <Link to="/blog/">Go to my blog</Link> <br /><br></br>
    <Link to="/about/">Go to my about page</Link> <br /><br></br>
    <Link to="/contact/">Go to my contact page</Link> <br />
  </PageTransition>
)
export default IndexPage

Also make the same update in your about page and run the app again. We should now get the slide transitions working in both pages.

Mixed transition

This should be pretty obvious but yea, you can have more than one transition type in your projects. You can have a different transition for every single page in your app if you so please. The only concern here is that sometimes doing a bit too much is bad for user experience.

It’s always best to keep it neat and simple, however if for any reason you decide to implement different transitions in different pages, all you need to do is style your PageTransition elements separately and boom! you’ll have different transitions for different pages.

Final thoughts

If your application’s pages are filled with contents that will require your users to scroll all the way down to the bottom of your pages, you may experience some weird behaviours when your pages transition in and out. Why? Because by default, when there are enough elements on the page, it jumps to the top of the page first before triggering transitions.

What we can do to fix this is set a time out for the transition so it can wait for the transition to be executed before the page scrolls to the top. Here’s how we do that, open the gatsby-browser.js file in the application’s root directory and update it with this code:

const transitionDelay = 500

exports.shouldUpdateScroll = ({
    routerProps: { location },
    getSavedScrollPosition,
}) => {
    if (location.action === 'PUSH') {
        window.setTimeout(() => window.scrollTo(0, 0), transitionDelay)
    } else {
        const savedPosition = getSavedScrollPosition(location)
        window.setTimeout(
            () => window.scrollTo(...(savedPosition || [0, 0])),
            transitionDelay
        )
    }
    return false
    }

In this post, we have gone through the various ways you can implement page transitions in your Gatsby projects. In the process we looked at how to set up a new Gatsby project from scratch, how to install the necessary dependencies for the transitions and how to use them to build pages.

The two approaches discussed here is not final, maybe there is a better way or a better tool out there that we can use to achieve most of this functionalities. Feel free to mention them so we can all learn and build better products for our users. You can find the source code for this project here. Switch between branches for the transition demonstrations.

Like this article? Follow @codebeast on Twitter