Stagger
Animate multiple targets in sequence with configurable delay, direction, and grid patterns.
stagger adds a per-element delay offset when animating multiple targets. Instead of all elements moving at once, each one starts slightly after the previous — creating a cascade effect.
Basic Stagger
Pass a number directly to stagger. Each element waits that many seconds more than the one before it.
import { Motion } from "@motion.page/sdk";
Motion("list", ".item", {
from: { opacity: 0, y: 20 },
duration: 0.5,
stagger: 0.08,
ease: "power2.out",
}).onPageLoad(); Element 1 starts at 0s, element 2 at 0.08s, element 3 at 0.16s, and so on.
StaggerVars Object
For full control, pass an object instead of a number. The shorthand stagger: 0.08 is equivalent to stagger: { each: 0.08, from: 'start' }.
| Property | Type | Default | Description |
|---|---|---|---|
each | number | — | Seconds between each element |
amount | number | — | Total spread across all elements (alternative to each) |
from | string | number | 'start' | Direction or origin of the stagger |
grid | 'auto' | [number, number] | — | Enable 2D grid staggering |
axis | 'x' | 'y' | — | Axis to measure distance for grid stagger |
ease | string | 'none' | Easing for stagger delay distribution |
each vs amount
Use each when you care about the gap between adjacent elements. Use amount when you care about the total time the full stagger takes regardless of element count.
// Always 0.1s between each element — total time grows with more elements
stagger: { each: 0.1 }
// Always 1s total spread — 10 elements = 0.1s each, 20 elements = 0.05s each
stagger: { amount: 1 } from — Stagger Origin
from controls which element starts first and how the cascade radiates outward.
| Value | Description |
|---|---|
'start' | First element leads (default) |
'end' | Last element leads — sequence runs in reverse |
'center' | Middle element leads, radiates outward |
'edges' | Both ends lead simultaneously, meet in the middle |
'random' | Random order on every play |
number | A specific element index leads (0-based) — e.g. from: 2 starts from the third element |
// Default — first to last
Motion("list-start", ".item", {
from: { opacity: 0, y: 16 },
duration: 0.5,
stagger: { each: 0.07, from: "start" },
ease: "power2.out",
}).onPageLoad();
// Reverse — last to first
Motion("list-end", ".item", {
from: { opacity: 0, y: 16 },
duration: 0.5,
stagger: { each: 0.07, from: "end" },
ease: "power2.out",
}).onPageLoad();
// Center outward — good for symmetrical layouts
Motion("list-center", ".item", {
from: { opacity: 0, scale: 0.8 },
duration: 0.4,
stagger: { each: 0.06, from: "center" },
ease: "back.out",
}).onPageLoad();
// Edges inward — both sides converge to the middle
Motion("list-edges", ".item", {
from: { opacity: 0 },
duration: 0.4,
stagger: { each: 0.06, from: "edges" },
}).onPageLoad();
// Random — each play has a different order
Motion("list-random", ".item", {
from: { opacity: 0, scale: 0.9 },
duration: 0.4,
stagger: { each: 0.05, from: "random" },
}).onPageLoad();
// Specific index — ripple from the third card
Motion("list-from-index", ".card", {
from: { opacity: 0, scale: 0.85 },
duration: 0.5,
stagger: { each: 0.06, from: 2 },
ease: "power2.out",
}).onClick({ each: true }); ease — Stagger Distribution
ease controls how the delay offsets are distributed across elements — not the easing of the animation itself. Without it, delays are evenly spaced. With it, elements bunch up at the start, end, or middle of the sequence.
// Delays accelerate — early elements are close together, later ones spread out
Motion("ease-in-stagger", ".dot", {
from: { opacity: 0, y: 20 },
duration: 0.4,
stagger: { each: 0.08, ease: "power2.in" },
}).onPageLoad();
// Delays decelerate — quick burst at the start, trailing off
Motion("ease-out-stagger", ".dot", {
from: { opacity: 0, y: 20 },
duration: 0.4,
stagger: { each: 0.08, ease: "power2.out" },
}).onPageLoad(); grid — 2D Stagger Patterns
Grid stagger measures the 2D distance from the origin element, creating ripple or wave effects across rows and columns.
Set grid: 'auto' and the SDK infers the grid dimensions from the layout of elements in the DOM. For irregular or custom grids, pass explicit [rows, columns].
// Ripple from center of a CSS grid
Motion("grid-ripple", ".cell", {
from: { opacity: 0, scale: 0.6 },
duration: 0.4,
stagger: {
each: 0.04,
from: "center",
grid: "auto",
},
ease: "power2.out",
}).onPageLoad();
// Explicit 3×4 grid, wave from top-left
Motion("grid-wave", ".tile", {
from: { opacity: 0, y: 20 },
duration: 0.5,
stagger: {
each: 0.05,
from: "start",
grid: [3, 4],
},
ease: "power2.out",
}).onPageLoad(); axis — Directional Grid Waves
Combine grid with axis to constrain stagger distance to a single axis — producing row-by-row or column-by-column waves instead of true radial ripples.
// Animate row by row (top to bottom)
Motion("row-wave", ".cell", {
from: { opacity: 0, y: 12 },
duration: 0.4,
stagger: {
each: 0.05,
grid: "auto",
axis: "y",
},
ease: "power2.out",
}).onPageLoad();
// Animate column by column (left to right)
Motion("col-wave", ".cell", {
from: { opacity: 0, x: 12 },
duration: 0.4,
stagger: {
each: 0.05,
grid: "auto",
axis: "x",
},
ease: "power2.out",
}).onPageLoad(); Common Patterns
Staggered Scroll Reveal
Motion("cards", ".card", {
from: { opacity: 0, y: 40 },
duration: 0.6,
stagger: 0.1,
ease: "power2.out",
}).onScroll({ scrub: false, toggleActions: "play none none none" }); Text Reveal with Stagger
Split text into words or characters and stagger them for a dramatic entrance.
Motion("headline", "h1", {
split: "words",
mask: true,
from: { y: "110%" },
duration: 0.6,
stagger: { each: 0.06, from: "start" },
ease: "power3.out",
}).onPageLoad(); See Text Splitter for the full text-splitting API.
Grid Ripple on Hover
Motion("grid-hover", ".cell", {
to: { scale: 1.1, backgroundColor: "#6633EE" },
duration: 0.3,
stagger: {
each: 0.03,
from: "center",
grid: "auto",
},
ease: "power2.out",
}).onHover({ onLeave: "reverse" }); Scene Delay + Stagger
Add a base delay to pause before the staggered sequence begins. The stagger offsets stack on top.
// Element 1 starts at 0.3s
// Element 2 starts at 0.38s
// Element 3 starts at 0.46s
Motion("delayed-list", ".item", {
from: { opacity: 0, x: -20 },
duration: 0.5,
delay: 0.3,
stagger: 0.08,
ease: "power2.out",
}).onPageLoad(); See Duration & Delay for how delay and stagger interact.
API Reference
// Shorthand — seconds between each element
stagger?: number
// Full object
interface StaggerVars {
each?: number; // Seconds between each element
amount?: number; // Total spread across all elements
from?: 'start' | 'end' | 'center' | 'edges' | 'random' | number;
grid?: 'auto' | [number, number]; // 2D grid stagger
axis?: 'x' | 'y'; // Grid stagger axis
ease?: string; // Easing for delay distribution
} Related: Duration & Delay · Text Splitter · Easing