Animating Route Transitions with React Router

August 23, 2017 0 Comments

Animating Route Transitions with React Router

 

 

React Router v4 provides an intuitive, declarative API for building React apps that support navigation. However, there is not much in the docs, stack-overflow, or blogs that explain how to animate between routes when building UIs that require motion.

In this article, we’re going to create a React app with the new React Router (v4) and add some animations when navigating between views. At the end of the tutorial, you will see how to use Higher Order Components (HOC) to compose your components with the React’s CSSTransitionGroupplugin.

Here is a preview of the app we’ll be building:

All the examples for this article are available in Codesandbox.io but in a real project, you will need to set a project with create-react-app at least. Let’s install this tool and setup a new project:

# Install globally
npm install -g create-react-app
#Create a new project
create-react-app router-animations

You need to install two more dependencies; react router, and the plugin for CSS transitions:

yarn add react-router-dom react-addons-css-transition-group

Notice that unlike the previous versions of React Router, we are installing react-router-dom and not react-router. v4 of React Router is platform independent which is why we’re installing react-router-dom which is dependent on react-router package itself and will pull it along in the installation process.

Just to have a less boring UI, copy the replace the index.css and logo.svg with the content found here and here respectively.

We’re all set to start adding routes. Create a pages folder in the src directory. This is where components that resolves to route pages will be added.

The simply has two pages:

  1. Subscribe page: Supposed email subscription page
  2. Thank you page: Thank you page shown after email subscription

Let’s start with the Subscribe page:

Next, add the Thank you page as well:

Apart from the obvious texts in the components, they have one thing in common — SubscribeForm. This is a UI component that just shows the subscription form.

Let’s create that as well:

The component renders a text input and a subscribe button if the thanks property is not defined. This property is defined on the SubscribeForm component only on the ThankYou page. If it is defined, the component just renders a button that says “Try Again”.

Depending on the page in view, the buttons are supposed to take us to the another page using the history.push() method when clicked.

With the pages set to be rendered, let’s define their routes and what paths they should be rendered to. Update your App component with the following:

Here is what’s going on the component above:

  • Routes are configured using Route from the react-router-dom library which we imported.
  • Route is just a component and receives properties like path to specify what location the component will be mounted. It also receives component which is the component that should be mounted when the specified path is matched

Now to make the router actually work with React, we need to wrap the App component with the BrowserRouter component:

// ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
registerServiceWorker();

The only change we made here is that we import the BrowserRouter and aliased it as Router (just for brevity), and then we wrap the App component with Router:

<Router>
<App />
</Router>

You should have some navigation on screen when you open the app:

Higher Order Components (HOC) allows you to extend your React components. They are basically functions that takes a component and returns a component. As simple as that.

They become handy when you have two components sharing the same features leading to repeated logic. For example Subscribe page which is a component might have a logo and a white background. Same as the Thank You page. To add a logo and a white background to both components, we would need to write the feature in both component classes. Instead, what we can do is use a HOC that implements the shared feature, and call the HOC with the Subscribe or Thank You.

An example will make more sense. Create a component called PageShell in the components directory:

// ./src/components/PageShell.js
import React from 'react';
import logo from '../logo.svg';
const PageShell = Page => { 
return props => {/* [1]/}
<div className="page">
<img src={logo} alt="" />
<Page {...props} /> {/
[2]*/}
</div>;
};
export default PageShell;
  • The component is a function which is designed to receive a component (Page) as argument.
  • It wraps the received component with some fancy DOM content[1] and returns, passes in its props[2] and returns the component as a function.
  • The expectation is that PageShell will be used like this: PageShell(PageComponent)

Now let’s wrap the page components in App with PageShell:

The only difference is that before passing Subscribe and ThankYou to the component property, we first wrap it in PageShell leading to this new look:

To achieve a transitions, each of the components needs to be wrapped with the ReactCSSTransitionGroup.

<ReactCSSTransitionGroup
transitionAppear={true}
transitionAppearTimeout={600}
transitionEnterTimeout={600}
transitionLeaveTimeout={200}
transitionName="SlideIn"
>
<Page />
</ReactCSSTransitionGroup>

The transitionName maps to a CSS class that defines what happens at every phase of the animation. Here is a small and truncated snippet from index.css:

.SlideIn-appear {
transform: translateX(30px);
opacity: 0;
}
.SlideIn-appear.SlideIn-appear-active {
opacity: 1;
transform: translateX(0);;
transition: all 0.6s linear;
}
.SlideIn-enter {
opacity: 0;
transform: translateX(30px);
}
.SlideIn-enter.SlideIn-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 0.2s linear 0.4s;
}
.SlideIn-leave {
opacity: 1.0;
transform: translateX(0);
}
.SlideIn-leave.SlideIn-leave-active {
opacity: 0;
position: absolute;
width: 100%;
transform: translateX(-30px);
transition: all 0.2s linear;
}

ReactCSSTransitionGroup maps to each of these phases (appear, enter, and leave) and applies what ever transition effect is available on the CSS class definition. You can learn more in the React Docs on animation add-ons.

If we were building a large application, you will end up getting yourself messed up with repeated code when trying to wrap each of these components with ReactCSSTransitionGroup. With HOC, you can use ReactCSSTransitionGroup in the Page Shell once and wrap every components that needs the ReactCSSTransitionGroup with the page shell:

That’s all you need to get transitions up and going in your routes:

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.

It’s tough to keep up-to-date on front-end dev. Join our weekly mailing list to learn about new tools, libraries and best practices that will help you build better apps:


Tag cloud