Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 15.2Advanced ~19 min · shot 2 of 7

Variants and Orchestration

In one line

Variants give animation states names. A parent that switches to a named state tells every child to switch to the same name, so one prop drives a whole group, and staggerChildren, delayChildren and when set the order.

0/7 · 0%

Think of it like this

A theatre director calls "Act two". Every actor knows what Act two means for them: the lights change, one walks on, one leaves. The director does not give each actor a script line by line. Variants are those named acts; each component knows what "visible" means for itself.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

variant
A named animation state, such as hidden or visible, defined once in an object and used by name.
propagation
A parent's variant name flowing down to children that have variants and no animate of their own.
staggerChildren
A transition setting on the parent: each child starts this many seconds after the previous one.
delayChildren
A transition setting on the parent: all children wait this long before the first one starts.
when
"beforeChildren" or "afterChildren": whether the parent's own animation finishes before or after its children's.
orchestration
Deciding the order and timing of many animations from one place.

Step by step

01Define the states

Write list and item variant objects. item says what hidden and visible look like for one row. list holds only the *transition* that orders its children.

02Switch one prop

The ul has animate={shown ? "visible" : "hidden"}. The li elements have variants={item} and nothing else. Press Hide: the log shows the list, then every item, switching to hidden.

03Read the delays

Press Hide, then look at the tracks: three opacity tracks with delays of 80, 40 and 0 ms (the last item first). Press Show: delays 100, 180 and 260 ms.

04Slow the stagger

Change staggerChildren: 0.08 to 0.3. Show now starts the items at 100, 400 and 700 ms: delay is delayChildren + stagger × index.

Try it yourself

  1. 1

    Slow the stagger

    Set staggerChildren: 0.3 and press Hide then Show (or open the Timeline after Show). Measured: the three opacity tracks start at 100, 400 and 700 ms.

  2. 2

    Mistype a variant name

    Change "visible" to "visibl" in the animate prop. The console still prints list starts: visibl, but at 400 ms the first item is at opacity 0 and 24 px left (measured): nothing animates.

Code & diagrams

Variants, propagation and stagger

The ul switches state and every li follows. Hide runs last-to-first, Show first-to-last. The console lists who starts in what order.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

list starts: visible
Lock starts: visible
Lights starts: visible
Bell starts: visible
A child with its own animate

A inherits "on". B's string `animate` keeps it out of the group. C's object `animate` does not: it also gets "on", and ends at opacity 1.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

B (own animate): "off"
C (an object): {"opacity":0.7}
A (inherits): "on"
C (an object): "on"

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Give a child an object animate

In the second example, add animate={{ opacity: 0.7 }} to item A.

terminal
$ Reload and read the console and the final opacity
── what you'll see ──
A logs its own object first, then "on", and ends at opacity 1, not 0.7.

Myth vs fact

Myth

Children need initial and animate to follow the parent.

Fact

They need only variants with matching names. Adding their own animate string opts them out.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    On the Timeline, a spring's delay is folded into its linear() curve: with delays of 80, 40 and 0 ms, the three transform tracks last 430, 390 and 350 ms and all show delay 0. Read the opacity tracks for the real delays.

  • ▸

    custom passes data into variant functions: visible: (i) => ({ opacity: 1, transition: { delay: i * 0.05 } }) with custom={i} on each child. Use it when the delay depends on something other than DOM order. Motion 14 also exports a stagger() function for delayChildren and for vanilla animate().

  • ▸

    Stagger totals grow with the list. A staggered entrance for a long list should be capped (about 0.5 s in total) or only applied to the first screenful.

Remember this

  1. 1

    The problem this solves: a menu of six items must appear one after another. With plain animate props you pass the state down six times and compute six delays by hand, and the delays go wrong the day a seventh item appears. Variants move that bookkeeping out of your components.

  2. 2

    Named states. A variants object maps names to targets: { hidden: { opacity: 0, x: -24 }, visible: { opacity: 1, x: 0 } }. Pass it as variants, then use the names in initial and animate: animate="visible". The name can also come from state, as in animate={shown ? "visible" : "hidden"}.

  3. 3

    Propagation. When a parent's animate is a variant name, children that have variants but no animate of their own receive the same name. They need not mention initial or animate at all. The example below logs the order Motion starts them: the list first, then Lock, Lights and Bell.

  4. 4

    Orchestration. Put transition inside the *parent's* variant. staggerChildren: 0.08 starts each child 0.08 s after the previous; delayChildren: 0.1 delays them all; staggerDirection: -1 runs the last child first. when: "beforeChildren" finishes the parent's own animation first, and "afterChildren" waits for the children. Measured on the Timeline: with delayChildren: 0.1 and staggerChildren: 0.08, the three opacity tracks start at 100, 180 and 260 ms; hiding with -1 and 0.04 gives 80, 40 and 0.

  5. 5

    **A child with its own animate.** A string animate on a child opts it out of propagation: item B below keeps its own label and stays at opacity 0.3. An object does not: item C asks for { opacity: 0.7 } and still receives the parent's "on" afterwards, and ends at 1 (measured, in the order the log shows). If a child must stay out of a group, give it a variant label of its own.

  6. 6

    Edge cases. A name that no variant defines is not an error: with animate="visibl" (a typo) the list logs the start but nothing moves, and the items stay hidden (measured). An empty list has no children to wait for. For long lists the delay adds up: 100 items at 0.08 s means the last starts after 8 s. Cap the total: staggerChildren: Math.min(0.08, 0.5 / n) keeps a 100-item entrance near half a second.

Explain it without notes

01

How does a parent control children without props reaching them?

02

What do staggerChildren, delayChildren and when each do, and where do they go?

Practice

01

Make a card whose title, text and button slide up one after another when the card appears, 0.1 s apart, after the card itself has faded in.

02

A list can have 5 or 500 rows. Write the variant so the whole stagger takes at most 0.6 s.

Done when you can

  • You can define variants and switch a group with one animate prop

  • You can place staggerChildren, delayChildren and when and read their effect on the Timeline

  • You can say which kind of child animate leaves a group and which does not

  • You can cap a stagger so a long list stays quick