The Nuances of React Transition Group
Published onEdit: Since I wrote this post I’ve become one of the maintainers of React Transition Group and we revamped the docs with better examples and added a couple of new features.
React Transition Group is a very useful tool for transitions, but it takes some getting used to because animation itself is tricky. Also, their documentation is currently a bit lacking and could use better examples. (PR in the works.)
In this post I’ll use the acronym “RTG” to refer to React Transition Group.
I’ll assume that you’re familiar with this tool. If not, I suggest you go play with it first, then come back. 🙃
State
Transition
is the most basic component in RTG. Using Render Props it exposes its current state, which you can use to do whatever you want. State can be one of:
entering
entered
exiting
exited
It’s not immediately obvious how to use it correctly, it often takes me a while to figure it out because I’m always trying out new transition strategies. It’s pretty powerful once you get a hang of it.
Timing
Timing is really important when it comes to transitions. Sometimes it’s tricky to know if you’ve done them correctly because they are usually subtle. I often double-check by slowing them down.
I noticed that people using RTG often start the transition too late, even RTG’s official examples demonstrate that mistake:
Notice that the fade-in effect is a little late? This is because opacity is being set to 1 in entered
state, rather than entering
, i.e. transitioning begins when the it’s already supposed to be done. Try it for yourself: change the entering
opacity to 1.
These delays don’t seem important at first, but they can add up to an interface that feels sluggish.
Animating presence
Transitions were a really fun addition to CSS! When used in moderation, they can really improve UX. However, when something needs to appear or disappear from the DOM, it’s not all rainbows and butterflies—this is where RTG comes in handy.
Let’s say that we want to toggle presence of an element with a fading effect. In React terms we would:
- mount it
- apply fade-in styles
- apply fade-out styles
- unmount it
Using RTG’s Transition component we could try changing the opacity based on the transition state:
entering
—opacity: 1
entered
—opacity: 1
exiting
—opacity: 0
exited
—opacity: 0
This is fine for fading out, but fading in won’t work because the component is mounted already in entering
state. This means that the first thing it “sees” will be opacity: 1
, it won’t have an initial opacity: 0
to transition from.
Solution: use CSSTransition
instead.
The reflow hack
Besides applying classes, CSSTransition
contains an important hack that gives us better control over transitions. It mounts a component with a class, which you can use to set the initial style, then it forces a reflow and immediately adds another class, which you can use to define the style you want to transition to.
The Transition
component is platform-agnostic on purpose. If you want to animate CSS styles, you should probably always use CSSTransition
, even if you’re using CSS-in-JS.
Conclusion
I found this out from a timing-related issue (one of many). I wanted to shed more light on what exactly the problem is because this behavior is not documented yet.
I thing RTG sometimes gets a bad rap because of this, but it’s a really useful component. I hope this post helped you understand it better!