A lightweight React utility for managing GSAP animation sequences in nested components. It addresses the common "React + GSAP timing trap" where child components' animations execute before the parent's due to React's bottom-up rendering and effect order. By using a shared context and queuing mechanism, this package ensures animations run in the desired sequence (e.g., parent first, then children) via a master timeline.
npm install gsap-react-queueOr with Yarn:
yarn add gsap-react-queueThis package requires @gsap/react, gsap, and react as peer dependencies.
Check out a working example on CodeSandbox: https://codesandbox.io/p/devbox/p4p7fp
In React applications using GSAP with the useGSAP hook, animations in nested components often face a timing issue:
- React's Rendering Order: React renders child components before completing the parent's render cycle. As a result, child effects (including
useGSAP) run first. - Unexpected Animation Sequence: If you want a parent element to animate before its children (e.g., a container fading in, then its items sliding up), the children might start animating prematurely. This leads to visual glitches, overlapping tweens, or incorrect sequencing.
- Common Scenarios: This "timing trap" appears in lists, modals, accordions, or any hierarchical UI where parent-child animation order matters. GSAP's timelines are powerful, but without coordination, nested
useGSAPcalls don't inherently respect a global order. - Why Not Just Pass Props?: While passing timelines via props works for simple cases, it becomes cumbersome in deeply nested or dynamic components (e.g., mapped lists). This package provides a declarative, hook-based queue that collects animations and executes them in sorted order on a shared timeline.
By wrapping your components in GSAPQueueProvider and using useGSAPQueue, animations are queued during the effect phase and executed sequentially after all components have mounted. You can override orders for fine control, ensuring parent → child flow without manual callbacks or refs propagation.
Wrap your app or the relevant component tree with GSAPQueueProvider. This creates a shared timeline and queue.
import { GSAPQueueProvider } from 'gsap-react-queue';
function App() {
return (
<GSAPQueueProvider>
<ParentComponent />
</GSAPQueueProvider>
);
}In your components, use useGSAPQueue to add animations to the queue. The hook takes a function that receives gsap and the shared timeline, allowing you to add tweens in order.
- Animations are auto-queued with incremental orders (0, 1, 2...).
- Use
orderOverridefor explicit control (lower numbers execute first).
import { useGSAPQueue } from 'gsap-react-queue';
import { useRef } from 'react';
function ParentComponent() {
const containerRef = useRef<HTMLDivElement>(null);
useGSAPQueue((gsap, timeline) => {
// Parent animation (order 0 by default)
timeline.from(containerRef.current, { opacity: 0, duration: 1 });
});
return (
<div ref={containerRef}>
<ChildComponent />
</div>
);
}import { useGSAPQueue } from 'gsap-react-queue';
import { useRef } from 'react';
function ChildComponent() {
const itemRef = useRef<HTMLDivElement>(null);
useGSAPQueue((gsap, timeline) => {
// Child animation (auto-assigned order 1)
timeline.from(itemRef.current, { y: 50, opacity: 0, duration: 0.5 });
});
return <div ref={itemRef}>Child Content</div>;
}In this setup:
- The parent's fade-in starts first.
- The child's slide-up follows sequentially on the same timeline.
For explicit ordering or cleanup:
useGSAPQueue((gsap, timeline) => {
timeline.to(someElement, { x: 100 });
// Return a cleanup function if needed
return () => {
gsap.killTweensOf(someElement);
};
}, 2); // orderOverride = 2 (executes after lower orders)For more control, access the shared context:
import { useGSAPQueueContext } from 'gsap-react-queue';
function SomeComponent() {
const { timeline, gsap, contextSafe } = useGSAPQueueContext();
// Use timeline directly for complex logic
timeline.to(...);
}gsap: Override the GSAP instance (defaults to importedgsap).timeline: Provide a custom timeline (defaults to a newgsap.timeline()).
- GSAPQueueProvider({ children, gsap?, timeline? }): Context provider for queuing.
- useGSAPQueue(fn: (gsap, timeline) => void | (() => void), orderOverride?: number): Hook to queue an animation function.
- useGSAPQueueContext(): Returns
{ queue, nextOrder, timeline, gsap, contextSafe }for direct access.
See the repository for issues and pull requests.
MIT