In Reanimated 4, when do you animate a shared value with withTiming rather than withSpring, and what do their defaults do?
answer
- duration and easing versus physics
- 300 ms, inOut quad
- stiffness 900, damping 120, mass 4
- spring duration is perceptual, runs 1.5x
- hand the fling velocity to the spring
basics
~20 sUse withTiming for a fixed duration and curve (default 300 ms, Easing.inOut(Easing.quad)) and withSpring for motion that should feel physical or continue a gesture's velocity. Reanimated 4's default spring is critically damped, so it settles without oscillating.
solid answer
~40 s`withTiming(to, { duration, easing })` interpolates over a set time; with no config it runs 300 ms on `Easing.inOut(Easing.quad)`, which suits fades, colour changes and anything that must finish on schedule. `withSpring(to, config)` simulates a spring, so its length follows from physics: it is the natural choice for a bottom sheet snapping to a detent, because you can pass the release `velocity`, and a spring replaced by another spring hands its velocity on. In Reanimated 4 the defaults changed to `stiffness: 900`, `damping: 120`, `mass: 4`, a critically damped spring that no longer wobbles; the old bouncy config is exported as `Reanimated3DefaultSpringConfig`. A spring is configured either by physics or by `duration` plus `dampingRatio`, and that `duration` is perceptual: the spring actually runs about 1.5 times as long.
code
typescript · 15 linesimport { Easing, withSpring, withTiming } from 'react-native-reanimated';
import type { SharedValue } from 'react-native-reanimated';
export function snapSheet(sheetY: SharedValue<number>, target: number, releaseVelocityY: number) {
'worklet';
sheetY.value = withSpring(target, { velocity: releaseVelocityY });
}
export function fadeSearchBar(opacity: SharedValue<number>, visible: boolean) {
'worklet';
opacity.value = withTiming(visible ? 1 : 0, {
duration: 200,
easing: Easing.out(Easing.quad),
});
}go deeper
Recall that withTiming is time plus easing (300 ms, inOut quad by default) and withSpring is physics, and say which suits a fade and which suits a snap.
Explain the two spring configuration styles, why Reanimated 4's defaults do not bounce, and that the completion callback runs on the UI runtime with a finished flag.
Show how you hand gesture velocity to the spring, keep interrupted springs continuous, and account for the Reanimated 3 to 4 default and duration changes when an upgrade alters the feel.
Talk about motion as a shared design token: agreeing spring presets across the app so screens feel consistent and upgrades that change library defaults are caught in review.
## Two animation functions, two mental models Reanimated's **animation functions** return an *animation object* that you assign to a shared value (`y.value = withTiming(0)`) or use directly as a value inside `useAnimatedStyle`. Reanimated then computes one step per frame on the UI runtime. The two you use most often answer different questions: - **`withTiming`** answers *"how long and along which curve?"* It moves from the current value to the target over a fixed `duration`, shaped by an `easing` function. - **`withSpring`** answers *"how stiff and how heavy?"* It simulates a mass on a spring, so the time it takes is an outcome of the physics, not an input. A third built-in, `withDecay`, models momentum that slows at a given rate; modifiers such as `withDelay`, `withSequence`, `withRepeat` and `withClamp` wrap the others. ## `withTiming` and its defaults | Config key | Default | Meaning | |---|---|---| | `duration` | `300` | Length in milliseconds | | `easing` | `Easing.inOut(Easing.quad)` | Curve that accelerates, then decelerates | | `reduceMotion` | `ReduceMotion.System` | Follows the device's reduced-motion setting | Reach for it when the timing is a design decision: a place card fading in, a map pin changing colour, a progress bar that must finish in exactly 250 ms. The `Easing` module offers `linear`, `bezier(...)`, `bounce`, `elastic(...)` and more, plus `in`, `out` and `inOut` wrappers. Note that React Native's own `Animated.timing` defaults to 500 ms, so numbers remembered from that API do not carry over. ## `withSpring` in Reanimated 4 A spring is configured in one of two mutually exclusive ways: 1. **Physics-based**: `stiffness`, `damping` and `mass`. Defaults are `stiffness: 900`, `damping: 120`, `mass: 4`. 2. **Duration-based**: `duration` and `dampingRatio`. Defaults are `duration: 550` and `dampingRatio: 1`. If both styles are passed, `duration` and `dampingRatio` win. Shared keys include `velocity` (default `0`), `overshootClamping` (default `false`), `energyThreshold` (default `6e-9`), `reduceMotion` and, for duration-based springs, `clamp`. The physics defaults are **critically damped**: the critical damping for stiffness 900 and mass 4 is 2 times the square root of 3600, which is exactly 120. Started from rest, such a spring reaches the target without oscillating. Lower `damping` or a `dampingRatio` below 1 brings the bounce back. ## What changed from Reanimated 3 - The **defaults** changed. Reanimated 3's spring (`damping: 10`, `mass: 1`, `stiffness: 100`) was visibly bouncy; it is still importable as `Reanimated3DefaultSpringConfig`, and the duration-based one as `Reanimated3DefaultSpringConfigWithDuration`. - **`duration` is now perceptual.** The spring really runs about 1.5 times the value you pass, so a `duration: 400` spring takes roughly 600 ms. The migration guide tells you to divide old values by 1.5 to keep the previous feel. - **`restDisplacementThreshold` and `restSpeedThreshold` are gone**, replaced by a single relative `energyThreshold` that you normally leave alone. ## Choosing in a real screen For a draggable bottom sheet over a map, the release is the moment that decides the feel: - **Snap with a spring** and pass the gesture's vertical velocity as `velocity`, so a fast flick carries into the motion instead of restarting from zero. - **Spring-to-spring interruption stays smooth.** When a new `withSpring` replaces a running spring on the same shared value (say, a collapse button tapped mid-snap), the new spring starts with the running spring's velocity plus any `velocity` you pass, so the motion does not stop dead. - **Use timing for the dependent visuals** that should not overshoot or feel physical, such as fading the search bar. Both functions accept a third argument, a completion callback `(finished, current) => ...`. It is workletized and runs on the UI runtime, and `finished` is `false` when the animation was cancelled, for example by a new assignment or `cancelAnimation`. ## How interviewers probe it - **"Why did the upgrade change the feel?"** Default springs, perceptual `duration` and the removed rest thresholds are the three Reanimated 4 changes to name. - **"Which would you use for a toast?"** Usually `withTiming`: a fixed, predictable fade that must end on time. - **"How do you make a flick feel natural?"** A spring seeded with the gesture's release velocity. ## Quick comparison | | `withTiming` | `withSpring` | |---|---|---| | Driven by | Duration and easing | Stiffness, damping and mass, or perceptual duration | | Default length | 300 ms | Emerges from physics | | Picks up gesture velocity | No | Yes, via `velocity` | | Typical use | Fades, colours, fixed-length transitions | Snapping, dragging, anything interruptible |
- Your bottom sheet felt bouncy on Reanimated 3 and now snaps flatly on Reanimated 4 with the same code. Why, and how do you restore it?Reanimated 4 changed `withSpring`'s defaults to a critically damped spring (`stiffness: 900`, `damping: 120`, `mass: 4`). Code that passed no config silently got the new feel. Pass `Reanimated3DefaultSpringConfig`, or tune `damping` or `dampingRatio` below critical, to bring the overshoot back deliberately.
- How do you know whether a Reanimated animation finished or was interrupted?Pass a completion callback as the third argument to `withTiming` or `withSpring`. It runs on the UI runtime with `finished` set to `true` when the animation reached its target and `false` when it was cancelled, for example by a new assignment to the same shared value or by `cancelAnimation`.
withTiming is a train timetable: it leaves and arrives at fixed times whatever happens on the way. withSpring is a door closer: how long the door takes depends on how hard it was pushed and how stiff the closer is.
saying these in an interview costs you the question
- withTiming defaults to 500 ms, the same as React Native's Animated.timing.
- A spring's duration setting is the exact time the animation takes.
- Reanimated 4 springs still use restDisplacementThreshold and restSpeedThreshold.
- withSpring always overshoots its target before settling.
- You can combine stiffness and damping with duration and dampingRatio to get both behaviours.
- The completion callback of withTiming runs on the JS thread, so it can call setState directly.