If you’re used to animating with CSS, Velocity.js feels like cheating.

Like so many, we had long since given up jQuery animations for CSS transitions (when we were lucky) and keyframes (when we weren’t). We hacked in setTimeouts that added animation-triggering classes post-render, we took care not to get flummoxed by bubbling “animationEnd” events, and we animated max-height instead of height, hoping we never had more than 9999px of text to show.

We did it because it was performant. Hardware-accelerated or whatever. It was a pain, but isn’t good performance supposed to be a pain? Eventually, the level of effort became self-justifying, and the conventional wisdom around animation took hold: “jQuery (and by extension, JavaScript) might be easy, but it’s too slow. CSS is hard and fast.”

And we were proud to take the hard route.

Velocity.js, in these terms, is cheating. It is, quite literally, a return to jQuery: Velocity hooks into $ and its velocity method is API-compatible with that scoundrel animate. The difference, as explained by Velocity creator Julian Shapiro, is in a few little tricks: a single timer loop and rigorously correct ordering of DOM operations, which combined have a night-and-day effect on performance. Suddenly, JavaScript-controlled animations are possible, and with them a variety of features you can build when you have a programming language at your disposal: custom tweening (including spring physics), interruptibility, chaining, workarounds for implicit heights, and reliable completion callbacks — not to mention Velocity’s UI Pack of ready-made effects.

Our team embraced Velocity in the same iconoclastic spirit with which we embraced React. Here was something that eliminated a giant engineering headache around animations, and we were eager to adopt it to easily bring additional fun and liveliness into our pages.

That being said, React and animation don’t actually play well together at first. There’s an impedance mis-match: React’s power stems from abstracting away the state transitions in your UIs (it does them quickly and correctly, enabling the declarative paradigm we’ve come to love), while animations live entirely within those state transitions.

The CSS transition properties modeled this pretty well: “whenever this other property is changed, apply that change gently.” The animations are declared, the transitions are derived. Keyframes are less successful: “when this is applied, set these properties to these specific values over time.” These semantics lead to CSS keyframe animations being applied at undesirable times (such as initial render), always from the very beginning, and without any means of dynamically modifying them.

To integrate smoothly with React’s declarative nature, we’ll need our animation behavior to be fully described by the output of our render methods: a desired end state.

Happily, Velocity is set up to give us just what we need. If you start a Velocity animation on an element, you know it will eventually come to your desired end state[1]. But unlike, say, CSS keyframes, it can be stopped on a dime, or started from the element’s current appearance, taking a smooth, direct path to the new animation’s conclusion[2]. Think of it like React’s reconciliation, just in (relatively) slow motion.

Our Velocity / React integration can then follow this simple algorithm:

Initially, an animated component will immediately appear as it would at the end of its given animation (at least by default). That way we can display a component immediately in a fresh state.

If that given animation ever changes, we run it to get to the new end state. If there’s an animation currently in progress, we stop it first, and then proceed smoothly from whatever intermediate state it left us in.

We built this, and called it VelocityComponent.