gsap.context()
gsap.context() is a GSAP method that collects every animation, timeline, and ScrollTrigger created inside a function so one `revert()` call cleans them all up at once. Its companion contextSafe wraps callbacks that run later (event handlers, delayed setup) so the animations they create also get tracked and reverted. Together they are how you stop GSAP from leaking tweens and firing on detached nodes when a React component unmounts or re-renders.
Updated June 30, 2026
How gsap.context and contextSafe work
React is the most-used framework at 69.9% developer usage (State of JS 2025), and its mount/unmount cycle is exactly what breaks naive GSAP code: tweens outlive the nodes they target. gsap.context() is GSAP's answer to that, documented as the cleanup primitive for React in the official guide (gsap.com/resources/React).
gsap.context(func, scope) runs func immediately and records every GSAP object created inside it. The optional second argument scopes selector strings, so gsap.to(".box", ...) inside the context only matches .box elements within that ref or element, not the whole page. The call returns a context object with a revert() method.
When you call ctx.revert(), it kills every tween, timeline, and ScrollTrigger the context recorded, and rolls inline styles back to where they started. In React you call it in the effect cleanup, so unmounting a component leaves no live tweens animating nodes that no longer exist.
Why contextSafe exists
A context only records what runs while its function executes. Anything created afterward, like a tween inside a mousemove handler, is created outside that window, so the context never sees it and never reverts it. contextSafe solves that. It wraps a function so any GSAP created inside it is added back into the context, and the wrapped function no-ops once the context has been reverted, which is what stops a late pointer event from animating an unmounted node.
useGSAP wraps this for you
useGSAP() from @gsap/react runs your setup inside a gsap.context() and calls revert() on unmount automatically. You rarely call gsap.context() by hand in React anymore, but useGSAP returns contextSafe precisely because event-handler tweens still need it.Reach for it when
- Any GSAP code in a React or Next.js component, so unmount cleanup is automatic instead of hand-killed
- Pointer or scroll handlers that create tweens after setup runs (magnetic hover, drag, cursor-follow) - wrap each in
contextSafe - A component that mounts and unmounts repeatedly (modal, drawer, route change) where leaked ScrollTriggers would pile up
- Scoping selector strings to one component so
.titledoesn't accidentally animate another section's.title
Use something else when
- You're in plain Vue or Svelte, not React - use the framework's lifecycle hook plus
gsap.context()directly, oronUnmounted/onDestroyto callrevert() - You're in vanilla JS with no component lifecycle - a context still helps for teardown, but a single
gsap.globalTimelineor manual.kill()is often enough - You only need to kill one specific tween - keep its reference and call
tween.kill()rather than reverting a whole context - You want to keep the end state instead of rolling styles back -
revert()undoes inline styles, so for a persistent final pose usetween.kill()orclearPropsselectively
Used in these Annnimate components
Every Annnimate React component runs its GSAP inside useGSAP with a scope ref, which is gsap.context() underneath. Two show both halves of the pattern:
- The Magnetic Button wraps its
mouseenter,mousemove, andmouseleavehandlers incontextSafeso the cursor-follow tweens created on every pointer move are tracked by the context and stop cleanly when the button unmounts - The Text Reveal creates a
SplitTextinstance and its line-mask tweens inside a scopeduseGSAP, so the context reverts the split (puts the original markup back) and kills the ScrollTrigger on unmount instead of leaving orphaned line wrappers in the DOM
The leak you won't see in dev
contextSafe keeps running after unmount and throws 'cannot animate a null target' or silently mutates a detached node. It often passes a quick local test and only shows up after repeated mount/unmount cycles. If a component animates fine once but breaks after navigating away and back, an un-contextSafe handler is the usual cause.See it running in production
Common questions
- What's the difference between gsap.context and contextSafe?
gsap.context()records the GSAP objects created while its function runs and gives you arevert()to clean them up.contextSafeis for code that runs later, after that function finished, like an event handler. It re-adds those tweens into the context so they get reverted too, and makes the handler no-op once the context is gone. Setup uses context; anything triggered after setup uses contextSafe.- Do I still need gsap.context if I use useGSAP?
- Not directly.
useGSAP()runs your callback inside agsap.context()and callsrevert()for you on unmount, so you almost never writegsap.context()by hand in React. You still needcontextSafe, though, which is whyuseGSAPhands it to you. Reach for rawgsap.context()only when you're not using the hook, for example inside a plainuseEffectyou control. - Why does my GSAP event handler animate after the component unmounts?
- Because the tween was created inside a handler that ran after
useGSAP/gsap.context()finished, so the context never recorded it andrevert()never killed it. Wrap the handler incontextSafeand remove the listener in the cleanup return. Both steps matter: contextSafe tracks the tween, removing the listener stops the handler firing at all. - Does ctx.revert() remove the final state of my animation?
- Yes.
revert()rolls inline styles back to what they were before the context ran, which is exactly what you want on unmount but not if you wanted to keep the end pose. To hold a final state, kill the specific tween withtween.kill()instead of reverting the whole context, or write the end values to the element outside GSAP. - Can I scope selector strings to one component with gsap.context?
- Yes, that's the second argument. Pass a ref or element as the scope and selector strings inside the context only match descendants of it. So
gsap.to('.title', ...)animates the.titlein this component, not every.titleon the page. WithuseGSAPyou pass the same thing as{ scope: containerRef }.
@gsap/react that scopes GSAP animations to a component and cleans them up automatically on unmount.