Reanimated 2
info
The Reanimated 2 integration operates on the JS thread. We recommend using Reanimated 3 when possible for better performance and features. For more details, refer to the Reanimated 3 support section.
While Reanimated v2 is fully integrated and functional, there are two important caveats to consider:
Animations on the JS Thread
In the example below, the animation runs on the JS thread due to the use of Reanimated v2, even though the animation itself is fairly simple.
tsx
import {useEffect } from "react";import {Canvas ,Rect ,mix } from "@shopify/react-native-skia";import {useSharedValue ,withRepeat ,withTiming ,useDerivedValue } from "react-native-reanimated";constMyComponent = () => {constprogress =useSharedValue (0);useEffect (() => {progress .value =withRepeat (withTiming (1, {duration : 3000 }), -1, true);}, [progress ]);constx =useDerivedValue (() => {returnmix (progress .value , 0, 100);});return (<Canvas style ={{flex : 1 }}><Rect x ={x }y ={100}width ={10}height ={10}color ="red"/></Canvas >);};
tsx
import {useEffect } from "react";import {Canvas ,Rect ,mix } from "@shopify/react-native-skia";import {useSharedValue ,withRepeat ,withTiming ,useDerivedValue } from "react-native-reanimated";constMyComponent = () => {constprogress =useSharedValue (0);useEffect (() => {progress .value =withRepeat (withTiming (1, {duration : 3000 }), -1, true);}, [progress ]);constx =useDerivedValue (() => {returnmix (progress .value , 0, 100);});return (<Canvas style ={{flex : 1 }}><Rect x ={x }y ={100}width ={10}height ={10}color ="red"/></Canvas >);};
Working with Host Objects
When your animation involves Skia objects and Reanimated v2, you must ensure that the code explicitly runs on the JS thread. To facilitate this, you can use the useDerivedValueOnJS
hook.
tsx
import {useDerivedValueOnJS ,Skia } from "@shopify/react-native-skia";import {useDerivedValue ,useSharedValue } from "react-native-reanimated";constpath =Skia .Path .MakeFromSVGString ("M 344 172 Q 344 167 343.793 163")!;constpath2 =Skia .Path .MakeFromSVGString ("M 347 169 Q 344 167 349 164")!;constprogress =useSharedValue (0.5);// ❌ This will crash as it's running on the worklet threaduseDerivedValue (() =>path .interpolate (path2 ,progress .value ));// ✅ This will work as expected since it runs on the JS threaduseDerivedValueOnJS (() =>path .interpolate (path2 ,progress .value ), [progress ]);
tsx
import {useDerivedValueOnJS ,Skia } from "@shopify/react-native-skia";import {useDerivedValue ,useSharedValue } from "react-native-reanimated";constpath =Skia .Path .MakeFromSVGString ("M 344 172 Q 344 167 343.793 163")!;constpath2 =Skia .Path .MakeFromSVGString ("M 347 169 Q 344 167 349 164")!;constprogress =useSharedValue (0.5);// ❌ This will crash as it's running on the worklet threaduseDerivedValue (() =>path .interpolate (path2 ,progress .value ));// ✅ This will work as expected since it runs on the JS threaduseDerivedValueOnJS (() =>path .interpolate (path2 ,progress .value ), [progress ]);
Similarly, when using host objects inside a gesture handler, ensure that its execution is on the JS thread:
tsx
import {vec } from "@shopify/react-native-skia";import {Gesture } from "react-native-gesture-handler";import {useSharedValue } from "react-native-reanimated";constpos =useSharedValue (vec (0, 0));// ❌ This will crash as it's running on the worklet threadGesture .Pan ().onChange (e =>pos .value =vec (e .x , 0));// ✅ This will work as expected since it runs on the JS threadGesture .Pan ().runOnJS (true).onChange (e =>pos .value =vec (e .x , 0));
tsx
import {vec } from "@shopify/react-native-skia";import {Gesture } from "react-native-gesture-handler";import {useSharedValue } from "react-native-reanimated";constpos =useSharedValue (vec (0, 0));// ❌ This will crash as it's running on the worklet threadGesture .Pan ().onChange (e =>pos .value =vec (e .x , 0));// ✅ This will work as expected since it runs on the JS threadGesture .Pan ().runOnJS (true).onChange (e =>pos .value =vec (e .x , 0));