Motion8 July 20269 min readNULLLAB

GSAP vs Framer Motion vs CSS animations

A working studio's honest comparison of GSAP, Framer Motion and modern CSS — what each is genuinely best at, what it costs, and how to choose without fanboyism.

GSAP vs Framer Motion vs CSS animations — cover
Three layers of motion, one system: CSS for states, Framer Motion for components, GSAP for choreography.

Ask three developers to name the best animation library and you'll get three confident, contradictory answers. We build motion-heavy sites for a living — GSAP timelines, Lenis smooth scroll, WebGL — and we still reach for plain CSS more often than either JavaScript option. The honest answer to the GSAP vs Framer Motion question is that they aren't competing for the same job. This is the comparison we wish every brief came with: what each tool is genuinely best at, what it costs in bytes and complexity, and how we decide on real projects.

Three tools that solve three different problems

The CSS-versus-JavaScript animation debate is usually framed as a performance question. It's really a control question. CSS transitions are declarative and fire-and-forget: describe two states, let the browser interpolate. Framer Motion is a React component library, animation expressed as props and tied to the component lifecycle. GSAP is a framework-agnostic imperative engine: you script exactly what happens, when, in what order, to anything with a numeric property.

The landscape also shifted recently, which dates most older comparisons. Framer Motion rebranded to Motion (motion.dev) in late 2024, became independent of Framer, and absorbed Motion One. GSAP was acquired by Webflow, and in 2025 the entire toolkit — including formerly paid plugins like SplitText, MorphSVG and ScrollSmoother — became free for commercial use. The old objection that GSAP's good parts cost money is gone.

So treat this less as a versus and more as a sorting exercise. Almost every polished site we've shipped uses at least two of the three. The mistake isn't picking the wrong web animation library; it's using one tool for every class of motion and paying for it in bundle size or awkward code.

CSS first: states, hovers and everything the compositor gives you free

Hover states, focus rings, toggles, accordions, colour and opacity shifts: none of this needs JavaScript. A transition on transform and opacity costs zero bytes of library code, and those two properties can be composited off the main thread. A CSS hover keeps animating smoothly even while your JavaScript is busy hydrating a React tree. No animation library can make that promise.

Modern CSS covers more ground than its reputation suggests. @starting-style and transition-behavior: allow-discrete let you animate elements in from display: none — entry and exit transitions used to be the classic excuse for reaching for a library. linear() easing can approximate spring curves. Scroll-driven animations via animation-timeline: scroll() and view() are landing in browsers now, though cross-browser support remains uneven enough that we don't rely on them for anything load-bearing.

Where CSS taps out is orchestration. Chained delays get brittle past two or three elements, you can't cleanly interrupt and reverse a half-finished sequence, and you can't drive values from user input or physics. When you catch yourself computing animation-delay values in a Sass loop, that's the signal to move up a layer.

Framer Motion: animation that speaks React's language

Framer Motion's core insight is that in React, animation state is component state. You write motion.div with an animate prop and the library handles interruption, spring physics and cleanup. Its two killer features solve problems React makes genuinely hard: AnimatePresence animates components out before they unmount, and the layout and layoutId props give you FLIP-based shared layout transitions — a card expanding into a detail view — in one attribute.

The developer experience is the draw. Variants propagate through a component tree so a parent can choreograph children declaratively. Gesture props like whileHover, whileTap and drag replace a pile of event-handler code. Because the current library builds on the Web Animations API where it can, many of its animations run hardware-accelerated too. For product UI in React — modals, menus, list reordering, page transitions — it's the strongest default, and the case for anything heavier only appears at the edges.

Those edges: long, multi-element narrative sequences. There is a useAnimate hook for imperative sequencing, but once a hero animation involves fifteen overlapping tweens with scroll scrubbing, you're fighting the tool's grain. Framer Motion animates components. It was never designed to direct scenes.

GSAP and ScrollTrigger: timelines, scroll choreography, total control

GSAP's timeline is still the best sequencing model in web animation. Tweens overlap with position parameters ('-=0.4'), get labelled, nest into parent timelines, and the whole construction can be paused, reversed, scrubbed or speed-shifted as one object. And GSAP doesn't care what it's animating: DOM nodes, SVG paths, canvas particles, three.js uniforms, plain object values. That last point matters enormously once WebGL enters a project.

ScrollTrigger is the reason agencies default to GSAP. Pinning sections, scrubbing a timeline to scroll position, snapping between states, coordinating with Lenis for smooth scroll — this is the machinery behind most award-site scroll choreography, and it's battle-tested in a way scroll-driven CSS isn't yet. Our own site runs on exactly this stack. With SplitText now free, the signature line-by-line text reveal costs nothing but bytes.

The trade-off is that GSAP is imperative and framework-agnostic, which means in React you manage refs and cleanup yourself. The official useGSAP hook handles scoping and teardown well, but the code never feels as native to React as Framer Motion does. You're writing a script that acts on the DOM, not describing UI. For choreography that's a feature; for a checkbox it's overkill.

Bundle cost and runtime performance, without the folklore

Rough, current numbers: CSS costs nothing. GSAP's core lands around 23–24 KB min+gzip, with ScrollTrigger adding roughly 13 KB more. Framer Motion's full featureset sits in the low thirties, but LazyMotion with the domAnimation bundle roughly halves that, and the framework-agnostic mini animate function from Motion is only a few kilobytes. All three are small next to one unoptimised hero image. Bundle size is a tiebreaker, not a verdict.

Runtime is more nuanced than 'CSS is faster'. Compositor-driven CSS wins for simple transform and opacity work. Framer Motion delegates to the Web Animations API where possible, so much of it can run off the main thread too. GSAP ticks on a requestAnimationFrame loop on the main thread — and in practice it's so heavily optimised that it's almost never the bottleneck. What actually tanks frame rates and INP is animating layout properties like width or top, and doing unthrottled work in scroll handlers.

One requirement applies regardless of tool: respect prefers-reduced-motion. CSS media queries, Framer Motion's useReducedMotion, or an early return before building GSAP timelines. We treat it as a hard gate in every motion hook we ship, and it's the first thing we check in a motion audit.

GSAP vs Framer Motion vs CSS: the framework we use on client work

Sort by interaction type, not by project, and most of the GSAP vs Framer Motion argument dissolves. A state change on one element — hover, focus, open/closed — is CSS, always. Enter, exit and layout animation inside a React component tree is Framer Motion. A sequence — anything with a timeline, scroll scrubbing, pinning, text splitting, canvas or WebGL, or a non-React codebase — is GSAP with ScrollTrigger. If a site is mostly product UI, we start Motion-first; if it's mostly narrative and scroll-driven, GSAP carries the site and CSS handles the states.

Choose per interaction, not per project: states to CSS, component motion to Framer Motion, choreography to GSAP.

Mixing them is normal, not messy — provided the motion itself is unified. Define durations and easing curves once, as design tokens (CSS custom properties and a shared JS config), and make every layer consume them. That's how a site with CSS hovers, Motion page transitions and a GSAP hero still feels like one hand animated it. The tools are interchangeable; the taste isn't.

Frequently asked questions

Is GSAP really free now, including the premium plugins?

Yes. After Webflow acquired GSAP, the entire toolkit was made free in 2025 — including previously paid Club plugins like SplitText, MorphSVG and ScrollSmoother — for commercial use. The old objection that GSAP's best features sat behind a licence no longer applies, which has shifted the calculus for a lot of teams.

Are Framer Motion and Motion the same library?

Effectively, yes. Framer Motion rebranded to Motion in late 2024, became independent of Framer, and merged with Motion One. The React API most people know continues as 'Motion for React', and the project also offers a tiny framework-agnostic animate function. Older tutorials referencing framer-motion still map closely to the current library.

Which is better for React: GSAP or Framer Motion?

It depends on the class of motion. For component lifecycle work — enter and exit animations, shared layout transitions, gestures — Framer Motion is more idiomatic and less code. For long timelines, scroll-scrubbed sequences, pinned sections or animating canvas and WebGL, GSAP with the useGSAP hook is stronger. Many production React sites use both, scoped to different jobs.

Are CSS animations faster than JavaScript animations?

For transform and opacity, CSS can run on the compositor thread and survive main-thread jank, so it has a real structural advantage. But well-written JavaScript animation is rarely the bottleneck. Frame drops usually come from animating layout properties like width or top, or from heavy scroll handlers — the properties you animate matter more than the tool.

Can I use GSAP and Framer Motion together on one site?

Yes, and it's common: Framer Motion for UI transitions inside React components, GSAP and ScrollTrigger for hero sequences and scroll choreography. The risks are duplicated bundle weight and inconsistent feel, so scope each library's responsibilities clearly and share duration and easing tokens between them so the motion reads as one system.

Keep reading

Motion that earns its milliseconds — cover

Motion5 min read

Motion that earns its milliseconds

What is creative engineering? — cover

Engineering8 min read

What is creative engineering?