skip to content

Animated API

Animated drives a value through timing, spring or decay and interpolates it into styles, and useNativeDriver moves frames to the UI thread. Interviewers ask which props the native driver accepts.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

6

In React Native, how do you animate a like-button heart pop with Animated.spring, and what does useNativeDriver: true change?

level: juniorimportance: must knowfreq 75%

answer

  1. a value, an animation, an Animated.View
  2. useAnimatedValue keeps one value
  3. timing, spring or decay
  4. the animation is sent to native up front
  5. useNativeDriver must be set explicitly

basics

~10 s

Keep an Animated.Value from useAnimatedValue, start Animated.spring on it, and bind it to an Animated.View's transform scale. useNativeDriver: true hands the animation to native before it starts, so the UI thread computes every frame.

solid answer

~40 s

You create a value with `useAnimatedValue(1)`, which keeps one `Animated.Value` across renders, and bind it to `<Animated.View style={{transform: [{scale}]}}>`. On a like, set it to a smaller scale and call `Animated.spring(scale, {toValue: 1, friction: 3, useNativeDriver: true}).start()` so the heart springs back with an overshoot. `Animated.timing` instead eases over a fixed `duration` (default 500 ms, ease-in-out), and `Animated.decay` slows an initial `velocity` to rest. `useNativeDriver: true` serializes the animation and hands it to native code before it starts; the UI thread then computes every frame, so a busy JavaScript thread, such as one rendering the feed, cannot make the pop stutter. The option is required: leaving it out logs a warning, fails TypeScript's check, and falls back to the JS driver. The native driver only accepts non-layout styles like `transform` and `opacity`.

code

tsx · 30 lines
tsx
import {Animated, Pressable, StyleSheet, useAnimatedValue} from 'react-native';
import {useState} from 'react';

export function LikeHeart() {
  const [liked, setLiked] = useState(false);
  const scale = useAnimatedValue(1);

  const onPress = () => {
    setLiked(prev => !prev);
    scale.setValue(0.6);
    Animated.spring(scale, {
      toValue: 1,
      friction: 3,
      tension: 120,
      useNativeDriver: true,
    }).start();
  };

  return (
    <Pressable onPress={onPress} accessibilityRole="button" accessibilityLabel="Like">
      <Animated.Text style={[styles.heart, {transform: [{scale}]}]}>
        {liked ? '\u2665' : '\u2661'}
      </Animated.Text>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  heart: {fontSize: 32, color: '#e0245e'},
});

go deeper

for a junior

Recall the three pieces: an Animated.Value from useAnimatedValue, a timing, spring or decay animation, and an Animated component, plus what useNativeDriver does.

for a middle

Explain why the native driver survives a busy JS thread, the defaults of timing and spring, and why the option must be explicit.

for a senior

Show judgement about when native driving is possible, which styles force the JS driver, and how an animation that must stay smooth during list rendering is designed.

for a principal

Set a team default for animation work, Animated with the native driver versus a worklet-based library, and the review rules that keep janky JS-driven animations out.

## The three pieces of an Animated animation React Native's `Animated` API is built from three parts: 1. **A value.** `Animated.Value` holds a number that changes over time; `Animated.ValueXY` holds an `{x, y}` pair for two-dimensional motion. In a function component, `useAnimatedValue(initial)` (or `useAnimatedValueXY`) creates it once and returns the same instance on every render; `new Animated.Value()` inside the component body would create a fresh one each render. 2. **An animation.** A driver such as `Animated.timing`, `Animated.spring` or `Animated.decay` moves the value toward a target. Calling `.start()` runs it, optionally with a callback receiving `{finished}`. 3. **An animated component.** `Animated.View`, `Animated.Text`, `Animated.Image` and friends accept animated values inside `style`, for example `transform: [{scale}]`. ## Choosing a driver | Driver | What it models | Key options and defaults | |---|---|---| | `Animated.timing` | a curve over fixed time | `duration` 500 ms, `easing` ease-in-out, `delay` 0 | | `Animated.spring` | a physical spring that can overshoot | `friction` 7 and `tension` 40 by default, or `bounciness`/`speed`, or `stiffness`/`damping`/`mass`; only one family at a time | | `Animated.decay` | an initial velocity slowing to rest | `velocity` required, `deceleration` 0.998 | For a like-button heart, `spring` gives the playful overshoot: start the scale below 1 and spring to 1 with a low `friction`. Mixing families, for example `friction` with `stiffness`, fails an invariant. ## What useNativeDriver: true changes Without the native driver, JavaScript computes each frame and pushes the new value to the view. With it: - The animation is **serializable**, so React Native sends its full description to native code **before** starting it. - Native code on the **UI thread** computes every frame and applies it to the view. - Once started, the **JavaScript thread can be blocked** without affecting the animation, which matters when a like happens while the feed is rendering new posts. Three rules come with it: - **Set it explicitly.** The option is required; if it is missing, `Animated` logs a warning, TypeScript reports an error, and the JS driver is used. - **Non-layout styles only** by default: `transform`, `opacity`, colours and similar properties work; `width`, `height`, margins and positions do not. - **One driver per value.** Once a value has run a native-driven animation, running a JS-driven animation on it throws. ## Building the heart pop - Keep a `scale` value at `1`. - On press, `scale.setValue(0.6)` and start `Animated.spring(scale, {toValue: 1, friction: 3, tension: 120, useNativeDriver: true})`. - The low friction makes the spring overshoot past 1 and settle, which reads as a pop. - Toggle the liked state in React as usual; the animation is decoration, not the source of truth. ## Common mistakes - Creating `new Animated.Value(1)` in the component body, so every render resets the heart. - Omitting `useNativeDriver`, then wondering why the pop stutters while the list scrolls. - Trying to pop the heart by animating `width` and `height` with the native driver instead of `transform: [{scale}]`. - Using a plain `View` instead of `Animated.View`, so the animated value is never applied. ## Shaping the motion - **Easing for `timing`.** The default is `Easing.inOut(Easing.ease)`. `Easing.out(Easing.back())` overshoots slightly at the end, `Easing.bezier(x1, y1, x2, y2)` matches a design tool's curve, and `Easing.linear` suits progress bars. - **Delay.** `delay` in the config waits before the animation starts, useful for chaining small effects without a sequence. - **Spring feel.** Lower `friction` (or `damping`) gives more bounce; higher `tension` (or `stiffness`) makes it faster. Pick one parameter family and tune only that. - **Starting velocity.** Both `spring` and `decay` accept a `velocity`, so an animation that follows a gesture can continue its speed instead of starting from rest. - **Two dimensions.** `Animated.ValueXY` animates `{x, y}` together, and `getTranslateTransform()` turns it into a `translateX`/`translateY` transform. ## What the start callback tells you `start(({finished}) => ...)` runs when the animation ends. `finished` is `true` for a normal end and `false` when it was stopped or replaced by another animation on the same value. For a like button that users tap repeatedly, the `false` case is common, so side effects in the callback need that check.

  • In React Native Animated, when would you pick Animated.decay over timing or spring?
    When the motion starts from a velocity rather than aiming at a target, such as flinging a card or a sticker after a gesture. `decay` takes the initial `velocity` and slows it with `deceleration` (0.998 by default) until it stops, so the end position depends on how hard it was thrown. `timing` and `spring` both need a `toValue`.
  • Why use useAnimatedValue instead of new Animated.Value() in a function component?
    `new Animated.Value(1)` in the component body creates a new value on every render, so any running animation is lost and the view jumps back to the initial value. `useAnimatedValue(1)` creates the value once and returns the same instance on later renders; it is the hook form of keeping the value in a ref.

Handing a musician the full sheet music before the concert instead of whispering each note: once the score is on stage, the performance continues even if the conductor gets distracted.

saying these in an interview costs you the question

  • useNativeDriver defaults to true, so you can leave it out
  • The native driver sends a message to native on every frame
  • Animated.spring and Animated.timing both require a duration
  • A plain View will apply an Animated.Value placed in its style
  • The native driver can animate any style property, including width
open as a page

In React Native 0.87, which style properties can Animated's native driver animate, and what happens if you native-drive a heart's width?

level: middleimportance: must knowfreq 62%

basics

~20 s

By default the native driver animates only non-layout styles: transform, opacity, colours, border radii, shadow opacity and radius, elevation, zIndex and filter. Native-driving width logs an error that the style is not supported; animate transform: scale instead.

open as a page

In React Native Animated, how do sequence, parallel, stagger and loop differ, and what happens when a child animation is interrupted?

level: middleimportance: should knowfreq 40%

basics

~20 s

sequence runs animations one after another, parallel starts them together, stagger starts them in parallel with increasing delays, and loop repeats one animation. An interrupted child stops a sequence, and stops all of a parallel unless stopTogether is false.

open as a page

In React Native Animated, what does interpolate do, and why can the default extrapolate make a springy heart shrink below its resting size?

level: middleimportance: should knowfreq 45%

basics

~20 s

interpolate maps a value's inputRange onto an outputRange, piecewise and linear by default. Outside the range it extends the edge segment unless told otherwise, so a spring overshooting past 1 pushes the output beyond the last value; extrapolate: 'clamp' fixes that.

open as a page

In a React Native Animated like-button, rapid taps leave the heart at the wrong scale and completion logic misfires; what is going on, and how do you make it robust?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Each new animation on a value stops the running one, whose callback gets finished: false, and an interrupted sequence stops mid-way. Keep the like state in React, stop and reset before each run, and act only when finished is true.

open as a page

In React Native, what does LayoutAnimation.configureNext do, and is UIManager.setLayoutAnimationEnabledExperimental still needed on Android in 0.87?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

LayoutAnimation.configureNext, called just before a state change, animates every layout change in the next commit: views created, moved, resized or removed. Under the New Architecture, the only architecture since 0.82, UIManager.setLayoutAnimationEnabledExperimental is a no-op that only logs a warning.

open as a page