A Deep Dive Into React Native Reanimated v1

Authors
Tomasz Krzyzowski
Software Engineer
@
Callstack

To provide users with smoothly working apps, we want our animations to run at 60 fps or even better. However it may sound like a bit of a challenge, with an Open Source library called <rte-code>react-native-reanimated<rte-code>, we can do it!

In this article, I'll describe to you what React Native Reanimated is and how it works under the hood basing on three use cases.

What is React Native Reanimated?

React Native Reanimated is an Open Source library used in React Native mobile development to create smooth animations and interactions that run on the UI thread. The library allows developers to declare animations on the JavaScript thread and run them on the native one to achieve smooth animations regardless of whether the JS thread is busy or not.

Despite being really powerful, the library has a very steep learning curve. First of all, we have to learn a specific language in order to build animations.

Second of all, we have to change our mindset and start thinking declaratively instead of imperatively.

It can be difficult to wrap your head around this style of building animations at first. That's why I will dive deeply under the hood of this library what, hopefully, will help you understand how it works and what idea stands behind it.

The idea behind Animated components

At first, I want you to understand what the purpose of rendering <rte-code><Animated.View/><rte-code> is and why we can use <rte-code>Animated.Node<rte-code> values in the <rte-code>style prop<rte-code>.

The example code snippet below uses <rte-code>Animated.Node<rte-code> to set the <rte-code>opacity<rte-code> (it is an instance of the <rte-code>Animated.Node<rte-code>) style property of the <rte-code>Animated.View<rte-code>.

Let's go through it, step by step.

At the beginning of our component, we are creating <rte-code>Animated.Value<rte-code> node with an initial value equal to <rte-code>1<rte-code> that represents <rte-code>opacity<rte-code>.

At first, we render <rte-code>Animated.View<rte-code> and we start building our animation tree on both JS and native sides at the same time. To avoid flickering, before sending our nodes, we calculate initial values on the JS side and library by using <rte-code>setNativeProps<rte-code> method to set these values directly as component props. The way how we calculate values on both JS and the native side is very similar.

In the diagram below, we have our animation tree that is replicated on both sides (native and JS).

Note: All diagrams below represent animated nodes not props in React.
example props node diagram

‍

As you may already observe, nodes have a bidirectional connection. It means that the node can be accessed by going deeper into the tree (from parent) or by going upwards to the parent (from children). This technique is crucial for the animation tree evaluation process that will be described below.

The building tree process uses the bottom to top approach. It means that: <rte-code>Props<rte-code> node wants to be attached to the tree but it requires its children to be attached before. <rte-code>Value<rte-code> node will become the eldest ancestor (in our case it is the root of our animation tree). It's children's will to inform that they should be evaluated in the next loop tick.

children scheduled for updates diagram

‍

Then, all marked nodes and their children will be processed in the next loop tick. In this step, we want to find all affected final nodes by searching through marked children nodes. Final nodes are one of these types: <rte-code>Props<rte-code> or <rte-code>Always<rte-code> (we will cover it later on). Nodes are evaluated recursively from the top to bottom (starting from the final nodes). That's why this bidirectional connection is crucial in order to make this process work correctly.

In our case, the Props node will evaluate <rte-code>Style<rte-code> node and <rte-code>Style<rte-code> node will evaluate <rte-code>Value<rte-code> node. The Value node will return actual held value that will be used by its parent and so on.

props final node evaluation example diagram

‍

After all the updates, the library is taking care of UI updates on the native side therefore new values will be visible for the end users.

What about a more interactive/real-world example?

How to trigger animations?

To make an animation run, we have to trigger it, it's quite obvious. In this chapter, I will describe two way sof triggering animations in React Native Reanimated library - by using <rte-code>setValue<rte-code> and by running <rte-code>Clock<rte-code>.

Animation triggered by setValue

We're going to start from the ground up. Together we'll deeply analyze a popular fade out animation that should be triggered once the button <rte-code>Hide<rte-code> has been pressed.

import React from "react";
import { StyleSheet, View, Button } from "react-native";
import Animated, { Value } from "react-native-reanimated";
const AnimatedComponent = () => {
const opacity = new Value(1);
return (
<>
<Animated.View
style={{
width: 300,
height: 300,
backgroundColor: 'red',
opacity
}}
/>
<Button title="Hide" onPress={() => opacity.setValue(0)} />
</>
);
};
view raw gistfile1.txt hosted with ❤ by GitHub

The only change from the previous example is that once we have pressed the <rte-code>Hide<rte-code> button then our <rte-code>Animated.View<rte-code> immediately disappears. What happened here? Look at the diagram below.

props final node evaluation diagram

‍

<rte-code>Value<rte-code> node informs that its value has been updated. As a result of that, all its children should be re-evaluated. Node manager will find our <rte-code>Props<rte-code> final node and start re-evaluation from there. In the end, re-evaluated <rte-code>Props<rte-code> node will be used for the UI updates.

Okay, but there was no animation at all right? Currently, we only know one way for triggering updates. It is done by calling setValue method directly on the <rte-code>Animated.Value<rte-code>.

Triggering animation by running Clock

We have to update our example a bit in order to make an actual animation.

```tsx
import React, { useState } from 'react';
import { Button } from 'react-ative';
import Animated, {
Value,
Easing,
startClock,
timing,
Clock,
useCode,
cond,
eq,
} from 'react-native-reanimated';
const AnimatedComponent = () => {
const [counter, setCounter] = useState(0);
const startAnimation = new Value<0 | 1>(0);
const opacity = new Value(1);
const clock = new Clock();
const state = {
finished: new Value(0),
position: opacity,
frameTime: new Value(0),
time: new Value(0),
};
const config = {
toValue: 0,
easing: Easing.inOut(Easing.ease),
duration: 500,
};
useCode(
() => [
cond(eq(startAnimation, 1), [
startClock(clock),
timing(clock, state, config),
]),
],
[counter],
);
return (
<>
<Animated.View
style={{
width: 300,
height: 300,
backgroundColor: 'red',
opacity,
}}
/>
<Button title="Hide" onPress={() => startAnimation.setValue(1)} />
<Button title="Reset" onPress={() => setCounter((s) => s + 1)} />
</>
);
}
```
view raw gistfile1.txt hosted with ❤ by GitHub

There are few things that need in-depth explanation so let's go through it step by step:

  • We create a counter variable that is used only for resetting animation state (triggering re-render)
  • We create a <rte-code>startAnimation<rte-code> that is an instance of <rte-code>Animated.Value<rte-code> .It will be used to trigger an animation once Hide button has been pressed.
  • We create an <rte-code>opacity<rte-code> as in the previous example.
  • We create a <rte-code>clock<rte-code> variables that is an instance of <rte-code>Animated.Clock<rte-code>.
  • We create <rte-code>state<rte-code> object that holds animation's state.
  • We create <rte-code>config<rte-code> object that holds animation's config.
  • We use <rte-code>useCode<rte-code> hook in order to define our animation tree and attach it properly. Under the hood returned value is wrapped up with <rte-code>Always<rte-code> node (second kind of final node).

Let's look at the diagram below to see what's going on here.

code example animation tree diagram

‍

This is our simplified animation tree (timing isn't a node itself it just groups up more nodes like <rte-code>Set Cond<rte-code> etc.). It also links our previous animation tree therefore updating <rte-code>Value<rte-code> (later on) will cause <rte-code>Props<rte-code> to trigger re-evaluation as well.

Once <rte-code>Hide<rte-code> button has been pressed. we set <rte-code>startAnimation<rte-code> value to 1. This node will notify node manager that it should be updated in the next loop tick . The node manager will go through children until it finds <rte-code>Always<rte-code> node (final node) which will start re-evaluation of the whole subtree.

Finding final nodes phase:

always node evaluation diagram part 1

‍

Evaluating Always node phase:

  • <rte-code>Cond<rte-code> node will be evaluated to <rte-code>true<rte-code>
  • <rte-code>StartClock<rte-code> will be evaluated and it will start <rte-code>Clock<rte-code> node which will notify nodes manager that it should be taken into consideration in each loop tick until it will be stopped by <rte-code>StopClock<rte-code> node.
  • Our invented <rte-code>Timing<rte-code> node will update Value node and this node will be processed in the next loop tick.
always node evaluation diagram part 2

‍

Nodes that notified the nodes manager are marked with red color. The final nodes found, which are <rte-code>Always<rte-code> and <te-code>Props<rte-code>, are marked in yellow.

final nodes found diagram

‍

After that, the process is repeated and new values of <rte-code>Value<rte-code> node are calculated by <rte-code>Timing<rte-code> in each animation loop tick and update <rte-code>Value<rte-code> node itself. On each frame <rte-code>Clock<rte-code>, while it is running, it will notify nodes manager and as a result again <rte-code>Always<rte-code> and <rte-code>Props<rte-code> nodes will be found and then evaluated.

Summary

As you can see, the new language may seem unintuitive but offers many possibilities. To create smooth animation with this language, we need to change our mindset - from imperative to declarative and get used to <rte-code>Clock<rte-code>, which is essential for triggering animations in desired scenarios and under the assumed conditions.

If you'd like to read more about the React Native Reanimated v1 library, check out the official docs.

And that's all for today. As the v2 is already live, stay tuned for more articles about React Native Reanimated library!To provide users with smoothly working apps, we want our animations to run at 60 fps or even better. However it may sound like a bit of a challenge, with an Open Source library called react-native-reanimated, we can do it!

Insights

Learn more about Animations

cover

Jul 8Podcast

React Native Ease: Platform-Native Animations Without the Extra Work

Łukasz talks with Janic from App & Flow about React Native Ease, Core Animation, Reanimated trade-offs, and performant UI animation in React Native.

cover

Jul 7Podcast

Where Reanimated Goes Next

A conversation with Bartłomiej Błoniarz on CSS animations in Reanimated, native primitives on iOS, shared element transitions, and the work still left on stability.

cover

Oct 30Live Stream

Reanimated, TanStack, Cross-Pollinating Languages & TWIR #256 Anniversary

Catch up on everything from TanStack updates and Astro insights to Reanimated deep dive and even Swift for Android. Plus, we celebrate the 256th newsletter!

Events Cover

Apr 16Talk

How to Make Your React (Native) App Feel Native

How to make your React Native app feel truly native, across iOS, Android, tvOS & more. Dive into best practices, navigation, and react-native-bottom-tabs.

cover

Feb 19Podcast

Reanimated 4 is the Future of Smooth React Native Animations

Reanimated 4 is now available in beta, and if you work with animations in React Native, you need to know what’s new!

cover

Apr 15Article

ReanimatedArc - Build Circular Animated UI Elements in React Native

Build good-looking and smooth animated arcs in React Native with ReanimatedArc library.

cover

Oct 25Article

Custom Screen Transitions in React Navigation

Wondering how to custom a screen transition animation in React Navigation? Check this step-by-step guide.

cover

Aug 8Article

Reanimating Your React Native Experience

Read how to achieve delightful developer experience with react-native-gesture and react-native-animated.

cover

Jan 17Article

React Native Animations Revisited - Part III

Learn how to respond to screen gestures with PanResponder. Check the last part of our Callstack series on React Native animations!

cover

Jan 10Article

React Native Animations Revisited - Part II

In this part of the three-part guide about animations in React Native we cover creating animations with Animated API.

cover

Jan 4Article

React Native Animations Revisited - Part I

Three-part guide for developers explaining how to deal with animations in React Native - Part 1.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.