Skip to main content

Transitions

A transition prop decides how an animate value reaches its target. The default is a spring tuned for everyday UI motion; durations, decay, and instant assignment are all opt-in.

Top-level vs per-property

transition accepts either one config (applied to every animating key) or a per-property map. Per-property entries always win.

// One config, applies to all animating keys
<Motion.View animate={{ opacity: 1, translateY: 0 }} transition={{ type: 'spring' }} />

// Per-property — opacity uses timing, translateY uses spring
<Motion.View
animate={{ opacity: 1, translateY: 0 }}
transition={{
opacity: { type: 'timing', duration: 200 },
translateY: { type: 'spring', tension: 180, friction: 12 },
}}
/>

Types

'spring' (default)

react-spring vocabulary. The library converts to Reanimated under the hood — raw stiffness / damping never appear in the public API.

transition={{
type: 'spring',
tension: 170, // default
friction: 26, // default
mass: 1, // default
velocity: 0,
restSpeedThreshold: undefined,
restDisplacementThreshold: undefined,
delay: 0,
repeat: undefined,
}}

Porting from raw Reanimated stiffness / damping

The conversion is a 1:1 alias rename, not a physics formula. If you have a Reanimated withSpring config tuned the way you want, port it by renaming two keys:

Reanimated rawInertia
stiffnesstension
dampingfriction
massmass

So a Material Design 3 emphasized spring ({ stiffness: 380, damping: 32, mass: 1 }) ports as { type: 'spring', tension: 380, friction: 32, mass: 1 }. No retuning, same perceptual result.

'timing'

Duration-based interpolation. Useful for opacity fades and anything where physics feel wrong.

transition={{
type: 'timing',
duration: 250, // default
easing: Easing.inOut(Easing.ease), // default
delay: 0,
repeat: undefined,
}}

User-supplied easing functions are auto-wrapped as worklets at JS time, so plain functions work in nested-transition contexts (variants, sequences, per-property maps) without manual 'worklet' directives. Easing fns must be pure — no captured JS-thread refs.

'decay'

Velocity-driven decay (the gesture-flick model). Combine with react-native-gesture-handler to drive scroll-style flick momentum.

transition={{
type: 'decay',
velocity: 800,
deceleration: 0.998,
clamp: [0, 600],
}}

decay cannot be repeated — repeat is ignored on decay configs.

'no-animation'

Skip interpolation entirely; jump to the target value. Equivalent to a direct shared-value assignment, but stays inside the transition shape so per-property overrides compose:

transition={{
opacity: { type: 'timing', duration: 200 },
translateY: { type: 'no-animation' },
}}

This is also what reduced-motion mode swaps every transition into. See MotionConfig.

delay

Available on every type except 'no-animation'. Applied before the underlying animation runs:

transition={{ type: 'spring', delay: 200 }}

repeat

A unified shape — one prop, no flags soup. See sequences and repeat for the full table.

Defaults

TypeFieldDefault
springtension170
springfriction26
springmass1
timingduration250
timingeasingEasing.inOut(Easing.ease)

If those defaults move, the source of truth is packages/core/src/transitions/resolve.ts.