Staggered Animations in React
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?
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 becomesentering
orexiting
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 whenin
prop gets set to true, we can setmountOnEnter
true.unmountOnExit
: Similar tomountOnEnter
, when thein
prop is false and the status changes toexited
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 asentering
, regardless of whether the value ofin
is true or false. Hence if you have any animations applied listening tostate === “entering”
, they won’t execute. Settingappear
to true will make the statusentering
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:
- Slideup animation when all the children mount with a stagger of 100ms.
- SlideIn from left animation when a new child gets added in the list.
- 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 🤓