Staggered Animations in React

Param Singh
8 min readSep 8, 2020

--

Adding the right amount of animations to you web app is like adding lime to your guacamole 🥑, both are delightful. Ask a UX designer, how imperative is animation to apps. And, considering we all have been using Android and ios devices for so long now, it’s natural to bring that rich user experience to your web apps too.

Agenda

In this article, we’ll be learning about

  • CSS animations & transitions
  • Animations & keyframes in styled-components
  • React Transition Group — Transition, CSSTransition, TransitionGroup
  • Staggered List animations

CSS 3 Animations Refresher

There are two ways to implement animation in CSS.

1. Transitions

I sure, we all are familiar with this basic transition on hover of an HTML element.

On an event like mouse hover, we’re changing one of the css property viz background-color and the rules about duration, easing, delay are taken from transition property.

2. Animations

While, transition is useful for quick cases, like a background color change or translating, scaling, rotating an element from one state to another. If we want to gain a finer control on the whole timeline of this Tweening, we would have to use keyframes and animation css properties as:

Here, we’re mutating the scale and color property for this svg heart. Notice, how inside keyframes, throughout the journey from 0 to 100%, we have added points or stops to gain finer control for the tweening.

And, here are a bunch of animation css properties to control the animation behaviour. We’re running infinite iterations of one keyframe and alternating the animation direction after each iteration.

Styled Components Animation

Let’s come back to our CSS in JS realm and understand how we can perform the same in styled-components.

Simply, change the @keyframes in css to keyframes from styled-component and refer it as a dynamic variable inside the styled component style. Rest, everything is usual.

The benefit here is that you can perform animations based on some conditions like when some state variable becomes true as

Or else, in case of vanilla js, you accomplish the same by adding a class to the element classList as

React Transition Group

The above kind of animations we discussed so far are either triggered by default when the html element first appears on screen or when some javascript event happens like button click or ajax call completion.

If talk more in the jargon of React components lifecycle which has three phases — mounted, updated and unmounted, we would want to apply animations when the components are mounted and when the component is about to leave.
Remember the old jquery days?

JQuery snippet

Notice, how we perform fadeOutUp animation during mouseleave by delaying or buying time for display: none by 200ms. The reasoning is simple,

You can’t perform animation on an already unmounted/disappeared element.

Hence, in our React apps where changes are so dynamically in reaction to dom events or timers or network requests, we need a way to defer the unmounting of the component till our exit animation is done.

React Transition Group provides a sophisticated mechanism to identify the mounting and unmounting states of the components for us to tap into them and adapt either through CSS or JS.

It’s a separate package, that needs to be installed like this

yarn add react-transition-group

Lets look at two of it exports:

1. Transition

Transition Component accepts either a function child (render prop pattern) or simple a React Element. The function has status as its only argument which represents the state in which the transition is currently in.

It can have four values as follow:

  • entering
  • entered
  • exiting
  • exited

Following are the props of the Transition component:

  • in : in prop takes in a boolean which determines the transition status. When it goes true, the status becomes entering or exiting when false.
  • timeout: It takes either a number or an object as { enter: 500, exit: 300 } for more fine grain control on duration of the entering and exit phase. For eg: here, entering phase will change to entered after 500ms and exiting to exited in 300ms.
  • mountOnEnter: By default the children of Transition are also mounted along with it. So, the <Box /> will be visible by default. In order to change this behaviour, for lazily mounting the Box when in prop gets set to true, we can set mountOnEnter true.
  • unmountOnExit: Similar to mountOnEnter, when the in prop is false and the status changes to exited after the exit timeout specified, the child is not unmounted by default. To change this, we can set it true.
  • appear: This is an interesting one. On initial mount, the transition status doesn’t assume as entering, regardless of whether the value of in is true or false. Hence if you have any animations applied listening to state === “entering”, they won’t execute. Setting appear to true will make the status entering even on the initial mount.

Next step now is to listen to these state prop changes in our styled-component viz Box here.

Here, we’re listening to the state changes for entering, entered and exiting.

For entering an entered, we’re applying the boxEnterAnimation.
We have to make sure that our animation-duration matches the timeout property of the <Transition timeout={3000} /> component in order to finish the animation before the status changes.

Here, I’ve kept the animation changes for status === “entered” because of animation-fill-mode or else the box would translate back to zero.

When, the box is about to exit, i.e when the in prop evaluates to false. exiting state is triggered and we perform the boxExitAnimation in a window of 3 seconds before the component unmounts.

Note: You can perform exiting animations on already unmounted element. It means that if you have something like this 👎

then, please don’t expect the exit animation to run at all, because the element would simply disappear or gets unmounted.

Feel free to play around with the codesandbox above.

2. CSSTransition

Lets look at a little less Javascripty approach for handling the transitions using CSS classes. Built on top of Transition component, CSSTransition doesn’t take a function as its child, rather it applies some CSS classes on its child, through which we can apply animations. Usage is simple as

And, we have to add some css classes for our Box to respond to as

Here, we have 3 x 3 set of css classes, each of the [appear, enter, exit] can have three variants as

[   appear   appear-active   appear-done   enter    enter-active    enter-done   exit     exit-active     exit-done]

The first column one are applied just before entering in that given phase. active means the phase is happening an done means it has finished. Also, we can pass classNames prop as a string to <CSSTransition classNames="box" />, all of these class will get prefixed with box as box-appear, box-appear-active, box-appear-done and so on.

Note: The good thing here is that, we get this extra appear class here, it very beneficial when you want to identify and separate your mount animations from enter animations. This is not possible very easily in the previous Transition component.

List Animations — React Transition Group

Lets accomplish our paramount list animation using all the knowledge gained so far and choosing the right component among CSSTransition and Transition for the right need.

We need to achieve the following requirements:

  1. Slideup animation when all the children mount with a stagger of 100ms.
  2. SlideIn from left animation when a new child gets added in the list.
  3. SlideOut to right animation when an existing child gets removed from the list.

When, multiple dynamic components are involved, it might become hard to keep state of each ones in prop. This is where ReactTransitionGroup comes very handy.

Here, the appear prop on TransitionGroup gets passed on as it is to all the children CSSTransition components which means they all now respect on mount animations which we can use to implement our slideup on load animation as

The trick for stagger animation is by delaying the animation by a certain amount gradually. For this gradual delay, we can use the item position inside the array as a delay metric. Hence, animation-delay becomes a multiple of array index for a given item with 100ms as per above example.

Index     Delay    Duration   Total Time (Delay + Duration)[0]       0        500        500
[1] 100 500 600
[2] 200 500 700
[3] 300 500 800
[4] 400 500 900
...

Hence, in order to fully accommodate time for all the elements transitions, we have to specify the timeout prop accordingly for the enter phase as

And That’s it! The on mount animation works well with all this maths and enter and exit animations are listening to the hidden in prop controlled by the parent TransitionGroup component.

Here’s the complete codesandbox

Summary

Congrats. We just learnt effectively about the latest ways of incorporating animations in our react apps using styled-components. Hence, there’s no excuse for not integrating animations in your next project or an existing one. Animations are very important for a great User experience. So, let’s make web apps offer as rich experience as the native apps but always use optimum amount of animations after consulting the designer in your team.

References

Please support by subscribing to my Youtube free React course series.

Thank You. Happy coding 🤓

--

--

Param Singh

Senior Front-end Engineer at AWS, Ex-Revolut, Flipkart. JS enthusiast. Cynophile. Environmentalist