GSAP vs Framer Motion in 2026: which to use for a Next.js site

Cover image for GSAP vs Framer Motion in 2026: which to use for a Next.js site

If you are weighing GSAP vs Framer Motion for a Next.js site, here is the short answer. Use Motion (the new name for Framer Motion) when most of your animation is UI state: menus, modals, lists, tabs and layout changes. Use GSAP when the animation is the experience: scroll-driven stories, pinned sections, text reveals and long timelines. Both are free and both work well in the App Router.

I build sites with both, and on most projects I only need one. The rest of this post explains how they differ in licensing, size, scroll, layout and React integration, with small code examples for each.

What are GSAP and Motion (Framer Motion) in 2026?

GSAP is a framework-agnostic animation library. It animates any numeric value on any object, so it works the same in React, Vue or plain HTML. Its plugins cover scroll (ScrollTrigger), text (SplitText), layout changes (Flip) and SVG (MorphSVG, DrawSVG). In React you use it through the @gsap/react package and its useGSAP hook.

Framer Motion is now called Motion. The React API ships in the motion package and you import it from motion/react. Motion is declarative: you describe states as props on a motion.div, and the library animates between them when the props change. The old framer-motion package still exists, so older tutorials still work.

bash
# GSAP and its React hook
npm install gsap @gsap/react

# Motion (formerly Framer Motion)
npm install motion

Here is the same fade-up in each. Notice the difference in style: GSAP is a function you call on elements, Motion is a component you render.

components/FadeUpGsap.tsx
"use client";
import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";

gsap.registerPlugin(useGSAP);

export function FadeUpGsap({ children }: { children: React.ReactNode }) {
  const ref = useRef<HTMLDivElement>(null);
  useGSAP(() => {
    gsap.fromTo(ref.current, { y: 24, autoAlpha: 0 }, { y: 0, autoAlpha: 1, duration: 0.8, ease: "power3.out" });
  }, { scope: ref });
  return <div ref={ref}>{children}</div>;
}
components/FadeUpMotion.tsx
"use client";
import { motion } from "motion/react";

export function FadeUpMotion({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      initial={{ y: 24, opacity: 0 }}
      animate={{ y: 0, opacity: 1 }}
      transition={{ duration: 0.8, ease: "easeOut" }}
    >
      {children}
    </motion.div>
  );
}

Is GSAP free for commercial use now?

Yes. Since GSAP 3.13 the whole library is free, including the plugins that used to be paid, such as SplitText, MorphSVG and ScrollSmoother. That covers commercial projects. The license is GSAP's own standard license, not MIT. Its main restriction is on building visual, no-code animation tools that compete with Webflow, which now owns GSAP. If you are building a normal website or app, it does not affect you.

Motion is MIT licensed. There is a paid Motion+ tier with extra examples and components, but the library itself costs nothing. So licensing no longer decides this choice, which was not true a few years ago.

How do bundle size and performance compare?

Motion's own docs say the full motion component cannot be tree-shaken below about 34 kb. If size matters, you can render the lighter m component inside LazyMotion, which the docs put at about 4.6 kb for the first render, with animation features loaded after. For simple imperative work there is also a small useAnimate build.

GSAP has a fixed core, and each plugin you register adds to it. You do not pay for plugins you never import. I do not trust published size tables for either library because they change between versions, so I check the real numbers in my own build. On a typical marketing site, neither library is what makes a page slow. Large images and third-party scripts usually are.

React integration

Motion fits the React model: state changes, props change, the element animates. Its motion values update the DOM without re-rendering your component, which keeps it fast. GSAP works on refs. The useGSAP hook runs your code after the DOM exists, scopes selectors to a container, and reverts every tween and ScrollTrigger on unmount. I cover the setup in detail in how to set up GSAP in the Next.js App Router.

For both, the rule for smooth motion is the same: animate transform and opacity, and avoid animating width, height, top or left on large elements.

ScrollTrigger vs useScroll: which handles scroll better?

This is where GSAP pulls ahead. ScrollTrigger can pin a section, scrub a whole timeline with the scrollbar, snap to labels and drive animations inside a horizontal track. A timeline with a position parameter lets you place twenty tweens precisely, then hand the whole thing to the scroll.

tsx
useGSAP(() => {
  const tl = gsap.timeline({
    scrollTrigger: { trigger: ".story", start: "top top", end: "+=1500", pin: true, scrub: 1 },
  });
  tl.fromTo(".story-title", { yPercent: 100 }, { yPercent: 0 })
    .fromTo(".story-image", { scale: 1.2 }, { scale: 1 }, "<")
    .fromTo(".story-copy", { autoAlpha: 0 }, { autoAlpha: 1 }, "-=0.2");
}, { scope: root });

Motion's useScroll gives you a scroll progress value between 0 and 1, and useTransform maps it to any style. It is clean for progress bars and parallax. It has no pinning, so for a pinned scene you use CSS position: sticky and do the math yourself.

components/Parallax.tsx
"use client";
import { useRef } from "react";
import { motion, useScroll, useTransform } from "motion/react";

export function Parallax() {
  const ref = useRef<HTMLDivElement>(null);
  const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "end start"] });
  const y = useTransform(scrollYProgress, [0, 1], [-60, 60]);
  return (
    <div ref={ref}>
      <motion.img src="/photo.jpg" alt="" style={{ y }} />
    </div>
  );
}

If you want a feel for what scrub, pinning and easing do before you choose, try them in my scroll playground. For a full GSAP example, see the horizontal scroll section guide.

Layout and exit animations: where Motion is stronger

Motion's best feature is the layout prop. Add it to an element and any change in its size or position animates, even when CSS caused the change. layoutId moves one element into another across the tree, like a tab underline sliding to the active tab. AnimatePresence keeps an element in the DOM until its exit animation finishes.

tsx
<AnimatePresence>
  {open && (
    <motion.div
      key="panel"
      initial={{ opacity: 0, y: 12 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: 12 }}
    />
  )}
</AnimatePresence>

GSAP can do layout animation with the Flip plugin, and it does it well. But you write more code: record the state, change the DOM, then animate from the old state. Exit animations are harder, because React removes the element before GSAP can animate it. You have to keep the element mounted yourself and unmount it in onComplete. For an app full of panels and lists, Motion saves real time.

GSAP vs Framer Motion: which should you use?

  • Pinned scroll scenes, scrubbed timelines, horizontal scroll: GSAP with ScrollTrigger.
  • Text split into lines, words or characters: GSAP with SplitText.
  • Modals, menus, toasts and anything that leaves the DOM: Motion with AnimatePresence.
  • Reordering lists, shared elements, tab indicators: Motion with layout and layoutId.
  • Drag and gesture-driven UI in React: Motion, for its gesture props.
  • Long, choreographed intros with exact timing: GSAP timelines.
  • The same code in React and a plain HTML page: GSAP, because it does not need React.

On a marketing site or portfolio, I usually pick GSAP, because the work is scroll and type. This site, nabe.pro, uses GSAP with Lenis for every section. In a product dashboard or an AI tool with lots of panels, I reach for Motion, because the motion follows state. When a project needs both, I keep the split clear so that each element is owned by one library.

Wrapping up

Choose by the kind of motion, not by popularity. Motion is the better fit for state, layout and exit animations in React. GSAP is the better fit for scroll, timelines and text. Both are free, and both are fast when you animate transforms and opacity. If you want this kind of work built for your product, see how I work on motion.

Frequently asked questions

Can I use GSAP and Motion in the same Next.js project?

Yes. They do not conflict, because each one animates the elements you hand it. The only rule is to never let both libraries animate the same property on the same element, or they will fight every frame. A common split is Motion for menus, modals and lists, and GSAP for scroll sections and text reveals.

Is Framer Motion the same as Motion?

Yes. Framer Motion was renamed Motion and is now an independent project. The React API lives in the motion package and you import it from motion/react. The old framer-motion package still installs, so existing code keeps working, but new projects should use motion. The API you know, such as motion.div, AnimatePresence and useScroll, is the same.

Does GSAP work with React Server Components?

GSAP runs in the browser, so the component that calls it needs the "use client" directive. Your page can stay a Server Component and render a small client component that owns the animation. Use the useGSAP hook from @gsap/react, which waits until the DOM exists and reverts every tween when the component unmounts.

Which is better for SVG animation, GSAP or Motion?

Both can animate SVG attributes and path drawing. GSAP goes further with plugins that are now free: MorphSVG morphs one shape into another, DrawSVG draws strokes, and MotionPath moves elements along a path. If the SVG work is a logo reveal or an illustrated scene with many parts, I pick GSAP. For an icon that changes state, Motion is enough.

Need this built?Motion & InteractionScroll experiences, micro-interactions and launch films that make a product feel alive.