Skip to main content

Sequences and repeat

Per-property keyframes and looping live in two related shapes: arrays for sequence steps, and the unified repeat config on transitions.

Keyframes

Pass an array as the value of any animatable key to step through frames in order:

<Motion.View
animate={{
translateX: [0, 100, 0],
opacity: [0, 1],
}}
/>

Each step inherits the transition that's in effect for that key. Sequences run on the UI thread without bouncing through JS.

Per-step transitions

Replace any step with { to, ...transitionOverride } to tune that step's physics independently:

<Motion.View
animate={{
opacity: [
0,
{ to: 1, type: 'spring', tension: 200 },
{ to: 0, type: 'timing', duration: 300, delay: 500 },
],
}}
/>

We use to (not value) because steps describe a destination — "animate to this".

Repeat

repeat lives on the transition, not on animate. One shape, no loop / repeatReverse cousins.

FormMeaning
repeat: 3Run 3 iterations total, alternating direction.
repeat: 'infinite'Loop forever, alternating direction.
repeat: { count: 3, alternate: false }Full control. alternate defaults to true.
repeat: { count: 'infinite', alternate }Endless with explicit alternate flag.
<Motion.View
animate={{ translateX: [0, 80, 0] }}
transition={{ type: 'timing', duration: 600, repeat: 'infinite' }}
/>

When applied to a sequence, repeat wraps the whole sequence, not each step. Per-step repeat overrides remain step-local — they apply only inside that step.

'no-animation' and 'decay' configs ignore repeat.

onAnimationEnd

Sequence and repeat lifecycle is reported through one callback:

<Motion.View
animate={{ translateX: [0, 100, 0] }}
transition={{ repeat: 2 }}
onAnimationEnd={({
key,
finished,
phase,
step,
iteration,
value,
target,
}) => {
// phase: 'step' | 'sequence' | 'repeat' | 'animation'
// step: index in the keyframe array, undefined for non-sequences
// iteration: 0 on the first pass, 1 on the second, …
}}
/>
phaseWhen it fires
'step'A non-final step in a sequence settles.
'sequence'Last step of a non-final iteration — the sequence is about to repeat.
'repeat'A non-final iteration of a non-sequence animation completes.
'animation'The terminal phase of the property — no more passes will run.

Transform parents fire once per logical event, not once per axis. A translateX + translateY animation on the same primitive produces the callback shape you'd expect from "translate", not two duplicates — when the terminal 'animation' phase fires for any transform key, key is reported as the sentinel 'transform'. Mid-step events ('step' / 'sequence' / 'repeat') still fire per-axis with the specific axis name ('translateX', 'scale', etc.) since each step is its own logical event.