
Why React Native Animations Jank and How Reanimated 3 Fixes It
- Larry Brooks
- React Native, Mobile Development
- 25 Jun, 2026
A list scrolling smoothly until a background API call completes — then dropping to 20-something fps for 200 milliseconds while state updates process — is one of the most common React Native performance complaints. No amount of JavaScript optimization will fix it. The problem is architectural: the animations run on the JavaScript thread, competing with every other operation for execution time.
Why the JS Thread Is the Wrong Place for Animations
React Native's JavaScript thread handles business logic, state management, API calls, data transformation, and — if you use the Animated API with useNativeDriver: false — animation calculations. When any of these operations takes more than 16 milliseconds (the budget for one frame at 60fps), animations drop frames.
Setting useNativeDriver: true helps for simple transform and opacity animations. But it does not support layout properties, color animations, or any animation that depends on JavaScript-computed values. For complex animations, the Animated API forces you back to the JS thread.
How Reanimated 3 Solves This
Reanimated 3 runs animation logic on the UI thread using worklets — small JavaScript functions that execute on a separate thread with direct access to view properties. The JS thread can be completely blocked, and Reanimated animations continue at 60fps.
Benchmarking a screen with a parallax scroll header, animated tab bar, and list with item entrance animations shows the difference clearly. With the Animated API, frame rates average in the low-40s during data fetching and drop into the mid-20s during heavy state updates. With Reanimated 3, frame rates hold at 59–60fps regardless of JS thread activity. The difference is not marginal — it is the difference between an animation users notice and one they do not.
The Migration Path
Reanimated 3 uses a different API than React Native's Animated. The core concepts are shared values (replacing Animated.Value), animated styles (replacing Animated.View style bindings), and worklets (replacing Animated.timing and Animated.spring).
The migration is not a find-and-replace. Reanimated's mental model is different: instead of imperatively starting and stopping animations, you declare how styles derive from shared values, and the UI thread recomputes them automatically.
Start by migrating the most janky animation. Learn the API on one real use case. Then migrate screen by screen, prioritizing animations that users interact with most frequently.
The Gesture Integration
Reanimated 3 combined with React Native Gesture Handler creates a gesture-animation pipeline that runs entirely on the UI thread. A swipe gesture that drives a card dismissal animation — the gesture tracking and the animation response — both execute on the UI thread without touching JavaScript.
This is how native apps achieve fluid gesture interactions. Reanimated 3 makes it achievable in React Native with the right architecture.
When to Use What
Use React Native's built-in Animated API for simple opacity and transform animations where useNativeDriver: true is sufficient. Use Reanimated 3 for layout animations, gesture-driven animations, spring physics, and any animation that must remain smooth during heavy JS thread activity.
If your app's animations stutter and your users notice, let's fix the architecture.
