Nested timelines
Nested timelines are GSAP timelines added inside another timeline as a single, self-contained unit, so a whole sub-sequence can be positioned, delayed, sped up, or reversed as one object. You build each piece as its own timeline, then drop it into a master timeline with `master.add(childTimeline, position)`.
Updated July 1, 2026
How nested timelines work
A GSAP timeline is itself an animation, so a timeline can hold another timeline the same way it holds a tween. You call master.add(child) and the child sequence gets placed on the master's playhead. From that point the master treats the whole child as one unit: it has a start time, a duration (set by its own children), and it plays, pauses, and reverses as a block.
The add() call takes a position parameter just like a tween does, so you can drop the child at an absolute time, relative to the master's end ("+=1"), or at a label. Wrapping each section in a function that returns a timeline is the pattern that makes this pay off. You build the sequence once and reuse it, and the master file reads as a list of named parts instead of two hundred chained tweens.
Timing propagates down the tree. If you call master.timeScale(2) the whole thing runs at double speed including every nested child, and master.reverse() reverses the children in order. That is the real reason to nest: you get one control surface over a sequence that is actually built from many independent pieces.
Use nested timelines for
- Page or section intros where each block (header, hero, cards) is its own self-contained reveal you sequence together
- Open and close animations where the same sub-sequence needs to play forward on open and reverse on close as one unit
- Reusable animation modules, build a
buildCardReveal()once and add it wherever a card enters - Any master sequence that got long enough that chaining raw tweens with
+=/-=offsets became hard to read or reorder - Coordinating several independent parts (a background wipe, a drawer, a staggered list) so one
play()/reverse()drives all of them
Use something else when
- The sequence is short (two or three tweens) - just chain them on one timeline, a nested structure is overhead you don't need
- You only need staggered copies of the same tween - use the
staggerproperty on a single.to()instead of hand-building N sub-timelines - It's a one-off entrance with no reverse and no reuse - a plain tween or a flat timeline is simpler
- The effect is a single CSS state change (hover color, simple fade) - most of the web ships this with a CSS transition and no JS timeline at all
That last point is worth grounding: 91.7% of mobile pages use a CSS transition, but only 18.4% load any JS animation library at all (HTTP Archive Web Almanac 2024). CSS covers the simple state changes. A timeline library earns its place exactly when you need to orchestrate a multi-part sequence, and nesting is how you keep that orchestration readable as it grows.
Used in these Annnimate components
Nesting shows up wherever a component has more than one moving part that has to open and close as a set:
- The Mega Menu builds separate timelines for the background reveal, the mobile drawer, and the staggered nav items, then plays and reverses them as a coordinated set on open and close, so the whole menu has one control surface even though it's three independent sequences underneath
- The Multi Flip packages its staggered flip tweens into a single paused timeline that the scroll trigger plays forward on enter and reverses on leave, the same building block, one controllable object instead of loose tweens
Don't nest a ScrollTrigger
scrollTrigger on the top-level master timeline, never on a tween or timeline nested inside it. A ScrollTrigger on a nested child fights the master's playhead and behaves unpredictably. Build the child timelines clean, add them to the master, and attach the scroll control to the master only.See it running in production
Common questions
- How do I add one timeline inside another in GSAP?
- Use
master.add(childTimeline, position). The child is treated as a single unit placed on the master's playhead at the position you pass (an absolute time, a relative offset like"+=0.5", or a label). The child keeps its own internal timing, the master just controls when it starts. - Does timeScale on the master affect nested timelines?
- Yes. Timing propagates down the whole tree.
master.timeScale(2)runs every nested child at double speed too, andmaster.reverse()reverses the children in order. That single point of control is the main reason to nest instead of chaining loose tweens. - What position does a nested timeline start at if I don't pass one?
- It appends to the end of the master, same as a tween.
master.add(child)with no position drops it right after whatever was added last. Pass a position parameter (0,"-=0.2","myLabel") when you want it to overlap or sit at a specific time. - Why isn't my nested timeline's duration what I set?
- A timeline's duration is determined by its children, not by a value you pass to the constructor. If the nested timeline is shorter or longer than expected, check the child tweens and their positions. The
durationoption ongsap.timeline({ duration })does not force a length the way it does on a tween. - Should I nest timelines or just use one flat timeline?
- Use a flat timeline for short sequences. Nest once the sequence gets long, once a sub-sequence needs to be reused, or once you want to play or reverse a whole block as one unit. Wrapping each part in a function that returns a timeline keeps the master file readable as a list of named parts.
.to() / .from() / .set() call.