Adding animation to a React project usually comes down to three libraries: Motion (formerly Framer Motion), GSAP and React Spring. All three can produce the same fades, slides and scroll effects. They differ in how you write the code, in their licence terms, and in what to watch for with the Next.js App Router. This guide sets out the facts for each, then looks at where animated component libraries fit.
Licence and version details checked on 2026-10-14.
First: do you need a JavaScript animation library?
Fixed state transitions such as hover colours, expand and collapse, or a fade-in on mount are well covered by CSS transition and @keyframes, and Tailwind's transition-*, animate-* and motion-reduce: utilities. A library starts to pay for itself when:
- an animation must be interrupted, reversed or continued as React state changes, for example a drag that springs back on release
- a component needs to finish an exit animation before it unmounts
- layout changes should be tweened automatically (layout animation)
- several animations must be sequenced on a timeline or tied to scroll progress
The three libraries at a glance
| Motion | GSAP | React Spring | |
|---|---|---|---|
| npm package | motion (React imports from motion/react) | gsap plus @gsap/react | @react-spring/web |
| Licence | MIT | Standard "No Charge" License | MIT |
| API style | Declarative: target state as component props | Imperative: call gsap.to() and build timelines inside a hook | Hooks: useSpring returns animated values applied to animated.* elements |
| Timing model | Duration and easing, or springs | Duration and easing, scheduled on timelines | Spring physics (mass, tension, friction) |
| React versions | Peer dependency on React 18 and 19 | @gsap/react requires React 17 or later | Peer dependency on React 16.8 through 19 |
Motion
"use client";
import type { ReactNode } from "react";
import { motion } from "motion/react";
export function FadeIn({ children }: { children: ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: "easeOut" }}
>
{children}
</motion.div>
);
}Motion's strength is how closely it follows React's data flow: exit animations go through AnimatePresence, and layout tweening is a layout prop. According to the official docs, it runs animations on the browser's native Web Animations API where it can, and falls back to JavaScript for things the native APIs cannot do, such as springs and gesture tracking.
On size, the bundle size guide lists the full motion component at about 34kb, the m component with LazyMotion at about 4.6kb for the initial render, and the useAnimate mini version at 2.3kb.
Reduced motion: wrap the app in <MotionConfig reducedMotion="user">. When the operating system setting is on, every motion component turns off transform and layout animations automatically, while properties such as opacity still animate (docs).
Motion also sells Motion+ (examples, premium components and more); the core library remains MIT.
GSAP
"use client";
import { useRef, type ReactNode } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
gsap.registerPlugin(useGSAP);
export function FadeIn({ children }: { children: ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
useGSAP(
() => {
gsap.from(ref.current, { opacity: 0, y: 16, duration: 0.4, ease: "power2.out" });
},
{ scope: ref },
);
return <div ref={ref}>{children}</div>;
}GSAP suits animations with complex timelines, such as staggered sequences across many elements or scroll-driven storytelling with ScrollTrigger. useGSAP wraps the animations it creates in gsap.context() and reverts them on unmount, so Strict Mode's double-run effects in React 18 and later leave nothing behind (React guide). Animations created later, in click handlers for example, need the contextSafe() wrapper the hook returns to be cleaned up. For reduced motion, use gsap.matchMedia() with a (prefers-reduced-motion: no-preference) condition.
Licence change: after Webflow acquired GSAP, the library moved to the Standard "No Charge" License, effective 30 April 2025. Per gsap.com/licensing, commercial use is free, and the plugins that used to be members-only, including SplitText and MorphSVG, are free as well. The prohibited use is building a no-code visual animation tool that competes with Webflow's animation builder without written consent. Ordinary websites and apps are not affected. This is not an open source licence like MIT, and the npm license field points to these terms, so if your company reviews licences, it needs to go through that process.
React Spring
"use client";
import type { ReactNode } from "react";
import { animated, useSpring } from "@react-spring/web";
export function FadeIn({ children }: { children: ReactNode }) {
const styles = useSpring({
from: { opacity: 0, y: 16 },
to: { opacity: 1, y: 0 },
});
return <animated.div style={styles}>{children}</animated.div>;
}React Spring describes motion as spring physics with no fixed duration. When an animation is interrupted, it continues from its current velocity, which suits drag, gestures and interactions that should feel physical. It is part of the pmndrs ecosystem, and @react-spring/three can drive react-three-fiber scenes directly. The latest version on npm is 10.1.2, published in June 2026. For reduced motion there is a built-in useReducedMotion hook, used with Globals.assign({ skipAnimation }) to make every spring jump to its end value.
Next.js App Router and Server Components
Animation always runs in the browser, and none of these libraries' hooks can be called in a Server Component. In practice:
- Put animated components in their own files with
"use client"at the top, and import them from Server Components. All three examples above follow this pattern. - Motion also ships a
motion/react-cliententry point that can replace the"use client"plusmotion/reactcombination; the docs say this reduces the JavaScript sent to the client (installation docs). useGSAPfalls back touseEffecton the server, so it does not throw, but the file still needs"use client".- An entrance animation with an initial state such as
opacity: 0is hidden in the server-rendered HTML, so the content stays invisible if JavaScript fails to load. For important content, consider animating position only rather than starting fully transparent.
Which one to pick
- UI animation centred on React components (enter, exit, layout, gestures): Motion. It reads most like the rest of your JSX.
- Complex timelines, scroll storytelling, SVG morphing, text splitting: GSAP. The plugin set is broad and now free. Check the licence against your company's review requirements first.
- Physical-feeling interaction and 3D scenes: React Spring.
- Hover states and simple entrances: CSS and Tailwind are enough; skip the dependency.
Using two libraries in one project is common, for example Motion for UI and GSAP for a long scroll story on a marketing page. Avoid letting both control the transform of the same element, because they overwrite each other.
Where animated component libraries fit
The three libraries above are animation engines. You still design the timing, easing and reduced-motion fallback of every effect yourself. An animated component library is a set of finished components built on one of these engines: marquees, border beams, letter-by-letter text reveals and so on.
The common distribution model now is the WebberUI registry: a CLI copies the component source into your project's components/ui/, rather than installing an npm package. You can edit the code directly, and there is no runtime dependency on the component library itself. The tradeoff is that updates mean reinstalling or merging by hand. The real runtime dependency is the animation engine the components use.
WebberUI works this way. Its components are built on motion/react, and the CLI adds the motion dependency during install. Components that animate stop or simplify their motion under prefers-reduced-motion. If you have already chosen Motion, the components follow the same patterns as your own code, so editing them does not require switching mental models.
npx webberui@latest add https://webberui.com/r/split-text.en.jsonBring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add split-text.enStart with the quick start, or browse the component catalogue by category. To compare animated component libraries, see WebberUI vs Aceternity UI vs Magic UI vs React Bits. For hands-on CSS, read building an infinite marquee with Tailwind and three ways to build an animated gradient border.