All right, that’s it for the quick demo!

Let’s see how we can set up some simple route animations!

React setup

Let’s install React with the fantastic Create React App, a simple way to get a React project up and running.

If you haven’t installed Create React App already (if you have, skip this step);

npm install -g create-react-app

Then let’s create our project;

create-react-app animatedroutes && cd animatedroutes

Then let’s install our packages for routes and animation;

yarn add react-router-dom animated react-transition-group

Now open the project in your favourite editor and run;

npm start

Adding React Router

Open your src/index.js file and add BrowserRouter from React

import React from "react";

import ReactDOM from "react-dom";

import { BrowserRouter } from "react-router-dom";

import App from "./App";

import registerServiceWorker from "./registerServiceWorker";

import "./index.css"; ReactDOM.render(

<BrowserRouter>

<App />

</BrowserRouter>,

document.getElementById("root")

); registerServiceWorker();

Then let’s create two components we can render;

First src/Home.js ;

import React, { Component } from "react"; export default class Home extends Component {

render() {

return (

<div className="page">

<h1>Home</h1>

<p>Hello from the home page!</p>

</div>

)

}

}

Then src/Subpage.js ;

import React, { Component } from "react"; export default class Subpage extends Component {

render() {

return (

<div className="page">

<h1>Subpage</h1>

<p>Hello from a sub page!</p>

</div>

)

}

}

Then open src/App.js and change it to this;

import React, { Component } from 'react';

import { Route, Link } from "react-router-dom"; import Home from "./Home";

import Subpage from "./Subpage"; class App extends Component {

render() {

return (

<div className="App">

<div className="TopBar">

<Link to="/">Home</Link>

<Link to="/subpage">Subpage</Link>

</div>

<Route exact path="/" component={Home} />

<Route exact path="/subpage" component={Subpage} />

</div>

);

}

} export default App;

Then remove everything in src/App.css and paste the following in src/index.css ;

html,

body,

#root {

height: 100%;

width: 100%;

} body {

margin: 0;

padding: 0;

font-family: sans-serif;

} .App {

position: relative;

display: flex;

flex-flow: column;

} .TopBar {

position: fixed;

top: 0;

left: 0;

display: flex;

flex-flow: row nowrap;

align-items: center;

width: 100%;

height: 62px;

padding: 0 24px;

} .TopBar a {

margin-right: 18px;

text-decoration: none;

} .animated-page-wrapper {

position: absolute;

top: 62px;

left: 0;

width: 100%;

height: 100%;

} .page {

padding: 0 24px;

}

Okay. You should now be able to navigate between two routes, the homepage and a subpage.

Adding TransitionGroup

Now we’re ready to start animating the routes.

There’s a few things we need to change and add to make this work;

Instead of rendering our routes the normal way, were now going to use the Route render-method to render our component and wrap them in a <TransitionGroup /> .

First import TransitionGroup in your src/App.js component like this;

import TransitionGroup from "react-transition-group/TransitionGroup";

Then we have to add a special function for Transition Group to render a single child. Above class App extends ... in src/App.js , add this function;

const firstChild = props => {

const childrenArray = React.Children.toArray(props.children);

return childrenArray[0] || null;

};

Then remove your routes and replace them with this;

<Route

exact

path="/"

children={({ match, ...rest }) => (

<TransitionGroup component={firstChild}>

{match && <Home {...rest} />}

</TransitionGroup>

)}/>

<Route

path="/subpage"

children={({ match, ...rest }) => (

<TransitionGroup component={firstChild}>

{match && <Subpage {...rest} />}

</TransitionGroup>

)}/>

You now have access to new lifecycle methods such as componentWillAppear() , componentWillEnter() and componentWillLeave() .

Let’s use them to make a Higher Order Component which animates our routes! Now the real fun begins!

Creating our Animated Wrapper and animating with Animated (can I say animated again..?)

Create src/AnimatedWrapper.js and paste in this;

import React, { Component } from "react";

import * as Animated from "animated/lib/targets/react-dom"; const AnimatedWrapper = WrappedComponent => class AnimatedWrapper

extends Component {

constructor(props) {

super(props);

this.state = {

animate: new Animated.Value(0)

};

}

render() {

return (

<Animated.div className="animated-page-wrapper">

<WrappedComponent {...this.props} />

</Animated.div>

);

}

}; export default AnimatedWrapper;

There’s a lot going on here, so I’ll explain a bit.

Were making a component to wrap our route component. It will receive the lifecycle methods from TransitionGroup, which we can use for animation.

We also use Animated to create a value we can use to animate different style properties of the div that wraps our child component.

Let’s add some lifecycle methods to animate our component, and an Animated.template`` to render and/or interpolate our state animation value.

Change src/AnimatedWrapper.js to this;

import React, { Component } from "react";

import * as Animated from "animated/lib/targets/react-dom"; const AnimatedWrapper = WrappedComponent => class AnimatedWrapper

extends Component {

constructor(props) {

super(props);

this.state = {

animate: new Animated.Value(0)

};

}

componentWillAppear(cb) {

Animated.spring(this.state.animate, { toValue: 1 }).start();

cb();

}

componentWillEnter(cb) {

setTimeout(

() => Animated.spring(this.state.animate, { toValue: 1 }).start(),

250

);

cb();

}

componentWillLeave(cb) {

Animated.spring(this.state.animate, { toValue: 0 }).start();

setTimeout(() => cb(), 175);

}

render() {

const style = {

opacity: Animated.template`${this.state.animate}`,

transform: Animated.template`

translate3d(0,${this.state.animate.interpolate({

inputRange: [0, 1],

outputRange: ["12px", "0px"]

})},0)

`

};

return (

<Animated.div style={style} className="animated-page-wrapper">

<WrappedComponent {...this.props} />

</Animated.div>

);

}

};

export default AnimatedWrapper;

Then we have to import it in each of our route components and wrap them like this;

Change src/Home.js to this;

import React, { Component } from "react";

import AnimatedWrapper from "./AnimatedWrapper"; class HomeComponent extends Component {

render() {

return (

<div className="page">

<h1>Home</h1>

<p>Hello from the home page!</p>

</div>

)

}

} const Home = AnimatedWrapper(HomeComponent);

export default Home;

and src/Subpage.js to this;

import React, { Component } from "react";

import AnimatedWrapper from "./AnimatedWrapper"; class SubpageComponent extends Component {

render() {

return (

<div className="page">

<h1>Subpage</h1>

<p>Hello from a sub page!</p>

</div>

)

}

} const Subpage = AnimatedWrapper(SubpageComponent);

export default Subpage;

That’s it! Your routes should now be animating in and out!

Further learning

I recommend reading through the Animated docs (they’re pretty sparse at the moment, the Animated.template`` function we’re using isn’t even documented outside of Github-issues.

You can look at the docs here; http://animatedjs.github.io/interactive-docs/

You can also download the example project living at http://animate.mhaagens.me/

here;

https://github.com/mhaagens/animated_routes_react

Follow me here on Medium or on Twitter for more React tutorials; https://twitter.com/mhaagens