Animations
Swap out animation drivers per-platform or at runtime
Tamagui animates style props through swappable drivers, with the same component API across web and native.
Features
Animate any style prop with animation config per-prop.
Can animate across all states (media queries, hover, etc).
Multiple drivers you can swap out with type safety.
SSR safe mount animations.
Enter and exit animations with AnimatePresence.
Add animations to Tamagui with an animation driver. Animation drivers are designed to be swappable, so you can use lightweight CSS animations or web-focused libraries on the web, while using Reanimated on native, without changing component code outside of configuration.
For this guide, we’ll use the React Native driver as an example, but you can choose from several Animation Drivers. For mount and unmount animations, see AnimatePresence.
Installation
yarn
npm
bun
pnpm
Then add it to your Tamagui config:
A config entry is { duration, bounce }, a CSS timing string, or
{ type: 'timing', duration, easing }. stiffness, damping, and mass still
work for a config already tuned against them. The same config resolves
to the same motion on all four drivers, so you can swap drivers without
retuning.
@tamagui/config ships this table for every driver, so you only need
createAnimations when you want names of your own:
Usage
The transition prop accepts the name of an animation you’ve configured. By default, animations will
apply to all animatable styles, similar to setting all in a CSS transition.
Use a hover object key or a hover: string clause to animate a state value:
String
Typed
Here is an interactive example:
The transition prop rules
If you add a transition prop, you must always keep the prop. If you need the animation to be disabled,
pass false, null or even undefined if it suits you.
The spring-based animation drivers have expensive hooks that would degrade runtime
performance if present on every component. As a workaround, the animation hooks
are called conditionally based on whether the transition key is present in the props object.
So, <Square transition={isActive ? 'bouncy' : null} /> rather than
<Square {...isActive && { transition: 'bouncy' }} />.
If you’d like to remove or add a transition prop after a component has already rendered, you’d have to change the key.
Enter clauses
Setting an enter object key or an enter: string clause tells a property to
start at that value, then animate to its base value after mount:
String
Typed
The transition value
transition takes a CSS transition string or an object. Both forms mean the
same thing:
The object form takes the same values under names:
The settings are preset, duration, bounce, easing, delay, behavior,
properties, spring, enter, and exit. Every other key names a style
property and gets its own timing. An unrecognized key is an error rather than a
silent no-op.
Granular animations
A per-property key wins for that property, the same way a later entry wins in a CSS transition list. These two are the same transition:
Per-property values take a preset name, a CSS timing, or an object:
Entries do not inherit from the base. { duration: 200, opacity: {} } gives
opacity no timing of its own, the same as transition: all 200ms, opacity in
CSS. Name what you want on each entry, or use preset for a shared starting
point.
Springs
A spring is duration plus bounce:
duration is the spring’s period, which is what “how fast does this feel”
means. It is not a stopwatch: a bouncy spring keeps ringing past it, which is
the point of a bouncy spring. bounce is 0 for critically damped (fast, no
overshoot), goes up toward 1 for loose and oscillating, and goes negative for
sluggish. A value outside -1 to 1 is an error.
On the web this compiles to a linear() easing that traces the real spring
curve, overshoot included, with no JavaScript running.
When you need the physics directly, spring is the escape hatch:
It takes stiffness, damping, mass, velocity, overshootClamping,
restDisplacementThreshold, and restSpeedThreshold. Tamagui derives the
duration/bounce pair from stiffness and mass, so the same motion reaches
the CSS driver too. It is a projection of duration and bounce, not a second API.
Presets
A preset is a name from your config’s animations. Any name works where a
timing goes, and preset in the object form takes overrides:
Overriding duration or bounce re-solves the spring, so the preset keeps
whichever of the two you did not name and drops the stiffness/damping it was
written with, since those are what you just replaced. mass carries over: it
describes the object rather than the curve. Naming stiffness or damping
yourself still wins outright, and a preset used without a duration or bounce
override runs exactly the numbers your config gave it.
The shipped presets are the same springs on every driver: quickest, quicker,
quick, medium, slow, slowest, lazy, superLazy, bouncy,
superBouncy, plus quickestLessBouncy, quickerLessBouncy, and
quickLessBouncy. Keep your own table small. A duration is CSS now, so
transition="200ms" needs nothing configured, and a name is worth having only
when it means something a duration cannot say.
Delay
delay is the second time in the CSS string, or the delay key:
Each square starts 100ms after the previous one, on enter, exit, and updates.
Enter/exit transitions
enter and exit set the transition to use while mounting and unmounting.
Set them when elements should enter slowly and leave quickly:
Each takes a whole transition value, so they can carry their own properties and per-property timings, and the base still applies to changes while mounted:
An enter or exit list replaces the base while it applies rather than merging
into it, because the two never run at the same time.
This works with all four animation drivers (CSS, React Native, Reanimated, Motion).
Pseudo-style transitions
Specify different transitions for entering and exiting pseudo states like hover, press, and focus. Entering a state uses that state’s transition, and exiting uses the base transition:
In this example:
- Hover enter: Uses the 200ms transition (snaps quickly to hover state)
- Hover exit: Uses the 1000ms base transition (fades slowly back)
This works with all pseudo styles:
And with group pseudo states:
Priority follows CSS specificity intuition: press > hover > focus. This works with all animation drivers.
Limiting which properties transition
Name the properties in the transition itself, the same way CSS does. In string
form the property leads the value, and in object form it is the properties key:
This replaces v2’s animateOnly prop, which was the same idea spelled as a
separate array. properties reaches every driver, so a list means the same
thing on CSS, Motion, React Native, and Reanimated.
Naming transform covers every transform part, so x, y, scale,
scaleX/scaleY, rotate and the rest all transition together. Naming one
part covers only that part:
The onTransition lifecycle
Every animated component accepts onTransition, a typed callback that fires at
the start and end of a transition. It replaces the untyped, enter-only
onDidAnimate from v2.
cause is enter when the component mounts into an AnimatePresence,
exit when it unmounts, and update for any style change while it stays
mounted. On the end phase, finished is false when the transition was
interrupted, for example an exit canceled by a re-enter, or an update
superseded by another before it settled.
A driver may coalesce a multi-property update into one start/end pair per batch,
but it never skips the end event. Exit end fires before or with presence
removal, so you can observe exit completion without reaching into presence
internals. There is a single callback rather than separate
onTransitionStart/onTransitionEnd props because those names collide with the
React DOM event props on web.
Imperative animation hooks
The same animated-number primitives the drivers use internally are exported from
tamagui (and @tamagui/core). Use them to drive an effect from a value you
control instead of from style props, for example a drag-linked overlay fade.
Each hook is a thin delegation to the configured driver, so first-party code and
your code exercise the same surface. They throw a helpful error if no
animations config is set or if the configured CSS driver came from the core
@tamagui/animations-css entry. Import createAnimations from
@tamagui/animations-css/extras when using these hooks, Sheet, or draggable
Toast with the CSS driver. The resolved driver must not change identity
mid-lifecycle, which is only a concern with animatedBy multi-driver setups.
useAnimatedNumberStyle takes a value and a worklet that maps it to a style
object. Spread that style onto the driver’s animated view. On the CSS driver, a
plain Tamagui view with a transition prop interpolates the change.
setValue(next, config, onFinished) accepts an optional completion callback that
fires once, with the value settled on the target. useAnimatedNumberReaction
observes intermediate values on the JS thread.
UniversalAnimatedNumber, AnimatedNumberStrategy, and the hook types are
public API.
What to know when animating
Driver completion
Every driver reports real completion. The CSS driver drives its animated numbers
with a requestAnimationFrame ticker and fires completion once when the value
settles, so onTransition end events and setValue completion callbacks are
accurate on web without an estimated-duration timer. Web completion detection
uses the element’s running animations rather than a guessed duration.
Conditional animations and HMR
The animation hooks are heavy, which initially meant we either had to choose
great performance or animations. We settled on a trade-off: we track if the
transition prop is set, and if so, we enable the hook. If it is ever set, even
just once, then the hooks will continue to run for the remainder of the
component lifecycle. This means if you ever plan to animate a component you
should keep transition always set on the component props. You can disable it
like so:
Note that because of this constraint, adding the transition prop to a mounted
component during dev HMR can error. Saving again or reloading clears it.
See also
- AnimatePresence: mount and unmount animations
- Animation Drivers: choose and configure animation drivers