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
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.
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.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.
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.
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.
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'.
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.
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)
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/onUnmountedand callgsap.context()manually - A one-shot animation outside a React tree (e.g. a route-leave transition orchestrator) - call
gsap.to()directly
Used in these Annnimate components
- The Magnetic Button wraps its pointer-tracking tweens in
useGSAP+contextSafeso the magnetic pull cleans up on unmount - The Text Reveal component runs
SplitTextinsideuseGSAPand returns a cleanup that reverts the split before the next render - The Parallax component creates its ScrollTrigger inside
useGSAPso leaving the page kills the trigger without a manual unmount handler - The Accordion uses
useGSAPwith adependenciesarray to rebuild its open/close timeline when the active panel changes
See it running in production
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, addrevertOnUpdate: trueso 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 youruseGSAPcallback). Anything that runs later than the initial mount and creates a tween needs the wrapper. - Can I use useGSAP in a Server Component?
- No -
useGSAPneeds 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()?
useGSAPis a thin wrapper overuseEffect+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 callctx.revert()instead ofctx.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 callScrollTrigger.refresh()after async content loads, but cleanup is handled.
revert() call cleans them all up at once.NextHorizontal scrollHorizontal scroll is a scroll-driven layout where a full-viewport section is pinned in place and its inner track slides sideways as the user scrolls vertically.