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.

By Satish Kumar November 10, 2025
GSAP vs. Motion: Which Animation Library to Choose in 2026

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, or backgroundColor.
  • Transformation: This is the secret to buttery-smooth, high-performance animation. Instead of changing properties like width that can force the browser to recalculate the entire page layout, transform operates 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.

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 linear ease means constant speed. It feels unnatural and mechanical for most UI movements.
    • An ease-out is very common. The animation starts fast and decelerates to a smooth stop. It feels responsive.
    • An ease-in-out is 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.

  1. The container fades in.
  2. Then, the title slides into place.
  3. 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:

  1. Triggering the "exit" state without actually removing the data.
  2. Adding a specific CSS class (e.g., .exiting).
  3. Using a setTimeout or waiting for an animationend event.
  4. 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:

  1. When isOpen becomes true, the modal components are added to the DOM and their initial to animate transition plays.
  2. When isOpen becomes false, <AnimatePresence> steps in. Instead of letting React remove the components instantly, it holds them in the DOM and tells them to play their exit animation.
  3. Once the exit animation is complete, AnimatePresence gracefully 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.

  1. A container card fades in.
  2. An image inside it slides in from the left.
  3. At the same time, the product title fades in.
  4. Finally, a price tag scales up from the center.
A modern, awesome gadget

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), or tl.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.

A team working collaboratively in a modern office

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.

AspectMotion (The Architect)GSAP (The Director)
Primary MindsetDeclarative: Describe states and relationships.Imperative: Give direct, sequential commands.
Best Suited ForState-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 IntegrationSeamless & 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.
ChoreographyHandled via variants and staggerChildren, defining a system of rules.Handled via the Timeline, providing a precise, script-like sequence of commands.
Learning CurveGentler 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."

Subscribe to my free Newsletter

Join my weekly newsletter to get the latest updates on design engineering, new projects, and articles I've written. No spam, unsubscribe at any time.