← All articles

[ Blog ]

June 27, 2026

9 min read

GSAP vs Framer Motion: Which Should You Use?

GSAP vs Framer Motion — a senior creative developer compares API model, React fit, scroll, timelines, bundle size, and when each library actually wins.

GSAPFramer MotionAnimationReactNext.jsPerformance
GSAP vs Framer Motion: Which Should You Use?

Every few weeks a client or a designer asks me the same thing: GSAP vs Framer Motion — which animation library should I use? I ship both in production. I have used GSAP for over a decade on award-winning sites, and I reach for Framer Motion (now just Motion) when a React app needs declarative, gesture-driven UI. They are not really competitors; they are different tools that happen to overlap. This guide is the comparison I wish people sent me — the real API differences, the React integration story, scroll, timelines, bundle size, and the honest answer to "can I use both?"

Quick note on naming: in February 2025 Framer Motion was renamed Motion and the recommended import is now motion/react (the old framer-motion package still works). And in April 2025 Webflow made GSAP 100% free, including every plugin that used to be paid (SplitText, MorphSVG, ScrollSmoother, and more). Both of those changes matter for this decision, so I will fold them in.

TL;DR: which one wins

  • Choose Framer Motion (Motion) if you are building a React/Next.js app and most of your animation is component state, enter/exit transitions, layout shifts, and gestures (hover, drag, tap). It is declarative, it knows about React's lifecycle, and AnimatePosition/layout animations are genuinely magic.
  • Choose GSAP if you need precise, complex, timeline-orchestrated motion — scroll-driven scenes, sequenced hero animations, SVG morphing, text splitting, physics-feel easing, or anything where you choreograph dozens of elements down to the millisecond. It is also framework-agnostic, so the same knowledge ports to Webflow, vanilla JS, or any framework.
  • Use both when it makes sense — they coexist fine. More on that below.

The core difference: declarative vs imperative

This is the fork in the road, so understand it before anything else.

Framer Motion is declarative. You describe the state you want and the library figures out the transition. You render a motion component and animate via props:

import { motion } from "motion/react";

export function Card({ isOpen }: { isOpen: boolean }) {
  return (
    <motion.div
      animate={{ opacity: isOpen ? 1 : 0, y: isOpen ? 0 : 24 }}
      transition={{ duration: 0.4, ease: "easeOut" }}
    >
      Content
    </motion.div>
  );
}

There is no "play" call. You flip isOpen, React re-renders, and Motion interpolates from the old values to the new ones. This maps perfectly onto how React already works.

GSAP is imperative. You grab elements and command them on a timeline. You decide exactly when each thing happens:

import gsap from "gsap";

const tl = gsap.timeline({ defaults: { ease: "power3.out" } });
tl.from(".hero-title", { y: 80, opacity: 0, duration: 0.8 })
  .from(".hero-sub", { y: 40, opacity: 0, duration: 0.6 }, "-=0.4")
  .from(".hero-cta", { scale: 0.9, opacity: 0, duration: 0.5 }, "-=0.3");

That "-=0.4" is a relative position parameter — start this tween 0.4s before the previous one ends. This kind of overlap and sequencing control is where GSAP pulls ahead and never looks back. When you are choreographing a complex intro or a scroll story, the timeline model is far more expressive than chaining declarative states.

React integration: useGSAP vs motion/react

People assume GSAP and React fight each other. They do not — you just need the right cleanup pattern.

GSAP ships an official hook, useGSAP (from @gsap/react), that handles cleanup and scoping for you. Everything created inside it is reverted automatically on unmount, which is exactly what you want in React 18/19 with Strict Mode double-invoking effects:

import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";

export function Hero() {
  const root = useRef<HTMLDivElement>(null);

  useGSAP(
    () => {
      gsap.from(".hero-title", { y: 80, opacity: 0, duration: 0.8 });
    },
    { scope: root } // selectors are scoped to this ref; auto-reverted on unmount
  );

  return (
    <div ref={root}>
      <h1 className="hero-title">Award-winning motion</h1>
    </div>
  );
}

With Motion you do not write cleanup at all — the component is the animation. Enter/exit is handled by AnimatePresence:

import { AnimatePresence, motion } from "motion/react";

<AnimatePresence>
  {isVisible && (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    />
  )}
</AnimatePresence>;

The takeaway: Motion is lower-friction for UI state, GSAP is more powerful for orchestration. If your animation is "this thing appears/disappears/moves when state changes," Motion wins on ergonomics. If your animation is "a 12-step sequence with overlapping timing," GSAP wins on control.

Scroll: ScrollTrigger vs Motion's useScroll

Scroll animation is where I get asked the most, and it is the clearest split.

GSAP ScrollTrigger is the most capable scroll engine on the web — pinning sections, scrubbing a timeline to scroll progress, snapping, and precise start/end triggers. Nothing else comes close for scroll storytelling:

import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);

gsap.to(".panel", {
  xPercent: -100,
  ease: "none",
  scrollTrigger: {
    trigger: ".panel-wrap",
    pin: true,        // pin the section while scrolling
    scrub: 1,         // tie progress to the scrollbar
    end: "+=2000",
  },
});

If you want the full breakdown, I wrote a dedicated GSAP ScrollTrigger tutorial covering pin, scrub and parallax, and a guide to pairing it with smooth scroll in Next.js using GSAP and Lenis.

Motion's useScroll is great for the common cases — progress bars, simple parallax, reveal-on-scroll — and stays fully in the React/declarative world:

import { motion, useScroll, useTransform } from "motion/react";

export function ParallaxImage() {
  const { scrollYProgress } = useScroll();
  const y = useTransform(scrollYProgress, [0, 1], ["0%", "30%"]);
  return <motion.img style={{ y }} src="/photo.jpg" alt="Parallax photo" />;
}

For a progress indicator or a single parallax layer, useScroll is cleaner. But the moment you need pinning, scrubbed timelines, or snapping multiple sections, ScrollTrigger is the answer — it is one of the main reasons GSAP shows up on so many Awwwards sites.

Timelines and sequencing

Both can sequence, but the depth differs.

GSAP timelines are a first-class object: nest them, control playback (tl.pause(), tl.reverse(), tl.timeScale(2)), seek to a label, and reuse them. This is irreplaceable for complex intros and interactive sequences.

Motion sequences with the animate() function (for imperative needs) or with delay/orchestration props like staggerChildren on variants. It is plenty for staggered lists and coordinated UI, but it is not a full timeline with scrub, labels, and time-scaling.

Bundle size: the honest numbers

Bundle size is often the deciding factor for performance-sensitive work, so let me be precise. These are approximate min+gzip figures; verify against bundlephobia for your exact version:

ConcernGSAPMotion (Framer Motion)
Core / baseline~23 KB gzip (core)~34 KB gzip (full motion)
Tree-shakingPer-plugin imports (ScrollTrigger ~10 KB extra)LazyMotion + m → ~4.6 KB initial, features loaded on demand
Plugins / extrasAll plugins now free (SplitText, MorphSVG, etc.)Layout/drag/gestures included or lazy-loaded
Framework lock-inNone (vanilla, any framework)React-only

The nuance: Motion's full motion component is heavier than GSAP core, but if you adopt the LazyMotion + m pattern you can ship as little as ~4.6 KB initially and load features on demand — beating GSAP for small, gesture-light UI. GSAP stays flat at ~23 KB for core and you add only the plugins you import. For an animation-heavy site you will likely pull in ScrollTrigger anyway, so budget ~33 KB+.

Neither number should scare you on a real project — but if you are shipping a tiny widget where every kilobyte counts, LazyMotion is the lighter floor.

When each library actually wins

Reach for Framer Motion (Motion) when:

  • You are building a React/Next.js product UI: modals, drawers, tabs, accordions, toasts.
  • You want layout animationslayout prop animating size/position changes for free is genuinely best-in-class.
  • Gestures matter: drag, hover, tap, pan with spring physics built in.
  • Your team is React-first and wants animation that reads like the rest of the component.

Reach for GSAP when:

  • You are crafting a signature hero, an intro sequence, or a scroll-driven story with precise timing.
  • You need ScrollTrigger (pin, scrub, snap) — there is no real substitute.
  • You want SVG morphing, advanced text effects (SplitText), or motion-path work.
  • You need the same skill to work outside React (Webflow, vanilla, Svelte, Vue).
  • You care about absolute, frame-accurate control over choreography.

On the award-winning Iventions events site, the heavy lifting — scroll choreography, the WebGL/Three.js hand-off, sequenced reveals — was GSAP. That is the kind of work where GSAP's control is non-negotiable. For a typical SaaS dashboard with animated panels and drag-to-reorder, I would reach for Motion every time.

Can you use both in the same project?

Yes, and I do. They do not conflict — GSAP mutates the DOM/styles imperatively while Motion drives its own components. A common, clean split:

  • Motion for UI state and gestures (menus, page-level enter/exit, drag).
  • GSAP for the hero, scroll scenes, and SVG/text effects.

The only rule: do not have both libraries animating the same property on the same element at the same time — you will get a fight over transform. Keep ownership clear (Motion owns this element, GSAP owns that one) and they live happily together. The bundle cost of shipping both is usually fine if each is doing what it is best at; if you are size-paranoid, pick one and lean in.

FAQ

Is GSAP still paid in 2026?

No. As of April 30, 2025, Webflow made GSAP 100% free, including all previously paid Club plugins (SplitText, MorphSVG, DrawSVG, ScrollSmoother, Inertia). This removed the biggest historical reason teams avoided it.

Is Framer Motion the same as Motion?

Yes — Framer Motion was rebranded to Motion in early 2025. The recommended package is motion and you import from motion/react. The old framer-motion package still works for existing projects.

Which is better for scroll animations?

GSAP's ScrollTrigger for anything beyond the basics (pinning, scrubbed timelines, snapping). Motion's useScroll/useTransform is perfectly good for progress bars and simple parallax inside React.

Which has the smaller bundle?

With LazyMotion + the m component, Motion can ship ~4.6 KB initially — smaller than GSAP core (~23 KB). But for an animation-rich site you will likely add ScrollTrigger to GSAP, so compare against your actual feature set, not the headline number.

Do I need to know GSAP if I only build React apps?

Not strictly — Motion covers most React UI animation needs. But the day you need a real scroll story, complex sequencing, or SVG morphing, GSAP is the tool, and it transfers to non-React work too. It is one of the defining skills of a creative developer.


Still unsure which fits your project — or want a hero/scroll experience that actually wins awards? That is exactly the kind of work I do. See how I can help on the services page.