React Animations 101: CSS transitions

TL;DR

  • when starting out with animations in react, learn about CSS transitions first and where you can use them
  • there’s no need for ReactCSSTransitionGroup etc., if you don’t need to animate a component when it leaves the DOM (unmount)

How do I get a navigation bar to slide in and slide out using react?

If you were to follow my learning path to get a navigation bar animation working with react, you will end up with a combination of react-motion, ReactCSSTransitionGroup, yet-another-react-animation-lib, a lot of head scratching, reading, more head scratching and an overcomplicated solution just to run this simple animation.

However, for the navigation bar to slide in and out, no external library is needed. CSS transitions are a lot simpler and quicker to write up and can be used to create these types of animations. In this post on react animations 101, we will explore a few examples and why CSS transition are a good fit for the navigation bar.

CSS transitions: a quick introduction

CSS transitions tell the browser that it should animate when a property on an element changes. CSS transitions let you decide which properties to animate (i.e. height, color etc.), how long the animation should run for, and how the transition will run (i.e. either slow at the start: ease-in, or slow at start and end: ease-in-out… ) Instead of relying on a javascript library to create all the animation frames that would be needed to create this animation, the browser will take care of that calculation.

#circle {
transition: transform 500ms ease-in-out;
}

In the above example, the browser will react to any changes in transform for the circle. As soon as the position of the circle is changed the animation starts. It runs for 500ms, with a slow start and slow end(ease-in-out) a timing function. (codepen)

without CSS transitions
with CSS transitions

Examples with React and CSS transitions

How do we use CSS transitions in react?

Progress bar example (Codepen)

Let’s start off with a progress bar first. Whenever we update the progress bar, it shouldn’t jump to its new progress indicator. Instead we want to see a gradual change to the progress bar’s width until it reaches its current progress status.

Progress bar component

In the progress bar component, the width gets changed whenever the progress is updated. A progress of 20% will result in 20% width of the progress bar.

const ProgressBar = ({ progress }) => (
<div id=”progressbar”>
<div className=”progress” style={{ width: progress + ‘%’ }} />
</div>
)

ProgressBar.css

.progressbar .progress {
transition: width 100ms ease-in-out;
}

Without CSS transitions whenever the progress bar updates, it would expand to its new width immediately. Now, every time the width changes, it will transition to the new width in 100ms.

You can explore the progress bar example a bit further in codepen.

  1. Change the length of the animation
  2. Change the timing function (ease-in, linear)
  3. Update the progress bar multiple times before the animation has finished. Does it run smoothly?

Navigation bar sliding in and sliding out

Navigation bar slide in / slide out

Finally let’s get to the navigation bar. If you are used to jQuery animations, you would use the following imperative approach: $(“#navbar”).slideIn(). The difference to jQuery is that react is declarative. i.e. what does the navigation component look like when it is visible. In this case, when the navigation bar is visible the slideIn class is applied, when it is not visible the slideOut class is applied.

The Navigation Bar Component (on codepen)

const Navbar = ({ visible }) => (
<div id="navbar" className={visible ? 'slideIn' : 'slideOut'}>
</div>
)

NavigationBar.css

#navbar {
width: 220px;
transform: translateX(-220px);
transition: transform 400ms ease-in;
}
#navbar.slideIn {
transform: translateX(0);
}
#navbar.slideOut {
transform: translateX(-220px);
}

Codepen example

We use the transform property in CSS to position the navigation bar. A translation of -220px on the x-axis means that it will be completely out of view when the navigation bar is added to a page on the left. .slideIn will move the element back into the page. .slideOut will move the element completely out of view again.

With the CSS transition, we tell the browser whenever there is a change to the transform property (in this case when the element’s position is changed), animate the change over 400ms. And voila, we have a simple sliding navigation bar with a few lines of code.

Why start with CSS transitions?

Easy to write
If there’s no need for any external library, there’s no need to learn yet another API. By just understanding the basic components of CSS transitions, you will be able to accomplish quite a bit.

Interruptible
In the above case, all the animations were interruptible. If you click multiple times on the navigation bar, it will slide in and stop its slide in when you click again. That is we did not have to wait for the animation to complete, before interacting with the app and giving it a new animation.

Performance
Browsers are getting better and better at handling CSS transitions. Specifically if you transition the following properties: transform, opacity. This means that the browser does not have to do a lot of work (no need to repaint other elements). Smooth animations on desktop and mobile are possible.

CSS transitions drawbacks

Browser support
CSS transitions don’t work with older browsers, such as IE 9. The graceful fallback here is that no animation is displayed, but the app will still function.

Advanced animations
More advanced animations such as chaining and staggering gets really difficult or near impossible with only CSS transitions and keyframes. Yes, you will need to use something else then. No silver bullets, not even with CSS ;)

In Summary

Rather than reaching out to the next animation library the next time, check if you can solve it with CSS transitions. You might be surprised that it accomplishes just what you need.

External references:

… but, but what about…

What about unmounting animations? How can I ensure silky smooth animations? When do I need to use react-motion / ReactCSSTransitionGroup and how?

I will explore some of these in upcoming posts. You can keep updated when the next one comes out