React

useGSAP

useGSAP is the official React hook from `@gsap/react` that scopes GSAP animations to a component and cleans them up automatically on unmount. It replaces `useEffect` for GSAP work, handles StrictMode's double-invocation correctly, and exposes `contextSafe` for wrapping event handlers that create tweens.

Updated September 6, 2026

Mechanics

How useGSAP works

useGSAP wraps gsap.context() under the hood. Pass a callback that creates your tweens and timelines; the hook records everything inside that context and reverts it on unmount. Any element you query inside the callback is scoped to the scope ref you pass in, so animations stay local to the component instance.

Component.jsx

The second argument can also be a dependencies array. The hook re-runs the callback (after reverting the previous context) whenever a dependency changes, which is how you wire props or state into the animation.

@gsap/react was downloaded 1.39 million times in the week of 23 to 29 August 2026, against 4.78 million for gsap itself (npm download counts, api.npmjs.org). Roughly one in three GSAP installs now runs through the React hook, which is why the patterns on this page are the ones you will meet in other people's codebases.
StrictMode

useGSAP vs useEffect: StrictMode, dependencies and scope

StrictMode runs effects twice

React 18 and 19 mount, unmount and remount every component once in development StrictMode. A plain useEffect that calls gsap.from() therefore creates the tween twice, and the second from starts from the already-animated state, so the element flashes or never animates. useGSAP reverts the whole context on the simulated unmount and rebuilds it on the remount, so the animation runs once and looks the same in development and production.

dependencies and revertOnUpdate

The config object accepts dependencies (the same idea as a useEffect array) and revertOnUpdate. By default the hook only re-runs the callback when a dependency changes and leaves the old tweens in place. Set revertOnUpdate: true when the animation should start fresh on every change, for example a state-driven open and close where a half-finished tween must not survive.

Component.jsx

scope

scope takes a ref. Every selector string inside the callback is then resolved against that element only, so two instances of the same component on one page never grab each other's .panel. Without scope, a class selector reaches the whole document, which is the most common cause of one card animating all cards.

Next.js

useGSAP in the Next.js App Router

Server Components cannot animate. Put the hook in a file that starts with 'use client', register plugins in that same file, and never touch window at module scope. GSAP core is safe to import on the server, but plugins such as ScrollTrigger read the viewport when registered, so registration belongs inside the client component.

components/Reveal.jsx

The hook returns nothing you need to store. On route change Next.js unmounts the client component, the context reverts, and every ScrollTrigger created inside it is killed with it. That is the whole reason to prefer useGSAP over a hand-written cleanup: forgotten ScrollTrigger.kill() calls are the usual source of 'my scroll animation jumps after navigating back'.

contextSafe

Use contextSafe for event handlers

Tweens created outside the initial callback - inside an onClick, a mouseenter handler, an effect that runs later - won't be tracked by the context and won't be reverted on unmount. Wrap those handlers in contextSafe so the same auto-cleanup applies.

Component.jsx
When

Use useGSAP for

  • Any GSAP animation inside a React component (the canonical pattern)
  • Scroll-driven reveals where ScrollTrigger should auto-clean on unmount
  • Hover and click interactions that create tweens on the fly (contextSafe)
  • Animations that depend on props or state and need to rebuild on change (dependencies)
Alternatives

Use something else when

  • Vanilla JS or non-React framework - use gsap.context() directly, or no context at all
  • Vue components - use template refs + onMounted / onUnmounted and call gsap.context() manually
  • A one-shot animation outside a React tree (e.g. a route-leave transition orchestrator) - call gsap.to() directly
In production

Used in these Annnimate components

  • The Magnetic Button wraps its pointer-tracking tweens in useGSAP + contextSafe so the magnetic pull cleans up on unmount
  • The Text Reveal component runs SplitText inside useGSAP and returns a cleanup that reverts the split before the next render
  • The Parallax component creates its ScrollTrigger inside useGSAP so leaving the page kills the trigger without a manual unmount handler
  • The Accordion uses useGSAP with a dependencies array to rebuild its open/close timeline when the active panel changes
Used in components

See it running in production

FAQ

Common questions

Do I need useGSAP if I'm only animating once on mount?
Yes - even a one-shot mount animation should use useGSAP. Without it, you'll either leak the animation on unmount or hit StrictMode's double-invocation in dev and see the animation run twice. The hook handles both for free.
Does useGSAP run twice in React StrictMode?
useGSAP runs its callback twice in development StrictMode, once on the simulated mount and once on the real one, but it reverts the first context before the second run, so you see one animation. A plain useEffect with gsap.from() runs twice without reverting, which is what causes the flash or the missing animation.
How do I re-run useGSAP when props or state change?
useGSAP re-runs its callback when a value in the dependencies array changes. Pass { dependencies: [open] } and, if a half-finished tween must not survive the change, add revertOnUpdate: true so the previous context is reverted before the callback runs again.
Why aren't my hover tweens cleaning up?
Tweens created inside event handlers aren't auto-tracked. Wrap each handler in contextSafe (provided as the second argument to your useGSAP callback). Anything that runs later than the initial mount and creates a tween needs the wrapper.
Can I use useGSAP in a Server Component?
No - useGSAP needs the DOM. The component that uses it must be a Client Component ('use client' at the top). The page itself can stay a Server Component; only the animated leaf needs to be client.
What's the difference between useGSAP and useEffect with gsap.context()?
useGSAP is a thin wrapper over useEffect + gsap.context() + a StrictMode-aware cleanup. You could write it yourself, but you'd have to handle the double-invocation correctly (revert on cleanup, not just kill tweens) and remember to call ctx.revert() instead of ctx.kill(). The hook does both correctly.
Does useGSAP work with ScrollTrigger?
Yes - any ScrollTriggers created inside the callback are killed when the context reverts. You still need gsap.registerPlugin(ScrollTrigger) once at module load, and you still need to call ScrollTrigger.refresh() after async content loads, but cleanup is handled.