GSAP vs. Motion: Which Animation Library to Choose in 2026
A complete guide for developers comparing GSAP and Motion (Framer Motion). Dive into code examples for React, understand the core philosophies, and choose the right animation library for your project.

Part 1: From Static to Story - Why Your Choice Matters
In today's digital landscape, a static website is a missed conversation. Motion is no longer just a decorative flourish; it's the language of a modern user experience. It guides the eye, provides critical feedback, creates a sense of polished craftsmanship, and transforms a simple interface into an engaging story.
You know your project needs this spark. But as you stand ready to build, you face a critical decision that will shape your workflow and your project's potential: which tool should you use? Do you stick to CSS? Do you build from scratch in JavaScript? Or do you leverage the power of a dedicated animation library?
For any animation beyond the simplest of hover effects, a library is the answer. These tools are powerful abstractions, saving you from reinventing the wheel and letting you build faster and better. And in the world of web animation, two names reign supreme.
Introducing the Titans
On one side, we have Motion (formerly the acclaimed Framer Motion). It's the modern champion of the React ecosystem, a library designed from the ground up to feel like a natural, intuitive extension of component-based development. It prioritizes a seamless developer experience and makes creating fluid, interactive UIs feel almost effortless.
On the other side, we have GSAP (the GreenSock Animation Platform). This is the battle-tested industry standard, a professional-grade toolkit revered for its raw performance and surgical precision. For over a decade, GSAP has been the engine behind countless award-winning, visually breathtaking websites, giving developers the power to animate anything they can imagine.
Our Goal: To Give You Clarity and Confidence
This is not just another feature list. This guide is designed to give you a deep, foundational understanding of why these libraries exist and how their core philosophies differ. Through clear explanations, practical code examples, and visual demonstrations, you will learn to think like an animator. By the end, you won't just know which library to choose for your next project—you will have the confidence and clarity to select the right tool for any job.
Part 2: The Language of Motion - Animation Fundamentals
Before you can direct a film, you need to understand shots, scenes, and pacing. Likewise, before we can master an animation library, we must understand the fundamental language of motion. These core concepts are the universal building blocks of every animation you will ever create, and the libraries are simply tools that give you powerful control over them.
The Properties: What You Animate
These are the visual properties of an HTML element that we can change over time.
- Position & Size: Changing an element's coordinates (
x,y) or its dimensions (width,height). - Appearance: Modifying properties like
opacity(from fully transparent to fully opaque),color, orbackgroundColor. - Transformation: This is the secret to buttery-smooth, high-performance animation. Instead of changing properties like
widththat can force the browser to recalculate the entire page layout,transformoperates on a separate layer, often handled by the computer's graphics processor (GPU). Transformations are incredibly efficient and include:scale: Making an element larger or smaller from its center point.rotate: Spinning an element around a chosen axis.skew: Slanting an element.
Key Takeaway
For the best performance, always prioritize animating transform and
opacity. Both GSAP and Motion are highly optimized to do this under the
hood.
The Controls: How You Animate
Changing a property instantly isn't an animation; it's a jump. The art is in controlling the transition between the start and end values.
-
Duration: The simplest control. How long does the animation take? A quick, snappy 0.2 seconds? Or a slow, graceful 1.5 seconds? This sets the tempo.
-
Easing: This is the personality and soul of your animation. Easing determines the rate of change over time, making motion feel natural and believable instead of robotic.
- Analogy: Think of a car accelerating. It doesn't instantly hit 60 mph. It starts slow, picks up speed, and then slows down as it comes to a stop. That acceleration and deceleration curve is its "ease."
- A
linearease means constant speed. It feels unnatural and mechanical for most UI movements. - An
ease-outis very common. The animation starts fast and decelerates to a smooth stop. It feels responsive. - An
ease-in-outis like the car: a gentle start, a faster middle, and a gentle end.
Ease:'linear'
Ease: 'power2.out'
For a fantastic visual exploration of different easing curves, check out the resources at easings.net or anime.js/easing-editor.
The Choreography: How You Combine Animations
The true power of animation comes from orchestrating multiple elements together.
- Sequencing: Making animations happen in a specific order. A title fades in, then a subtitle slides up. This creates a logical, story-driven flow.
- Staggering: A beautiful and highly effective UI pattern. Instead of animating five list items in all at once, you animate the first one, then 0.1 seconds later the second, then 0.1 seconds later the third, and so on. This creates a delightful and visually appealing "domino effect."
With this vocabulary in hand—properties, controls, and choreography—we are now equipped to understand the real-world problems that animation libraries were designed to solve. Let's explore why trying to manage all of this by hand is a path fraught with complexity.
Perfect. Now we'll set the stage with Part 3, establishing the "pain" that makes the libraries' solutions so valuable. We will reinforce the "abstraction is speed" theme and use clear, relatable examples of the complexity involved in manual animation.
Part 3: The "Hard Way" & The Need for Speed
You've learned the language of motion. You're ready to start choreographing. Your first instinct might be to ask, "Can't I just use the tools built into the browser, like CSS Animations or plain JavaScript?"
The answer is a qualified yes, but it brings us to a fundamental principle of modern software development: Abstractions are great. Abstractions are speed.
We don't write machine code to build websites; we use programming languages. We don't manually draw pixels on a screen; we use HTML and CSS. We use these powerful abstractions because they handle immense low-level complexity, allowing us to build faster, think at a higher level, and focus on solving the actual problem.
An animation library is a high-level abstraction for motion. To truly appreciate the speed and power it provides, let's first walk through the complex, frustrating problems it elegantly solves for you.
The Pain Point #1: The Pyramid of Doom
Let's try to build a simple, choreographed sequence: a notification banner appears.
- The container fades in.
- Then, the title slides into place.
- Finally, the dismiss button appears.
To do this with browser APIs, you need to know when one animation has finished before starting the next. This requires callbacks or event listeners, which leads to a notoriously messy, nested code structure.
The Manual JavaScript Code (The "Pain"):
const banner = document.querySelector('.banner');
const title = document.querySelector('.title');
const button = document.querySelector('.button');
// Action 1: Fade in banner
banner.style.opacity = 1;
banner.addEventListener(
'transitionend',
() => {
// This function only runs AFTER the banner has faded in.
// Action 2: Slide in title
title.style.transform = 'translateY(0)';
title.addEventListener(
'transitionend',
() => {
// This function only runs AFTER the title has slid in.
// Action 3: Fade in button
button.style.opacity = 1;
// What if the sequence had 10 steps?
// This pyramid becomes unreadable and impossible to edit.
},
{ once: true }
);
},
{ once: true }
);This nested "pyramid" is fragile. If you need to change the timing or re-order the animations, you have to carefully refactor the entire chain. There is no central "play/pause" button and no easy way to control the sequence as a whole. This is a low-level way of thinking that slows you down.
The Pain Point #2: The Exit Problem in React
In modern component-based frameworks like React, an even bigger challenge arises: exit animations.
Imagine a to-do list. When you complete an item, you want it to fade out gracefully before it's removed from the list.
The problem is that React controls the DOM. When you update your state to remove the to-do item, React immediately deletes it from the page. It's gone in a flash. Your fade-out animation never even gets a chance to play.
Solving this manually is a fight against the framework. It involves a complex dance of:
- Triggering the "exit" state without actually removing the data.
- Adding a specific CSS class (e.g.,
.exiting). - Using a
setTimeoutor waiting for ananimationendevent. - Then, finally, updating the state to truly remove the item.
This is a huge amount of boilerplate for a very common UI pattern. It's a clear signal that we need a better abstraction.
These are the exact kinds of problems that GSAP and Motion were born to solve. They abstract away the messy timing issues, the framework conflicts, and the low-level details, letting you declare your creative intent and get back to building. That is the power of a great library. That is speed.
Part 4: The Core Philosophy - Director vs. Architect
We now know why we need a library. But GSAP and Motion are not interchangeable; they are built on two fundamentally different philosophies. Understanding this difference is the most important step in choosing the right tool, as it will shape how you think, how you code, and what you can create with ease.
The difference is Imperative ("The Director") versus Declarative ("The Architect").
Imperative Animation: You Are The Director
The imperative approach puts you in the director's chair. You are in complete control of the scene, and you issue explicit, step-by-step commands to your actors (the DOM elements). You are responsible for the how, the when, and the what.
This is the world of GSAP.
Your code is a script of commands. You find an element on the page and tell it precisely what to do.
The Mindset of a Director:
- You think in verbs and actions: "fade," "move," "rotate," "pause."
- Your logic is a sequence: First, do this. Then, do that.
- Your code is often separate from the element it controls. It's a script that acts upon the stage (the DOM).
Use Case: A Simple Welcome Message
Let's animate a heading and a subheading. The heading should fade in first, and then the subheading should slide up.
The Imperative Code (Conceptual GSAP):
// We need access to the elements, so we use refs in React.
const titleRef = useRef(null);
const subtitleRef = useRef(null);
// Inside a React Effect or useGSAP hook, we issue our commands.
// Command #1: Animate the title.
gsap.to(titleRef.current, { opacity: 1, duration: 0.8 });
// Command #2: Animate the subtitle, but with a delay.
// We are explicitly telling it to wait.
gsap.to(subtitleRef.current, { opacity: 1, y: 0, duration: 0.5, delay: 0.4 });Notice how we are giving direct orders. The logic is a sequence of commands. This model gives you incredible power for creating detailed, custom-timed choreographies.
Declarative Animation: You Are The Architect
The declarative approach is like being an architect designing a smart building. You don't write the code for the thermostat; you create a blueprint that defines the building's states. For example: "In the 'occupied' state, the temperature is 72°F. In the 'empty' state, the temperature is 65°F." The building's control system (the library) is responsible for figuring out how to transition between those states.
This is the world of Motion.
You don't issue commands. You describe what your component should look like in different states, and the library animates between them for you when the state changes.
The Mindset of an Architect:
- You think in nouns and states: "visible," "hidden," "hovered," "tapped."
- Your logic is a description of outcomes.
- Your code is tightly integrated with the elements themselves, often as props on your JSX components.
Use Case: The Same Welcome Message
Let's build the same animation, but this time by describing its final state.
The Declarative Code (Conceptual Motion):
// We describe the final, animated state directly on the component.
// The component "knows" how to get here from its default or initial state.
<motion.h1
// The state we want to end up in.
animate={{ opacity: 1 }}
transition={{ duration: 0.8 }}
/>
<motion.p
// We declare its final state...
animate={{ opacity: 1, y: 0 }}
// ...and how it should transition, including the delay.
transition={{ duration: 0.5, delay: 0.4 }}
/>See the difference? We are not commanding the elements to move. We are declaring that their animated state is one with opacity: 1. The "how" is handled by Motion. This model is incredibly powerful for creating animations that are tightly bound to the state of your user interface.
Why This is the Key to Your Decision
This isn't just a matter of style; it's a fundamental fork in the road that determines what each library excels at.
-
GSAP's imperative model makes it the undisputed king of complex, story-driven sequences. When you need to create a cinematic intro with dozens of elements timed to perfection, you need a director's script.
-
Motion's declarative model makes it a seamless extension of state-driven user interfaces. When your animation needs to react to user input, component props, or application state, you need an architect's blueprint.
Now, let's bring this theory to life. We'll dive deep into each library with practical, real-world code, and you'll see these two powerful philosophies in action.
Part 5: A Deep Dive into Motion - The React Architect
If you're building with React, Motion is designed to feel like home. It extends the component model you already know, allowing you to build intricate animations with a syntax that is both intuitive and deeply integrated. It operates as the "architect's control system," automatically handling transitions as you describe the states of your UI.
Let's see how this declarative approach provides elegant solutions to real-world animation challenges.
1. The Core Syntax: From HTML to motion
The journey begins by converting any standard HTML or SVG element into an animatable one by simply prefixing it with motion.. This simple change unlocks a new set of props for you to declare your animations.
Let's create a card that fades in and scales up when it appears.
Declarative Animation in Action
The Code:
'use client';
import { motion } from 'motion/react';
export function MotionCard() {
return (
// "motion.div" is a drop-in replacement for a regular div.
<motion.div
// --- STYLING ---
// We define the card's appearance using Tailwind utilities.
// It's responsive, theme-aware, and has a clean, modern look.
className='w-full max-w-md rounded-xl border bg-card p-8 shadow-sm'
// --- ANIMATION ---
// The initial state (before animation).
initial={{ scale: 0.95, y: 20, opacity: 0 }}
// The final state (after animation).
animate={{ scale: 1, y: 0, opacity: 1 }}
// The transition rules. A spring animation provides a natural, physical feel.
transition={{ type: 'spring', stiffness: 100, damping: 20, mass: 0.5 }}
>
<p className='text-center text-xl font-semibold tracking-tight text-card-foreground'>
Declarative Animation in Action
</p>
</motion.div>
);
}The Architect's Logic: We haven't written a single command. We've created a blueprint with two states: initial and animate. When React renders this component, Motion's control system sees these two states and automatically generates a smooth, physics-based animation between them.
2. The Magic Component: Solving Exits with AnimatePresence
Remember the "exit problem" where React removes components before they can animate out? Motion's solution is a component so powerful it feels like magic: <AnimatePresence>.
The Use Case: A modal that slides in when opened and slides out when closed.
The Code:
// src/components/AnimatedModal.tsx
'use client';
import { useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { X } from 'lucide-react';
import { Button } from '@/components/ui/button'; // Using shadcn's Button component
export function AnimatedModal() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
{/* The button that triggers the modal */}
<Button onClick={() => setIsOpen(true)}>Open Modal</Button>
{/* AnimatePresence handles the mounting/unmounting animations */}
<AnimatePresence>
{isOpen && (
// 1. The Modal Backdrop / Overlay
<motion.div
// Covers the entire screen and centers its content
className='fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setIsOpen(false)}
// A lower duration makes the backdrop feel more responsive
transition={{ duration: 0.2, ease: 'easeInOut' }}
>
{/* 2. The Modal Content */}
<motion.div
// Styled like a responsive, theme-aware card
className='relative w-full max-w-lg rounded-xl border bg-card p-6 shadow-lg'
initial={{ scale: 0.95, y: 16, opacity: 0 }}
animate={{ scale: 1, y: 0, opacity: 1 }}
exit={{ scale: 0.95, y: 16, opacity: 0 }}
transition={{ type: 'spring', stiffness: 120, damping: 15 }}
// This is crucial: it prevents a click inside the modal from closing it
onClick={(e) => e.stopPropagation()}
>
{/* Close Button: An accessible icon button in the top-right corner */}
<button
onClick={() => setIsOpen(false)}
className='absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none'
aria-label='Close modal'
>
<X className='h-5 w-5 text-muted-foreground' />
</button>
{/* Modal Body Content */}
<div className='space-y-2'>
<h3 className='text-xl font-semibold text-card-foreground'>
Modal Title
</h3>
<p className='text-sm text-muted-foreground'>
This is the content of the modal. You can place any components
or elements you need inside here.
</p>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
);
}How it Works:
- When
isOpenbecomestrue, the modal components are added to the DOM and theirinitialtoanimatetransition plays. - When
isOpenbecomesfalse,<AnimatePresence>steps in. Instead of letting React remove the components instantly, it holds them in the DOM and tells them to play theirexitanimation. - Once the exit animation is complete,
AnimatePresencegracefully removes them.
This powerful abstraction turns a complex, manual process into a simple, declarative state description.
3. Effortless Interaction: Gestures as Props
Motion continues the declarative pattern for user interactions. Instead of setting up manual event listeners, you can simply describe how a component should look whileHover or whileTap.
The Code:
// src/components/InteractiveButton.tsx
'use client';
import { motion } from 'motion/react';
export function InteractiveButton() {
return (
<motion.button
// --- STYLING (with Tailwind CSS) ---
// These classes are based on Shadcn UI's default button for a clean,
// theme-aware, and accessible appearance.
className='
inline-flex items-center justify-center rounded-md px-4 py-2
text-sm font-semibold text-primary-foreground
bg-primary
shadow-sm
transition-colors
hover:bg-primary/90
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
disabled:pointer-events-none disabled:opacity-50
'
// --- HOVER ANIMATION (with Motion) ---
// We only animate the properties that Tailwind can't, like `scale`.
// The background color change is handled by Tailwind's `hover:` prefix.
whileHover={{ scale: 1.05 }}
// --- TAP ANIMATION (with Motion) ---
// Provides immediate physical feedback when the user clicks.
whileTap={{ scale: 0.95 }}
// --- TRANSITION PHYSICS ---
// A spring animation makes the scaling feel natural and responsive.
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
>
Hover and Click Me
</motion.button>
);
}This is the core value proposition of Motion: by describing the "what" (the states), you are freed from worrying about the "how" (the implementation). This leads to cleaner, more readable React components where the animation logic is a natural part of the component's definition.
Part 6: A Deep Dive into GSAP - The Animation Director
If Motion is the integrated architect, GSAP is the master director, giving you a professional-grade toolset to command any element on your stage. GSAP's imperative approach provides a level of control and precision that is unmatched, especially when you need to create complex, story-driven animations that go beyond simple state transitions.
It is renowned for its buttery-smooth performance, its reliability across all browsers, and an ecosystem of powerful plugins that can handle almost any animation challenge imaginable. Let's see how this "director's toolkit" empowers you to build.
1. The Powerhouse Feature: The Timeline
Remember the "pyramid of doom" and the nightmare of sequencing manual animations? GSAP's elegant solution is its most famous feature: the Timeline. A timeline is a container that acts as your director's script or a video editing sequence. You add animations to it one by one, and it handles all the complex timing for you.
The Use Case: A multi-step "product reveal" animation.
- A container card fades in.
- An image inside it slides in from the left.
- At the same time, the product title fades in.
- Finally, a price tag scales up from the center.

Awesome Gadget
A perfect blend of style and cutting-edge technology.
$99
The Code:
// src/components/ProductReveal.tsx
'use client';
import { useRef } from 'react';
import { useGSAP } from '@gsap/react'; // GSAP's official hook for React
import gsap from 'gsap';
import { Button } from '@/components/ui/button';
export function ProductReveal() {
// 1. THE REFS AND HOOKS
// We use a ref to get a direct, stable reference to the main container element in the DOM.
// This is how GSAP will "find" the component to animate.
const cardRef = useRef < HTMLDivElement > null;
// useGSAP() is a React hook that safely manages our GSAP animations.
// - It automatically handles cleanup, preventing memory leaks when the component unmounts.
// - The { scope: cardRef } option ensures our selectors (like ".product-image")
// only ever find elements *inside* this specific component instance.
useGSAP(
() => {
// 2. THE ANIMATION STRATEGY: "SET-THEN-TO"
// This is the most reliable pattern for complex "reveal" animations.
// Instead of animating FROM a hidden state (which can cause bugs), we first
// explicitly SET everything to its starting position, then animate TO its end position.
// --- STEP 1: SET the initial state ---
// Instantly hide the entire card. `autoAlpha` is a GSAP-specific property
// that handles both `opacity` and `visibility` for performance.
gsap.set(cardRef.current, { autoAlpha: 0 });
// Instantly place the image 100% of its own width to the left and hide it.
gsap.set('.product-image', { x: '-100%', autoAlpha: 0 });
// Instantly move the text elements down 20px and hide them.
gsap.set(['.product-title', '.product-desc'], { y: 20, autoAlpha: 0 });
// Instantly shrink the price and button down and hide them.
gsap.set(['.product-price', '.product-cta'], {
scale: 0.8,
autoAlpha: 0,
});
// --- STEP 2: Animate TO the final state using a Timeline ---
// A timeline is like a script or a video editor for your animations.
// It lets us sequence multiple animations in a controlled way.
// The `defaults` object applies these settings to every animation in the timeline.
const tl = gsap.timeline({ defaults: { ease: 'power2.out' } });
// Animation #1: Fade in the entire card container.
tl.to(cardRef.current, { autoAlpha: 1, duration: 0.4 });
// Animation #2: Slide in the image.
// `x: '0%'` animates it back to its default position.
// The Position Parameter `'-=0.2'` is a powerful timing control. It means:
// "Start this animation 0.2 seconds *before* the previous one has finished."
// This creates a smooth, overlapping effect.
tl.to(
'.product-image',
{ x: '0%', autoAlpha: 1, duration: 0.8 },
'-=0.2'
);
// Animation #3: Slide up and fade in the title and description.
// `stagger: 0.2` tells GSAP to animate these two elements sequentially,
// with a 0.2-second delay between the start of each.
tl.to(
['.product-title', '.product-desc'],
{ y: 0, autoAlpha: 1, stagger: 0.2 },
'-=0.5' // Start this sequence 0.5s before the image finishes sliding in.
);
// Animation #4: Scale up and fade in the price and button.
// The `ease: 'back.out(1.4)'` creates a fun "overshoot and settle" effect.
tl.to(['.product-price', '.product-cta'], {
scale: 1,
autoAlpha: 1,
stagger: 0.15,
ease: 'back.out(1.4)',
});
},
{ scope: cardRef }
);
return (
<div
ref={cardRef}
className='flex flex-col w-full max-w-xs rounded-xl border bg-card text-card-foreground shadow-md overflow-hidden'
>
<img
src='/gsap-vs-motion-guide-2026/awesome-gadget.jpg'
className='product-image w-full aspect-square object-cover'
alt='A modern, awesome gadget'
/>
<div className='flex flex-1 flex-col p-4 md:p-6'>
<div className='flex-1'>
<h3 className='product-title text-2xl font-bold tracking-tight'>
Awesome Gadget
</h3>
<p className='product-desc mt-1.5 text-sm text-muted-foreground'>
A perfect blend of style and cutting-edge technology.
</p>
</div>
<div className='mt-4 flex items-center justify-between gap-4'>
<p className='product-price text-xl font-semibold text-primary'>
$99
</p>
<Button size='sm' className='product-cta'>
Add to Cart
</Button>
</div>
</div>
</div>
);
}The Director's Control: The timeline gives you absolute, unambiguous control.
- Readable Script: The code reads like a set of instructions, making complex sequences easy to understand.
- Flexible Timing: The position parameter (
"<") is a director's note, allowing you to precisely overlap and orchestrate animations. You could also use"+=0.2"to say "start 0.2s after the previous animation ends." - Total Control: You can call methods like
tl.pause(),tl.reverse(),tl.progress(0.5), ortl.timeScale(2)to manipulate the entire sequence as a single unit.
2. The Unbeatable Ecosystem: Scroll-Based Animation with ScrollTrigger
One of the biggest reasons developers choose GSAP is for its world-class plugin ecosystem. The most famous and powerful of these is ScrollTrigger, a plugin that makes creating performant, complex scroll-based animations incredibly simple.
The Use Case: An "About Us" section where text slides in from the left and an image slides in from the right as the user scrolls them into view.
Scroll Down
To see the animation trigger.
About Our Mission
We believe in creating tools that are not only powerful but also a joy to use. Our mission is to blend cutting-edge technology with intuitive design, empowering creators to build the future of the web.

The Code:
// src/components/ScrollingSection.tsx
'use client';
import { useRef } from 'react';
import { useGSAP } from '@gsap/react';
import gsap from 'gsap';
// 1. IMPORT THE PLUGIN: We must import the specific GSAP plugin we want to use.
import { ScrollTrigger } from 'gsap/ScrollTrigger';
// 2. REGISTER THE PLUGIN: This line "activates" the ScrollTrigger plugin,
// making its features available to the main GSAP engine. This only needs to be done once in your project.
gsap.registerPlugin(ScrollTrigger);
export function ScrollingSection() {
// We use a ref to get a direct reference to the main <section> element in the DOM.
// This ref will serve as our "trigger" for the animation.
const containerRef = useRef < HTMLElement > null;
// useGSAP() is a React hook that safely manages our GSAP animations,
// including automatic cleanup when the component unmounts.
useGSAP(
() => {
// 3. THE STRATEGY: A SINGLE TIMELINE
// Instead of creating two separate animations with two separate ScrollTriggers,
// it's far more efficient to create a single timeline and attach ONE ScrollTrigger
// to the timeline itself. The timeline will then control all the animations inside it.
const tl = gsap.timeline({
// 4. THE SCROLLTRIGGER OBJECT: This is the brain of the scroll-based animation.
scrollTrigger: {
// `trigger`: This is the element that GSAP will watch. The animation's
// start and end points are calculated relative to this element's position.
trigger: containerRef.current,
// `start`: Defines when the animation should begin. It's a string with two values:
// [trigger] [scroller] -> "when the [top] of the trigger hits the [80% down] of the scroller (viewport)".
// In simple terms: "Start the animation when the top of our section is 80% down the screen."
start: 'top 80%',
// `end`: Defines where the animation should be considered "complete". This is useful
// for more complex scrubbing animations, but it's good practice to include.
end: 'bottom 20%',
// `toggleActions`: This is the most important property for controlling playback.
// It's a string of four keywords that define the behavior for:
// [onEnter] [onLeave] [onEnterBack] [onLeaveBack]
// - onEnter: Scrolling down, when the start point enters the viewport.
// - onLeave: Scrolling down, when the end point leaves the viewport.
// - onEnterBack: Scrolling up, when the end point re-enters the viewport.
// - onLeaveBack: Scrolling up, when the start point leaves the viewport.
// Our setting "play none none reverse" means:
// - `play`: When we scroll down into the section, play the animation.
// - `none`: When we scroll past the section, do nothing.
// - `none`: When we scroll back up into the section, do nothing.
// - `reverse`: When we scroll back up past the start, reverse the animation to hide it.
toggleActions: 'play none none reverse',
},
});
// 5. ADDING ANIMATIONS TO THE TIMELINE
// We are using `.from()` tweens, which animate FROM the specified values TO the element's default state.
// `autoAlpha` is a special GSAP property that animates `opacity` and also toggles `visibility`,
// which is more performant than animating opacity alone.
// Animation #1: The text column slides in from the left.
tl.from('.text-column', {
x: -200, // Start 200px to the left of its final position
autoAlpha: 0, // Start fully transparent and invisible
duration: 0.8,
ease: 'power2.out',
});
// Animation #2: The image column slides in from the right.
tl.from(
'.image-column',
{
x: 200, // Start 200px to the right of its final position
autoAlpha: 0,
duration: 0.8,
ease: 'power2.out',
},
// The Position Parameter `'<'`: This is a powerful timing control.
// It tells GSAP to start this animation at the exact same time as the
// start of the previous animation on the timeline. Without this, the image would
// wait for the text animation to finish completely before starting.
'<'
);
},
{ scope: containerRef } // Scoping ensures our selectors only find elements inside this component.
);
return (
<section
ref={containerRef}
className='w-full max-w-5xl mx-auto py-16 md:py-24 px-6'
>
<div className='grid grid-cols-1 md:grid-cols-2 gap-12 items-center'>
<div className='text-column space-y-4'>
<h2 className='text-3xl md:text-4xl font-bold tracking-tighter text-foreground'>
About Our Mission
</h2>
<p className='text-base md:text-lg text-muted-foreground'>
We believe in creating tools that are not only powerful but also a
joy to use. Our mission is to blend cutting-edge technology with
intuitive design, empowering creators to build the future of the
web.
</p>
</div>
<div className='image-column'>
<img
src='/gsap-vs-motion-guide-2026/scrolling-section.jpg'
alt='A team working collaboratively in a modern office'
className='w-full h-auto rounded-xl shadow-md'
/>
</div>
</div>
</section>
);
}
/**
* Helper component to demonstrate the scroll trigger.
* In a real app, this would just be the other content on your page.
* We need this empty space to have something to scroll through.
*/
export function ScrollTriggerDemo() {
return (
<div className='bg-background text-foreground'>
<div className='h-72 flex items-center justify-center text-center p-4'>
<div>
<h1 className='text-4xl font-bold'>Scroll Down</h1>
<p className='text-muted-foreground'>To see the animation trigger.</p>
</div>
</div>
<ScrollingSection />
</div>
);
}Why This is a Game-Changer:
ScrollTrigger abstracts away an enormous amount of complexity. Without it, you would need to write dozens of lines of code to manually track scroll positions with an IntersectionObserver or scroll event listeners, calculate positions, and manage performance. GSAP turns this into a simple, descriptive object. This is abstraction at its finest, providing immense speed and power.
Part 7: The Long Game, The Final Verdict, & Your First Challenge
We've journeyed through the fundamentals of motion, felt the pain of manual animation, and explored the profound philosophical differences between the declarative Architect (Motion) and the imperative Director (GSAP). You've seen the code, you've analyzed the showdown.
Now we arrive at the final, crucial question: which tool should you invest your time in to master for the long game?
The answer, like most things in development, depends on the career you want to build.
The Path of the UI/Product Engineer
If your primary focus is building functional, beautiful, and highly interactive user interfaces for web applications, then mastering Motion is a strategic career move.
Your world revolves around component states, user interactions, and application data. Motion is built for this world. Its declarative nature means your animations are a direct reflection of your component's state, leading to code that is cleaner, more predictable, and easier to maintain. For the vast majority of application UI work—animating modals, lists, buttons, and page transitions—Motion provides the fastest and most idiomatic path to a polished result within a React environment.
The Path of the Creative Developer or Agency Specialist
If you are drawn to the world of high-impact brand websites, immersive digital storytelling, and award-winning interactive experiences, then mastering GSAP is an absolute necessity.
This is the domain of the unique and the unforgettable. It requires cinematic hero sections, complex scroll-based narratives, and animations that are precisely choreographed to the millisecond. This level of custom storytelling demands the power and precision of GSAP's Timeline. Its unparalleled performance, versatility beyond the DOM (SVG, WebGL), and powerhouse plugins like ScrollTrigger are the undisputed industry standard for creating these top-tier web experiences.
The Ultimate Secret: Become Animation Bilingual
Here’s the insight that will set you apart: the best developers don't choose a side forever. They build a toolkit. They don't see this as a rivalry, but as having two specialized tools for different jobs.
The ultimate goal is to become bilingual.
Start with the library that best fits your immediate needs. If you're a React developer building apps, that's almost certainly Motion. Immerse yourself in it. But don't stop there. Once you're comfortable, take on a small project with GSAP. Learn the basics of its timeline.
The developer who can use Motion to quickly build a beautifully animated settings panel, and then switch to GSAP to craft a jaw-dropping, scroll-driven product reveal on the marketing page, is the developer who can build anything.
The Final Verdict
Let's distill it all down to a simple choice for today.
-
✅ Choose Motion if: You are building user interfaces in React or Next.js and you value a clean, state-driven workflow that feels like a natural extension of your components.
-
✅ Choose GSAP if: You need to create custom, precisely-timed sequences, build immersive scroll-based experiences, or want a powerful, framework-agnostic tool for any creative animation challenge.
The Decision Matrix: Your At-a-Glance Guide
This showdown makes the choice clearer. Here is a matrix to summarize the strengths and ideal use cases for each library.
| Aspect | Motion (The Architect) | GSAP (The Director) |
|---|---|---|
| Primary Mindset | Declarative: Describe states and relationships. | Imperative: Give direct, sequential commands. |
| Best Suited For | State-driven UIs, interactive gestures (hover/tap), layout animations, and exit animations in React. | Complex storytelling, cinematic sequences, scroll-based narratives, and high-performance creative animation. |
| React Integration | Seamless & Idiomatic. It feels like a natural part of the React component model. | Excellent & Robust. The useGSAP hook makes it safe and easy, but the mindset remains imperative. |
| Choreography | Handled via variants and staggerChildren, defining a system of rules. | Handled via the Timeline, providing a precise, script-like sequence of commands. |
| Learning Curve | Gentler for React developers, as the prop-based API is very familiar. | Steeper to master advanced features like the Timeline, but the basics are very straightforward. |
| Choose this if... | "...you want to quickly and easily bring your React UI to life with fluid, state-based animations." | "...you need to create a custom, precisely controlled animation sequence that tells a story." |
- Ready to build? Get Started with the Motion Docs
- Ready to direct? Get Started with the GSAP Docs