Timeline

stagger

Stagger is a GSAP property that offsets the start time of each target in a tween, animating them one after another instead of all at once. It's the single most-used tool for making a list, grid, or sequence of elements feel choreographed rather than mechanical.

Updated June 1, 2026

Mechanics

How stagger works

The simplest form is a number - the delay in seconds between each target's start time:

script.js

For 10 items at stagger: 0.08, the first item starts at t=0, the last at t=0.72, and the whole sequence finishes at t=0.72 + 0.6 = 1.32s.

Object form

The richer config

script.js

The two eases compose: ease: "expo.out" is the per-element animation curve, stagger.ease is how the START TIMES distribute across the set. A power2.in stagger ease makes early items cluster close together and later items spread out - useful for 'spilling outward' effects.

When

Use stagger for

  • Reveal animations on lists, cards, or grids
  • Character-by-character text reveals (paired with SplitText)
  • Grid-of-tiles entrance animations
  • Sequencing logo paths or SVG elements
  • Anywhere 'one after another' reads better than 'all at once'
Alternatives

Use something else when

  • Single elements - just don't pass stagger
  • True asynchronous sequencing - use a GSAP timeline with explicit position parameters per tween
  • Physics-based delays - stagger is deterministic; for true randomness use from: "random" or compute delays per-target
In production

Used in these Annnimate components

  • Dual Scramble staggers characters by 30ms with from: "start" for a left-to-right read
  • The Magnetic Button uses stagger internally on its label characters when scrambling
Used in components

See it running in production

FAQ

Common questions

What's the difference between stagger.each and stagger.amount?
each is the delay between each target's start (in seconds). amount is the total time the staggered set should span - GSAP calculates each automatically as amount / count. Use each when you want a fixed rhythm regardless of count; use amount when you want the total animation length to stay constant as the count changes.
How do stagger.from values like 'start', 'center', 'edges' work?
They define WHICH target gets index 0 - the one that starts first. start (default): first child first. end: last child first. center: middle child first, rippling outward. edges: first and last simultaneously, rippling inward. random: shuffled order. For grids, you also have from: [0.5, 0.5] which uses normalized 2D coordinates to pick a focal point.
Why isn't my grid stagger respecting the rows/columns?
You need to pass stagger: { grid: [rows, cols], ... }. Without grid, GSAP treats the targets as a 1D list and staggers in DOM order. With grid: [3, 4], GSAP knows the layout and can stagger by distance from a focal point (from: 'center') instead of by index.
Can I combine stagger with a position parameter on a timeline?
Yes. The position parameter ('<', '>', '+=0.5') decides WHEN the staggered batch starts; the stagger itself controls the rhythm within. Common pattern: tl.to(items, { ... stagger: 0.05 }, 'reveal') to fire all staggered items together with another tween labeled 'reveal'.