How to Create Staggered Animations Across Multiple Elements with Anime.js
Use the stagger() utility exported from animejs to generate per-element delays or property values that automatically calculate offsets based on each target's index, with optional support for grid layouts, easing curves, and directional control.
Anime.js provides a declarative API for orchestrating complex motion, and the stagger utility defined in src/utils/stagger.js makes it trivial to create staggered animations across multiple elements. By passing stagger() as a property value in your animation configuration, you generate dynamic offsets that adapt to each element's position in the DOM collection, enabling everything from simple cascading delays to radial grid ripples without manual index calculations.
Core Implementation and Algorithm
The stagger functionality lives in src/utils/stagger.js, where the stagger(val, params = {}) function returns a callback that Anime.js invokes for each target during animation initialization. This callback receives the signature (target, i, t, tl) and computes the final value based on the element's position.
According to the source code in juliangarnier/anime, the internal algorithm performs six distinct steps:
- Determine the origin (
from) – defaults to the first element, but supports'center','last', a numeric index, or'random'. - Create the distance map (
values) on the first execution, calculating either linear indices or Euclidean distances when agridis provided. - Apply easing (
staggerEase) if an easing function or spring object is specified inparams.ease. - Reverse or shuffle the distance map when
reversed: trueorfrom: 'random'is set. - Scale the distances to the user-provided step size (
val) or range ([min, max]). - Add the start offset (
params.start) and optionalmodifierfunction before returning the final number or string.
This pure function design allows the same stagger configuration to be reused across different properties—delay, translateX, opacity, or scale—within a single animation.
Basic Delay Patterns
The most common use case for stagger() is creating incremental delays. Import stagger from animejs and pass it to the delay property.
Simple Incremental Delays
import { animate, stagger } from 'animejs';
animate('.item', {
translateX: 100,
duration: 800,
delay: stagger(100),
autoplay: true,
});
Each element receives a delay of index × 100ms, producing a sequence starting at 0ms, then 100ms, 200ms, and so on.
Staggered Values with CSS Units
When passed a string with units, stagger() detects and preserves the unit suffix for each computed value.
animate(document.querySelectorAll('#stagger div'), {
translateX: stagger('1rem'),
duration: 600,
});
This results in translateX values of 0rem, 1rem, 2rem, etc., allowing unit-aware staggered positioning.
Custom Start Offset
Use the start parameter to insert a base delay before the first element's offset begins.
animate('.item', {
translateX: 200,
delay: stagger(50, { start: 200 }),
});
Here, the first element waits 200ms, while subsequent elements add 50ms increments (200ms, 250ms, 300ms...).
Directional Control and Origin Points
By default, staggering proceeds from the first element to the last. The from and reversed parameters modify this directionality.
Origin from the Center
Setting from: 'center' calculates offsets based on distance from the middle of the collection, creating symmetric ripple patterns.
animate('.item', {
translateX: 100,
delay: stagger(80, { from: 'center' }),
});
Elements at the center receive the smallest delay, while peripheral elements wait longer.
Reverse Order and Specific Indices
Start the sequence from a specific index or reverse the entire order.
animate('.item', {
translateX: 100,
delay: stagger(60, { from: 1, reversed: true }),
});
This configuration begins at index 1 and counts backwards, effectively reversing the cascade direction.
Easing Stagger Values
Apply easing curves to stagger offsets to create non-linear timing patterns, such as accelerations or decelerations across the element group.
animate('.item', {
translateX: 100,
delay: stagger(120, { ease: 'inOutQuad' }),
});
Internally, parseEase('inOutQuad') produces a function that maps the linear distance to an easing curve, smoothing the delay distribution so elements cluster or spread according to the easing function's shape.
Grid-Based Spatial Layouts
For two-dimensional arrangements, the grid and axis parameters calculate spatial distances rather than simple indices.
Radial Grid Staggering
Treat elements as a matrix and stagger from the center outward.
animate('#grid div', {
scale: [1, 0],
delay: stagger(30, { grid: [5, 3], from: 'center' })
});
With grid: [5, 3], elements are treated as a 5-column by 3-row matrix. Distances compute from the grid center, yielding a radial ripple effect across the 2D plane.
Axis-Specific Cascades
Restrict staggering to a single dimension using axis: 'x' or axis: 'y'.
animate('#grid div', {
translateX: stagger(20, { grid: [5, 3], axis: 'x' }),
translateY: stagger(20, { grid: [5, 3], axis: 'y' })
});
This creates independent horizontal and vertical cascades, useful for wave-like effects that sweep across rows or columns specifically.
Timeline Scheduling
In createTimeline, staggered offsets become part of the timeline's scheduling system, automatically extending the total duration.
import { createTimeline, stagger } from 'animejs';
const tl = createTimeline({ defaults: { duration: 500 }, autoplay: false })
.add('.item', { translateX: 200 }, stagger(150));
console.log(tl.duration); // total length = last offset + animation duration
The third argument to .add() accepts a stagger callback, positioning each element's animation start time according to the calculated offset while maintaining precise timeline control.
Summary
- The
stagger(val, params)function insrc/utils/stagger.jsreturns a callback that computes per-element offsets based on index or grid position. - Basic syntax supports numeric steps (
stagger(100)) or unit strings (stagger('1rem')), with an optionalstartoffset. - Directional control uses
from: 'center' | 'last' | indexandreversed: trueto change the animation origin and flow. - Easing can be applied to stagger values via the
easeparameter for non-linear timing distributions. - Grid staggering treats elements as 2D matrices using
grid: [cols, rows]andaxis: 'x' | 'y'for spatial effects. - Timeline integration allows staggered offsets to schedule animation start times within
createTimelineinstances.
Frequently Asked Questions
How does the stagger function determine the delay for each element?
The stagger function in src/utils/stagger.js determines delays by first building a distance map based on each element's index relative to the specified from origin (first element, center, last, or random). It scales these distances by the provided step value (val), applies optional easing or reversal, and adds the start offset. The result is returned as a callback that Anime.js executes for each target during animation initialization, as verified in tests/suites/stagger.test.js.
Can stagger be used with CSS properties that require specific units?
Yes. When you pass a string containing units to stagger(), such as stagger('2rem') or stagger('10px'), the utility detects the unit suffix and appends it to each computed numeric value. This allows you to create staggered translations, margins, or other dimensional properties while maintaining valid CSS unit syntax.
What is the difference between linear and grid-based staggering?
Linear staggering (the default) calculates offsets based solely on the element's index in the one-dimensional array of targets. Grid-based staggering, activated by providing grid: [columns, rows], calculates Euclidean distances from the origin point within a two-dimensional matrix. Grid mode enables radial ripple effects and axis-specific cascades that respect spatial layout rather than just DOM order.
How do I reverse the order of a staggered animation without changing the DOM?
Pass reversed: true in the stagger parameters. This inverts the distance map after the origin is calculated, causing the animation to flow from the last element toward the first (or from the periphery toward the center when combined with from: 'center'). You can also set from: 'last' to start the calculation from the final element while maintaining forward progression.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →