When it comes to building buttery-smooth animations in React Native, Reanimated stands out. But what makes Reanimated so powerful is its ability to offload heavy computation and animation logic from the JavaScript thread to the UI thread using something called worklets.
Let’s break down what worklets are, why threading matters, and how you can leverage them like a pro using Reanimated 3+!
🧠 What Are Worklets?
Worklets are short-running JavaScript functions that execute on the UI thread.
Normally, React Native animations and gestures run on the JS thread, but that can lead to dropped frames and lag if the JS thread is busy. Worklets solve this problem by running independently on the UI thread, enabling frame-perfect performance.
To convert a function into a worklet, simply prefix it with 'worklet'
as the first line inside the function.
const myWorkletFunction = () => {
'worklet';
// Runs on the UI thread
};
✨ Key Features of Worklets
- Runs in a Separate Thread: Keeps UI animations smooth even when JS is blocked.
-
Simple Syntax: Add
'worklet'
to turn any function into a worklet. - Performance-Optimized: Ideal for gestures, transitions, and animations.
🧵 Threading in Reanimated
Reanimated gives you fine control over where and how your functions run. Here’s a quick breakdown:
Function | Description | Use Case |
---|---|---|
runOnJS(fn) |
Runs a worklet function on the JS thread | Update state or call non-worklet code |
runOnUI(fn) |
Runs a JS function on the UI thread | Trigger animations from JS |
runOnRuntime(fn, rt) |
Runs function on a custom runtime | Handle heavy calculations in background |
createWorkletRuntime(name) |
Creates a new worklet runtime | Manage separate execution environments |
💻 Practical Example: Combining Threads for Best UX
Let’s look at a hands-on example with animation, tap gestures, and offloading a heavy task.
const handleTap = Gesture.Tap().onEnd(() => {
'worklet';
runOnJS(updateCounter)(); // Update React state
scale.value = withSpring(scale.value === 1 ? 1.5 : 1); // UI animation
});
Here’s what’s happening:
-
runOnJS()
ensures we can update the React state from the UI thread. -
withSpring()
animates the scale using the UI thread.
const startHeavyTask = () => {
runOnRuntime(runtime, () => {
'worklet';
let sum = 0;
for (let i = 0; i < 1000000; i++) sum += i;
runOnJS(updateComputationResult)(sum); // Push result back to JS
})();
};
This is a great example of using runOnRuntime
to run a CPU-intensive task (like sum calculation) on a custom runtime without blocking your animation or gestures.
📦 Why This Matters
In real-world apps, it's very common to:
- Animate elements in response to gestures
- Perform heavy calculations (e.g., filtering, math, AI ops)
- Keep UI snappy and lag-free
By using worklets + threading, you split the workload efficiently across multiple threads, making your apps feel native and responsive.
🎯 Final Thoughts
React Native Reanimated offers developers a superpower: the ability to control how code runs across threads. With worklet
, runOnUI
, runOnJS
, and custom runtimes, you can push performance boundaries and deliver stunning animations even in the most demanding UI interactions.
🔧 Code Sandbox / Demo
You can try the code above in a React Native project with react-native-reanimated
and react-native-gesture-handler
configured. Make sure to add this in your babel.config.js
:
plugins: [
[
'react-native-reanimated/plugin',
{
processNestedWorklets: true,
},
],
],
Top comments (0)