All posts

· 4 min read

GSAP, Motion or plain CSS: a decision table

Three animation stacks, what each is actually good at, and the constraints that decide between them. No winner, because there isn't one.

This question usually gets answered with a preference. It's better answered with a constraint, because the three are good at different things and the differences are concrete.

Quick note on names: the library formerly called Framer Motion is now Motion, and it's no longer part of Framer. Same library, same API, new home.

What each one actually is

CSS animations are declarative and run in the browser's own animation engine. Compositor-friendly properties — transform, opacity, filter — animate off the main thread, so they keep going while your JavaScript is busy. Nothing to install and nothing in your bundle.

GSAP is a JavaScript animation engine with a timeline model. It animates anything with a number in it, sequences with precision, and has the deepest plugin set on the web — ScrollTrigger, MorphSVG, SplitText. It runs on the main thread.

Motion is a React-first library built around declarative props and springs. You describe states and it interpolates between them. It has a real physics spring, gesture handling, and layout animations that would be genuinely painful to write by hand.

Bundle sizes for the two libraries move often enough that printing numbers here would be wrong within a release. Check them against Bundlephobia the day you decide; what doesn't move is that CSS costs nothing and both libraries cost something.

The table

CSSGSAPMotion
Bundle costnothingcore plus each plugina dependency, trimmable with LazyMotion
Off the main threadyes, for transform and opacitynono
Spring physicssampled into linear()via plugin or easesnative
Interrupt mid-flightrestartsredirectsredirects, carries velocity
Sequencingby hand with delaysa real timelinevariants and stagger
Layout animationnomanuallayout prop
SVG morphingnopluginno
Works without JavaScriptyesnono
Works outside Reactyesyesno

The rows that actually decide it

Interruption. This is the one that catches people. A CSS animation doesn't know its current velocity, so re-triggering one mid-flight starts it again from its first keyframe. If you're animating something a user can interrupt — a menu they open and close quickly, a card they hover in and out of — CSS will look broken in a way that has nothing to do with your curve. GSAP and Motion both redirect from wherever the element currently is.

Layout animation. If you need an element to move smoothly when its position changes because the layout changed — a list reordering, a card expanding into a detail view — Motion's layout prop does it and the other two make you measure and compensate by hand. This single feature is worth the dependency on its own.

Anything SVG-shaped. Morphing one path into another, drawing a line along a stroke, splitting text into per-character spans. GSAP has plugins for all of it and they've been maintained for over a decade.

No-JavaScript-at-all. If the animation should run before hydration, or on a page that has no JavaScript, CSS is the only one of the three that qualifies.

The rough answer

For most interfaces, most of the motion is entrances, hovers and state transitions on transform and opacity. CSS does that, for free, off the main thread. Reaching for a library for a fade-up is how bundles get big.

Add Motion when you're in React and you need interruption, gestures or layout animation — which is a real threshold, not a vague one. Add GSAP when you need a timeline with precise sequencing, or one of its plugins.

Plenty of production apps use two of them. That's not indecision — CSS for the ambient stuff and GSAP for the one scroll section is a perfectly coherent setup.

What we'd push back on

The framing "which one is fastest" is mostly noise. All three are fast enough for interface work, and the performance differences that matter in practice come from what you animate, not what animates it. Animating width or box-shadow is expensive in all three, because it's the browser doing layout and paint work sixty times a second regardless of who asked. Animating transform is cheap in all three.

If you want a real performance rule, it's that one: pick compositor-friendly properties. Picking a library is a distant second.

Not deciding once

The reason verve exports all three is that this decision is usually made once, by whoever set the project up, and it's expensive to revisit — the animation gets written in the syntax of whatever won.

Tuning motion and choosing a library are different decisions, and they don't have to happen at the same time. In verve you design the motion on a timeline and pick the dialect at export. The same spring becomes a Motion transition, a GSAP tween or a sampled linear() easing. We check that they match, within a measured tolerance, on every change to the solver.

So if the answer turns out to be "CSS for this one and GSAP for that one", you don't have to know which is which before you start.