Tutorial

How To Animate Page Transitions on a Static GatsbyJS Site

JavaScript

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

Introduction

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 preconfigured with service workers, code splitting, server-side rendering, intelligent image loading, asset optimization, and data prefetching.

GatsbyJS, however, does not come with animated page transitions out of the box. In this tutorial, in order to enhance your front-end design capabilities, you will see a few strategies to add page transitions to your GatsbyJS application, such as using a React transition group and using a transition plugin.

Prerequisites

To get started with this tutorial, you need preliminary knowledge of React.js. This will help you navigate the code we’ll use here.

Step 1 — Creating a New Gatsby App

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

  • 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 will see your Gatsby project live.

GatsbyJS project placeholder in browser

Step 2 — 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 this 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.

Installing 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 entry 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

Configuring GatsbyJS

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:

gatsby-config.js
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 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 will look more like this:

Current project file tree

Now 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:

transition.js
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. We then 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 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 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 before, then 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 following code:

// 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

Now when we run our app, we will 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.

GIF of implemented fading page transitions

Step 3 — Adding Page Transitions with the GatsbyJS Page Transition Plugin

The previous step showed one way to transition pages in GatsbyJS. 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 demonstrate how to add this plugin to our Gatsby app and use it to transition our application’s pages.

Installing Dependencies

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

Configuring GatsbyJS

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 file and update the plugins array with the following code:

gatsby-config.js
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 are:

  • Default transitions
  • Custom Transitions
  • No Transitions
  • Multiple Transitions

Let’s start with the default transition, which is actually the same as what we saw in the last example. To get this transition working in our pages, 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, then 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.

GIF of implemented fading page transitions

You can set your preferred transition duration for your transitions; the higher the number you set, the slower the transition speed.

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 more.

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 behavior 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

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

GIF of implemented sliding page transitions

You can also 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 too many transition types can negatively impact user experience.

If you decide to implement different transitions in different pages, style your PageTransition elements separately and you’ll have different transitions for different pages.

Fixing Scrolling Interactions

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

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

gatsby-browser.js
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
    }

Conclusion

In this tutorial, 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.

You can find the source code for this project at this GitHub repository. Switch between branches for the transition demonstrations.

Creative Commons License