O transitions in React, where art thou

Eric SK. Cheung
3 min readDec 1, 2020

// Manual alternative to react-transition-group

CSS is where it’s at. Vanilla HTML + CSS + Javascript can do probably everything you can imagine for a web page. But once you throw in frameworks like React, well, there’s unforeseen consequences.

One of the first conflicts I immediately discovered when using React is animation transitions suddenly go missing when you conditionally render.

transition, where’d you go?

transition: 600ms ease-in-out will do absolutely nothing for your element if it is being conditionally rendered through React’s best practices.

return (
<button onClick={() => setState(!state}>CLICK!</button>
{state && <div className="example">Element with Transition</div>}
)

Here, ‘Element with Transition’ will not have a transition effect. This is because the element doesn’t exist when the page first loads, so neither does the transition, and remounting will not bring it back.

Damn you, DAN!

So what do you do? Well, people have put together an entire library for this problem, called react-transition-group which allows you to control the transitions and effects for every step of the mounting and unmounting process.

This seems great because you can specify everything you need, except for the fact that for every element you want to transition, you have to write 4 CSS classes.

Literally, every step has to be defined with a class:

.example-enter {
opacity: 0.01;
}

.example-enter.example-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}

.example-leave {
opacity: 1;
}

.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}

If you’re fine with that, then there you go. But I was thoroughly annoyed the first time I used it, and vowed to walk to the ends of the Earth in order to find another way.

One method I usually turn to is to collaborate between React’s state with CSS classes. Instead of using state to conditionally render the element, use the state to conditionally decide the CSS class.

return (
<button onClick={() => setState(!state)}>CLICK!</button>
<div className={state ? "open":"close"}>Element with Transition</div>
)

Here, instead of having state decide the existence of the entire element, I am using state to decide the CSS class, switching between a class that is invisible to a class that is visible.

To make this element fade-in, simply do this:

.close {
opacity: 0;
transition: 400ms ease-in-out;
}
.open {
opacity: 1;
transition: 400ms ease-in-out;
}

Or if you want to make the element expand like a drop down, you can:

.close {
height: 0;
transition: 400ms ease-in-out;
}
.open {
height: 500px;
transition: 400ms ease-in-out;
}

Just like that, you save time on explicitly writing 4 CSS classes for an element, and skip the added baggage of an npm library.

--

--