Sign In

Access the AI Animation Generator and premium effects

or
Don't have an account? Create one

You've Hit Your Limit

Upgrade your plan to keep generating animations and unlock premium effects.

Monthly Yearly Save 17%
Regular
$12/mo
For individual developers
  • 50 AI generations per month
  • All text animation effects
  • Code export & copy
Integration Guide

GSAP + React

Master modern React integration patterns for GSAP animations. Learn the useGSAP hook, proper cleanup, refs, server components, and production-ready patterns for React and Next.js.

Chapter 1

Installation & Setup

To use GSAP with React, you need to install both the core GSAP library and the official React package. The @gsap/react package provides the useGSAP hook, which handles all the complexity of integrating GSAP with React's lifecycle and Strict Mode.

Terminal
npm install gsap @gsap/react

# For Next.js projects (same command)
# The @gsap/react package works with both React and Next.js

After installation, you can import GSAP and the useGSAP hook in your React components. The @gsap/react package is a lightweight wrapper around GSAP that handles React-specific concerns like cleanup and Strict Mode.

JavaScript/React
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { ScrollTrigger } from 'gsap/ScrollTrigger';

// Register plugins before using them
gsap.registerPlugin(ScrollTrigger);

Important Note

Always register GSAP plugins globally in your app or component. You only need to register each plugin once, typically at the top of your component file or in a main setup file. Registering plugins multiple times has no negative effects but is unnecessary.

Chapter 2

The useGSAP Hook

The useGSAP hook is the recommended way to use GSAP in React. It automatically handles cleanup, works properly with React 18 Strict Mode, and manages animation lifecycle. Without this hook, you'd need to manually kill animations when components unmount to prevent memory leaks.

The hook takes a callback function where you define your animations, and it ensures they're cleaned up when the component unmounts. This is much better than using raw useEffect because it understands GSAP's requirements.

FadeInComponent.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

export default function FadeInComponent() {
  const boxRef = useRef(null);

  useGSAP(() => {
    // Animation code runs here
    gsap.from(boxRef.current, {
      opacity: 0,
      y: 50,
      duration: 1,
      ease: 'power2.out'
    });
  });

  return (
    <div ref={boxRef} style={{ width: '100px', height: '100px', backgroundColor: '#00FF66' }}>
      Box
    </div>
  );
}
❌ Don't Do This
useEffect(() => {
  gsap.to(el, { x: 100, duration: 1 });
  // No cleanup! Animation
  // references leak in memory
}, []);
✓ Do This Instead
useGSAP(() => {
  gsap.to(el, { x: 100, duration: 1 });
  // Cleanup is handled automatically!
});

The useGSAP hook automatically kills all animations when your component unmounts. You don't have to remember to call gsap.killTweensOf() or manage cleanup manually. This is especially important in Single Page Applications where components mount and unmount frequently.

AnimatedButton.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef, useState } from 'react';

export default function AnimatedButton() {
  const buttonRef = useRef(null);
  const [isAnimating, setIsAnimating] = useState(false);

  // Re-run animation when isAnimating changes
  useGSAP(() => {
    if (isAnimating) {
      gsap.to(buttonRef.current, {
        scale: 1.1,
        duration: 0.3,
        ease: 'power2.out',
        onComplete: () => setIsAnimating(false)
      });
    }
  }, [isAnimating]); // Dependency array like useEffect

  return (
    <button
      ref={buttonRef}
      onClick={() => setIsAnimating(true)}
      style={{ padding: '10px 20px', fontSize: '16px' }}
    >
      Click me!
    </button>
  );
}

useGSAP Dependency Array

useGSAP accepts a dependency array just like useEffect. When dependencies change, the animation callback re-runs. This is how you re-trigger animations based on state changes. Always include dependencies if your animation relies on props or state.

Chapter 3

Refs and Element Targeting

GSAP needs to target DOM elements. In React, you use useRef to create references to DOM elements. The useGSAP hook's scope parameter allows you to target child elements using CSS selectors within that scope, making it safer and more predictable in React components.

There are two main approaches: using a ref to target a single element, or using the scope parameter to query child elements within a container. The scope approach is particularly powerful because it keeps your animations isolated to a specific component.

CardAnimation.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

export default function CardAnimation() {
  const cardRef = useRef(null);
  const imageRef = useRef(null);

  useGSAP(() => {
    // Animate the image inside the card
    gsap.to(imageRef.current, {
      scale: 1.05,
      duration: 0.6,
      ease: 'power2.out'
    });
  });

  return (
    <div ref={cardRef} className="card">
      <img ref={imageRef} src="image.jpg" />
      <h3>Product</h3>
    </div>
  );
}
ListAnimation.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

export default function ListAnimation() {
  const containerRef = useRef(null);

  useGSAP(() => {
    // Query all .list-item elements within containerRef
    // This is scoped—only animates items in THIS component
    gsap.from('.list-item', {
      opacity: 0,
      x: -50,
      duration: 0.6,
      stagger: 0.1,
      ease: 'power2.out'
    });
  }, { scope: containerRef }); // Scope parameter!

  return (
    <ul ref={containerRef}>
      <li className="list-item">Item 1</li>
      <li className="list-item">Item 2</li>
      <li className="list-item">Item 3</li>
    </ul>
  );
}

Why Use Scope?

The scope parameter is crucial in React because it keeps animations isolated to your component. Without scope, a CSS selector like '.list-item' would target elements throughout your entire page, potentially animating elements from other components. Always use scope when targeting child elements with selectors.

Best practice: Use refs for single elements, use scope + selectors for multiple child elements. This keeps your animations predictable and prevents conflicts when multiple instances of a component exist on the same page.

Chapter 4

contextSafe for Event Handlers

When you create event handlers (click, hover, etc.) that trigger animations, you need to use contextSafe. This method wraps your animation code so it's properly cleaned up when the component unmounts, preventing "memory leak" warnings in React Strict Mode.

contextSafe is a method returned from useGSAP that wraps functions to ensure they respect the component's lifecycle. It's essential when animations are triggered dynamically by events rather than immediately on mount.

❌ Don't Do This
const handleClick = () => {
  gsap.to(box, { x: 100 });
  // Causes warnings in Strict Mode
}

<button onClick={handleClick}>
✓ Do This Instead
const { contextSafe } = useGSAP();

const handleClick = contextSafe(() => {
  gsap.to(box, { x: 100 });
  // Properly cleaned up!
});

<button onClick={handleClick}>
InteractiveBox.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

export default function InteractiveBox() {
  const boxRef = useRef(null);
  const { contextSafe } = useGSAP();

  // Wrap event handlers with contextSafe
  const handleMouseEnter = contextSafe(() => {
    gsap.to(boxRef.current, {
      scale: 1.1,
      backgroundColor: '#00FF66',
      duration: 0.3,
      ease: 'power2.out'
    });
  });

  const handleMouseLeave = contextSafe(() => {
    gsap.to(boxRef.current, {
      scale: 1,
      backgroundColor: '#00CC88',
      duration: 0.3,
      ease: 'power2.out'
    });
  });

  return (
    <div
      ref={boxRef}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      style={{
        width: '100px',
        height: '100px',
        backgroundColor: '#00CC88',
        cursor: 'pointer'
      }}
    >
      Hover me!
    </div>
  );
}

contextSafe Usage

Destructure contextSafe from useGSAP and wrap any function that creates animations. This is required for click handlers, hover handlers, scroll listeners, and any other event-driven animations. It ensures cleanup happens properly when the component unmounts.

Chapter 5

ScrollTrigger in React

ScrollTrigger is GSAP's plugin for scroll-based animations. It works great in React with proper setup. The key is registering the plugin, using the scope parameter to target the right elements, and being mindful of cleanup when components with ScrollTriggers unmount.

ScrollTrigger creates listeners on scroll events. When a component with ScrollTrigger unmounts, those listeners must be cleaned up. The useGSAP hook handles this automatically, but you need to be careful with route changes in Single Page Apps.

ScrollReveal.jsx
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

gsap.registerPlugin(ScrollTrigger);

export default function ScrollReveal() {
  const containerRef = useRef(null);

  useGSAP(() => {
    // Animate elements as they scroll into view
    gsap.from('.reveal-item', {
      opacity: 0,
      y: 50,
      duration: 0.8,
      stagger: 0.2,
      ease: 'power2.out',
      scrollTrigger: {
        trigger: '.reveal-item',
        start: 'top 75%',        // When top of element is 75% down viewport
        end: 'top 25%',          // When top of element is 25% down viewport
        toggleActions: 'play none none none',
        markers: false           // Set to true for debugging
      }
    });
  }, { scope: containerRef });

  return (
    <div ref={containerRef}>
      <div className="reveal-item">Item 1</div>
      <div className="reveal-item">Item 2</div>
      <div className="reveal-item">Item 3</div>
    </div>
  );
}
ParallaxEffect.jsx
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

gsap.registerPlugin(ScrollTrigger);

export default function ParallaxEffect() {
  const layerRef = useRef(null);

  useGSAP(() => {
    // Scrub: tie animation progress to scroll position
    gsap.to(layerRef.current, {
      y: -100,              // Move up 100px
      ease: 'none',         // No easing for scrub
      scrollTrigger: {
        trigger: layerRef.current,
        start: 'top center',
        end: 'bottom center',
        scrub: 1,           // 1 second smoothing
        markers: false
      }
    });
  });

  return (
    <div
      ref={layerRef}
      style={{
        width: '100%',
        height: '200px',
        backgroundColor: 'rgba(0,255,102,0.1)',
        borderRadius: '8px'
      }}
    >
      Scroll to see parallax
    </div>
  );
}

ScrollTrigger Route Changes

In Next.js with Next/Link or other client-side routing, ScrollTriggers need special handling. When users navigate to a new page, refresh ScrollTrigger by calling ScrollTrigger.refresh() after new content mounts. This is especially important if your page length changes.

For comprehensive ScrollTrigger coverage, see our complete ScrollTrigger guide. ScrollTrigger is incredibly powerful but also has nuances with pinning, refreshing, and complex layouts.

Chapter 6

Timeline Patterns

Timelines are essential for coordinating multiple animations. In React, you typically create timelines inside useGSAP and store them in refs if you need to control them (play/pause/reverse). This pattern lets you sequence complex animations while respecting React's lifecycle.

A common pattern is storing the timeline in a ref so you can control it from event handlers. This is particularly useful for interactive animations where users trigger play/pause or reverse actions.

SequenceAnimation.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

export default function SequenceAnimation() {
  const tl = useRef(null);
  const box1Ref = useRef(null);
  const box2Ref = useRef(null);
  const box3Ref = useRef(null);

  const { contextSafe } = useGSAP(() => {
    // Create timeline with paused state
    tl.current = gsap.timeline({ paused: true });

    // Add animations in sequence
    tl.current
      .to(box1Ref.current, { x: 100, duration: 0.6 }, 0)
      .to(box2Ref.current, { y: -50, duration: 0.6 }, 0.2)
      .to(box3Ref.current, { rotation: 360, duration: 0.6 }, 0.4);
  });

  // Control timeline with buttons
  const handlePlay = contextSafe(() => tl.current.play());
  const handlePause = contextSafe(() => tl.current.pause());
  const handleReverse = contextSafe(() => tl.current.reverse());

  return (
    <div>
      <div ref={box1Ref} className="box">Box 1</div>
      <div ref={box2Ref} className="box">Box 2</div>
      <div ref={box3Ref} className="box">Box 3</div>

      <div style={{ marginTop: '20px' }}>
        <button onClick={handlePlay}>Play</button>
        <button onClick={handlePause}>Pause</button>
        <button onClick={handleReverse}>Reverse</button>
      </div>
    </div>
  );
}
StateTriggeredAnimation.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef, useState } from 'react';

export default function StateTriggeredAnimation() {
  const tl = useRef(null);
  const menuRef = useRef(null);
  const [isOpen, setIsOpen] = useState(false);

  useGSAP(() => {
    tl.current = gsap.timeline({ paused: true });

    tl.current
      .to(menuRef.current, { opacity: 1, duration: 0.3 })
      .to(menuRef.current, { x: 0, duration: 0.4 }, 0);

    // Play or reverse based on isOpen state
    if (isOpen) {
      tl.current.play();
    } else {
      tl.current.reverse();
    }
  }, [isOpen]); // Re-run when isOpen changes

  return (
    <>
      <button onClick={() => setIsOpen(!isOpen)}>
        {isOpen ? 'Close' : 'Open'} Menu
      </button>

      <div ref={menuRef} style={{ opacity: 0, x: '-100%' }}>
        <a href="#">Home</a>
        <a href="#">About</a>
        <a href="#">Contact</a>
      </div>
    </>
  );
}

Timeline Management

Always create timelines with { paused: true } when you plan to control them manually. Store timeline references in useRef, never directly in state. Use dependency arrays to re-trigger timelines when state changes. This pattern gives you full control over complex animation sequences.

Chapter 7

Next.js & Server Components

Next.js 13+ uses Server Components by default. Since GSAP manipulates the DOM and requires browser APIs, all GSAP code must run on the client. Use the 'use client' directive to mark components as client components.

The pattern is simple: mark any component that uses GSAP with 'use client' at the top. If you're using Next.js App Router with client components that import GSAP dynamically, everything works seamlessly.

components/HeroAnimation.jsx
'use client'; // Mark as client component

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

export default function HeroAnimation() {
  const textRef = useRef(null);

  useGSAP(() => {
    gsap.from(textRef.current, {
      opacity: 0,
      y: 50,
      duration: 1,
      ease: 'power2.out'
    });
  });

  return (
    <h1 ref={textRef}>
      Welcome to my Next.js site
    </h1>
  );
}
app/layout.tsx
// app/layout.tsx
import type { Metadata } from 'next';
import { HeroAnimation } from '@/components/HeroAnimation';

export const metadata: Metadata = {
  title: 'My Site',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        {/* Server Component */}
        {/* Client Component with GSAP */}
        <HeroAnimation />
        {children}
      </body>
    </html>
  );
}
app/page.tsx
import dynamic from 'next/dynamic';

// Lazy load component with animations
// Splits into separate bundle for better performance
const AnimatedComponent = dynamic(
  () => import('@/components/AnimatedComponent'),
  { ssr: false } // Don't render on server
);

export default function Page() {
  return (
    <main>
      <h1>My Page</h1>
      <AnimatedComponent />
    </main>
  );
}

Next.js Best Practices

Always use 'use client' when importing GSAP. For heavy animations, use dynamic imports with ssr: false to keep server bundle size small. This separates animation code into client-only bundles, improving performance. ScrollTrigger works great in Next.js—just remember to refresh it on route changes if needed.

Next.js works beautifully with GSAP. The main rule: mark components using GSAP with 'use client' and you're good to go. The rest of your application can stay server-rendered.

Chapter 8

Common Patterns

Here are real-world animation patterns you'll use repeatedly in React projects. Master these and you can build almost any animation interaction.

1. Enter/Exit Animations

Modal.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef, useState } from 'react';

export default function Modal() {
  const overlayRef = useRef(null);
  const modalRef = useRef(null);
  const [isOpen, setIsOpen] = useState(false);
  const { contextSafe } = useGSAP();

  useGSAP(() => {
    if (isOpen) {
      // Animate in
      gsap.to(overlayRef.current, {
        opacity: 1,
        duration: 0.3
      });
      gsap.to(modalRef.current, {
        opacity: 1,
        scale: 1,
        duration: 0.4,
        ease: 'back.out'
      }, 0);
    } else {
      // Animate out
      gsap.to(overlayRef.current, {
        opacity: 0,
        duration: 0.2
      });
      gsap.to(modalRef.current, {
        opacity: 0,
        scale: 0.8,
        duration: 0.3,
        ease: 'back.in'
      }, 0);
    }
  }, [isOpen]);

  const handleClose = contextSafe(() => {
    setIsOpen(false);
  });

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>

      <div
        ref={overlayRef}
        onClick={handleClose}
        style={{ opacity: 0, position: 'fixed', inset: 0 }}
      />

      <div
        ref={modalRef}
        style={{ opacity: 0, scale: 0.8, position: 'fixed' }}
      >
        Modal Content
      </div>
    </>
  );
}

2. Stagger Animations in Lists

ProductList.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef } from 'react';

export default function ProductList({ products }) {
  const containerRef = useRef(null);

  useGSAP(() => {
    // Animate all product items with stagger
    gsap.from('.product-item', {
      opacity: 0,
      y: 20,
      duration: 0.6,
      stagger: 0.1,
      ease: 'power2.out'
    });
  }, { scope: containerRef, dependencies: [products] });

  return (
    <div ref={containerRef}>
      {products.map((product) => (
        <div key={product.id} className="product-item">
          <h3>{product.name}</h3>
          <p>${product.price}</p>
        </div>
      ))}
    </div>
  );
}

3. Responsive Animations

ResponsiveAnimation.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef, useState, useEffect } from 'react';

export default function ResponsiveAnimation() {
  const boxRef = useRef(null);
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    // Check if mobile
    const checkMobile = () => setIsMobile(window.innerWidth < 768);
    checkMobile();
    window.addEventListener('resize', checkMobile);
    return () => window.removeEventListener('resize', checkMobile);
  }, []);

  useGSAP(() => {
    if (isMobile) {
      // Mobile animation (less intense)
      gsap.from(boxRef.current, {
        opacity: 0,
        x: 20,
        duration: 0.4,
        ease: 'power1.out'
      });
    } else {
      // Desktop animation (more complex)
      gsap.from(boxRef.current, {
        opacity: 0,
        x: -100,
        rotation: -45,
        duration: 0.8,
        ease: 'power2.out'
      });
    }
  }, [isMobile]);

  return <div ref={boxRef}>Responsive Box</div>;
}

Pro Tip: Kill Animations First

When animations might re-run, kill existing animations first to prevent conflicts: gsap.killTweensOf(el). This ensures clean state when dependencies change or users trigger the same animation multiple times quickly.

Chapter 9

Performance & Best Practices

GSAP is highly optimized, but there are patterns that make animations even better. Following these practices ensures smooth 60fps animations and clean React code.

Use Transforms

Animate x, y, rotation, scale, and opacity instead of top/left/width/height. Transforms are GPU-accelerated and much faster. GSAP automatically uses transforms under the hood for these properties.

Minimize Re-renders

Store timeline and animation references in useRef, not state. Changing state causes re-renders which you don't need. Use contextSafe for event handlers to prevent memory leaks without state updates.

Respect Motion Preferences

Check prefers-reduced-motion media query to disable animations for users who prefer them. This is a simple CSS check that shows respect for accessibility.

Clean Code Patterns

Always use useGSAP, never raw useEffect for GSAP. Use scope to isolate selectors. Wrap event handlers with contextSafe. These patterns prevent bugs and memory leaks.

Avoid Common Mistakes

Don't animate in loops without cleanup. Don't use document.querySelector globally (use scope instead). Don't forget contextSafe for events. Don't animate too many elements at once on mobile.

Test Performance

Use Chrome DevTools Performance tab to monitor frame rate. Target 60fps for smooth animations. On mobile, test on real devices—simulators don't show true performance.

AccessibleAnimation.jsx
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useRef, useState, useEffect } from 'react';

export default function AccessibleAnimation() {
  const elementRef = useRef(null);
  const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);

  useEffect(() => {
    // Check for prefers-reduced-motion
    const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
    setPrefersReducedMotion(mediaQuery.matches);

    const listener = (e) => setPrefersReducedMotion(e.matches);
    mediaQuery.addEventListener('change', listener);
    return () => mediaQuery.removeEventListener('change', listener);
  }, []);

  useGSAP(() => {
    if (prefersReducedMotion) {
      // Skip animation, just set final state
      gsap.set(elementRef.current, { opacity: 1, y: 0 });
    } else {
      // Play animation
      gsap.from(elementRef.current, {
        opacity: 0,
        y: 50,
        duration: 1,
        ease: 'power2.out'
      });
    }
  }, [prefersReducedMotion]);

  return <div ref={elementRef}>Accessible animation</div>;
}

React 18 Strict Mode

In development, React 18 Strict Mode intentionally double-invokes effects to catch bugs. useGSAP handles this properly—animations won't double-play. If using raw useEffect, you'll see double animations. This is why useGSAP is strongly recommended.

Frequently Asked Questions

No, but it's highly recommended. You can use GSAP with raw useEffect, but you'll need to manually manage cleanup with useEffect and kill animations on unmount. The @gsap/react package (specifically the useGSAP hook) handles all this automatically and is much cleaner. It also handles React 18 Strict Mode properly.

Yes, perfectly! The useGSAP hook is specifically designed to work with React 18 Strict Mode. Strict Mode intentionally double-invokes effects during development to catch bugs. useGSAP handles this gracefully—your animations won't double-play. If you use raw useEffect, you'll see animations trigger twice, which is why useGSAP is recommended.

Create exit animations in your current page that complete before the route changes. You can use useRouter from Next.js to detect navigation and trigger exit animations. Alternatively, use layout transitions with framer-motion or other libraries. Another approach: animate content in and out as users navigate between pages, using dynamic route segments to control what renders.

Yes! Next.js App Router works great with GSAP. Mark any component using GSAP with 'use client' at the top. The component becomes a client component while the rest of your app stays server-rendered. You can even use dynamic imports with ssr: false to keep your server bundle small. This is the recommended approach for Next.js 13+.

This is React 18 Strict Mode in development. Strict Mode intentionally double-invokes effects to help you catch bugs. If you're using useGSAP, it handles this properly and animations won't actually double-play (it kills the first and only plays once). If you're using raw useEffect, you'll see the double behavior. Use useGSAP to avoid this issue entirely.

Use the useGSAP hook—it automatically cleans up animations when components unmount. If using raw useEffect, you must manually kill animations in the cleanup function. Always wrap event handlers with contextSafe to ensure they're cleaned up properly. Never store animations in component state. Store timeline and animation references in useRef instead.

scope is a parameter to useGSAP that limits CSS selector queries to child elements within that ref. contextSafe is a function returned from useGSAP that wraps event handlers to ensure they're cleaned up properly. Scope is for targeting elements, contextSafe is for event handlers. You can use both in the same component.

Yes! GSAP has excellent TypeScript support. Both @gsap/react and gsap ship with TypeScript definitions. You can type refs as useRef<HTMLDivElement>(null) and get full intellisense. The @gsap/react package provides proper types for useGSAP hooks and contextSafe function.

React + GSAP Checklist

Do This
  • ✓ Use useGSAP hook for animations
  • ✓ Wrap event handlers with contextSafe
  • ✓ Use scope for scoped selectors
  • ✓ Store refs in useRef, not state
  • ✓ Register plugins once globally
  • ✓ Animate transforms (x, y, rotation)
  • ✓ Mark components 'use client' in Next.js
Avoid This
  • ✗ Raw useEffect with GSAP
  • ✗ Creating animations in event handlers without contextSafe
  • ✗ Global selectors without scope
  • ✗ Storing animations in state
  • ✗ Forgetting to clean up animations
  • ✗ Animating layout properties (width, height)
  • ✗ Ignoring React 18 Strict Mode warnings

Ready to Build Animated React Apps?

You've learned everything needed to integrate GSAP into your React and Next.js projects. From basic animations with useGSAP to complex interactive sequences with ScrollTrigger, you're equipped to create engaging, performant animations that users will love.