Skip to main content

Why it’s fast

react-native-ease runs animations entirely on platform APIs. Once the JS layer sends new target values to the native view, there is no further JS involvement — no animation loop, no per-frame prop updates, no re-renders.
On iOS, react-native-ease uses CABasicAnimation and CASpringAnimation on CALayer key paths. Core Animation is processed by a separate OS service called the render server (backboardd). Animations run in that process — not on the main thread, and not in your app process at all. This is why iOS benchmarks show ~0.01ms: there is essentially nothing to measure from the app’s perspective.

What “zero JS overhead” means

During a running animation, the JS thread does nothing. Specifically:
  • No animation loop ticking on JS
  • No Animated.Value listeners firing
  • No useAnimatedStyle worklet evaluating per frame
  • No prop updates being dispatched from JS to native
  • No React re-renders
This means your JS thread remains fully available for user interaction, navigation, data fetching, and other work while animations play.

Benchmark data

The example app includes a benchmark that measures per-frame overhead when animating translateX in a loop on a configurable number of views (linear, 2s duration). All approaches animate the same property with the same timing. Reanimated results are shown both with experimental feature flags off (default) and on (FF).

Android

UI thread time per frame: anim + layout + draw (ms). Lower is better. Release build, emulator, M4 MacBook Pro.
ViewsMetricEaseReanimated SVReanimated SV (FF)Reanimated CSSReanimated CSS (FF)RN Animated
10Avg0.211.150.750.990.450.36
10P950.331.701.531.440.800.62
10P990.481.942.261.621.350.98
100Avg0.362.711.812.191.010.71
100P950.563.092.292.671.911.08
100P990.713.202.632.972.251.36
500Avg0.608.315.375.502.371.60
500P950.759.266.366.342.861.88
500P990.879.596.896.883.223.84

iOS

Display link callback time per frame (ms). Lower is better. Release build, simulator, iPhone 16 Pro, M4 MacBook Pro.
ViewsMetricEaseReanimated SVReanimated SV (FF)Reanimated CSSReanimated CSS (FF)RN Animated
10Avg0.011.331.081.060.630.83
10P950.021.671.591.341.011.18
10P990.031.901.681.501.081.31
100Avg0.013.723.332.712.483.32
100P950.015.214.503.833.394.28
100P990.025.684.754.913.794.55
500Avg0.016.846.544.163.704.91
500P950.017.697.324.594.225.66
500P990.028.107.454.714.335.89
Run the benchmark yourself in the example app. Use a release build for accurate numbers — debug builds include extra overhead that skews results.

Hardware layers (Android)

For complex view hierarchies animating opacity, scale, or rotation, enable useHardwareLayer to rasterize the view into a GPU texture. Animated property changes are then composited on the RenderThread without redrawing child views.
<EaseView
  animate={{ opacity: isVisible ? 1 : 0 }}
  useHardwareLayer
  style={styles.complexCard}
>
  {/* many children */}
</EaseView>
See Hardware Layers for trade-offs, including GPU memory usage and clipping behavior. useHardwareLayer is a no-op on iOS, where Core Animation already composites off the main thread.

View recycling

Fabric recycles native views when components are unmounted and remounted. react-native-ease handles this correctly on both platforms:
  • iOSprepareForRecycle resets all animated properties (opacity, translation, scale, rotation, border radius) to their identity values before a view is reused
  • Androidcleanup() performs the same reset
This ensures no stale animation state leaks from one component instance to the next.

Tips for maximum performance

Spring animations preserve momentum when interrupted. If a user taps rapidly or reverses mid-animation, a spring will redirect smoothly from its current velocity rather than snapping. Use timing only when a precise duration matters (e.g., syncing with audio).
<EaseView
  animate={{ translateX: isOpen ? 200 : 0 }}
  transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }}
/>
When animating opacity, scale, or rotation on a view with many children, useHardwareLayer moves compositing to the RenderThread. This is most impactful on Android when the view hierarchy is deep.
<EaseView
  animate={{ scale: isActive ? 1.05 : 1 }}
  useHardwareLayer
/>
Animating width or height requires a layout recalculation pass on every frame. This is outside the scope of react-native-ease by design — these properties are not supported in animate. If you need layout animations, use Reanimated’s layout animation API.
Use the delay parameter on transition to stagger entrance animations. This keeps all animations on the native side rather than scheduling JS timers.
{items.map((item, i) => (
  <EaseView
    key={item.id}
    initialAnimate={{ opacity: 0, translateY: 20 }}
    animate={{ opacity: 1, translateY: 0 }}
    transition={{ type: 'timing', duration: 300, delay: i * 80 }}
  />
))}

Build docs developers (and LLMs) love