Documentation Index
Fetch the complete documentation index at: https://mintlify.com/shopify/react-native-skia/llms.txt
Use this file to discover all available pages before exploring further.
React Native Skia provides several hooks and utilities to simplify common animation patterns and workflows.
useValue
Deprecated: Use Reanimated’s useSharedValue instead.
The legacy useValue hook is deprecated in favor of Reanimated 3’s useSharedValue for better performance and features.
// Old (deprecated)
import { useValue } from "@shopify/react-native-skia";
const x = useValue(0);
// New (recommended)
import { useSharedValue } from "react-native-reanimated";
const x = useSharedValue(0);
useComputedValue
Deprecated: Use Reanimated’s useDerivedValue instead.
The legacy useComputedValue hook is deprecated in favor of Reanimated 3’s useDerivedValue.
// Old (deprecated)
import { useComputedValue } from "@shopify/react-native-skia";
const doubled = useComputedValue(() => x.current * 2, [x]);
// New (recommended)
import { useDerivedValue } from "react-native-reanimated";
const doubled = useDerivedValue(() => x.value * 2);
useLoop
Create looping animations with Reanimated:
import { useSharedValue, withRepeat, withTiming, Easing } from "react-native-reanimated";
import { useEffect } from "react";
const useLoop = (duration: number = 1000) => {
const progress = useSharedValue(0);
useEffect(() => {
progress.value = withRepeat(
withTiming(1, { duration, easing: Easing.linear }),
-1, // infinite
false // don't reverse
);
}, [duration]);
return progress;
};
// Usage
const LoopDemo = () => {
const progress = useLoop(2000);
const rotation = useDerivedValue(() => {
return progress.value * Math.PI * 2;
});
return (
<Canvas style={{ flex: 1 }}>
<Group transform={[{ rotate: rotation }]}>
<Rect x={108} y={108} width={40} height={40} color="blue" />
</Group>
</Canvas>
);
};
useTiming
Simplify timing animations:
import { useSharedValue, withTiming } from "react-native-reanimated";
import { useEffect } from "react";
const useTiming = (
toValue: number,
duration: number = 300,
delay: number = 0
) => {
const value = useSharedValue(0);
useEffect(() => {
const timer = setTimeout(() => {
value.value = withTiming(toValue, { duration });
}, delay);
return () => clearTimeout(timer);
}, [toValue, duration, delay]);
return value;
};
// Usage
const TimingDemo = () => {
const opacity = useTiming(1, 500);
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={128} cy={128} r={50} color="red" opacity={opacity} />
</Canvas>
);
};
useSpring
Simplify spring animations:
import { useSharedValue, withSpring } from "react-native-reanimated";
import { useEffect } from "react";
const useSpring = (
toValue: number,
config?: { damping?: number; stiffness?: number }
) => {
const value = useSharedValue(0);
useEffect(() => {
value.value = withSpring(toValue, config);
}, [toValue]);
return value;
};
// Usage
const SpringDemo = () => {
const scale = useSpring(1.5, { damping: 10, stiffness: 100 });
return (
<Canvas style={{ flex: 1 }}>
<Group transform={[{ scale }]}>
<Circle cx={128} cy={128} r={50} color="green" />
</Group>
</Canvas>
);
};
useAnimatedValue
Manage animated values with controls:
import { useSharedValue, withTiming, cancelAnimation } from "react-native-reanimated";
import { useCallback } from "react";
const useAnimatedValue = (initialValue: number = 0) => {
const value = useSharedValue(initialValue);
const animateTo = useCallback((toValue: number, duration: number = 300) => {
value.value = withTiming(toValue, { duration });
}, []);
const reset = useCallback(() => {
value.value = withTiming(initialValue);
}, [initialValue]);
const stop = useCallback(() => {
cancelAnimation(value);
}, []);
return { value, animateTo, reset, stop };
};
// Usage
const AnimatedValueDemo = () => {
const { value, animateTo, reset } = useAnimatedValue(0);
return (
<View>
<Canvas style={{ width: 256, height: 256 }}>
<Circle cx={value} cy={128} r={20} color="blue" />
</Canvas>
<Button title="Animate" onPress={() => animateTo(200)} />
<Button title="Reset" onPress={reset} />
</View>
);
};
useAnimatedPath
Animate between paths:
import { useDerivedValue, useSharedValue } from "react-native-reanimated";
import { Skia, SkPath } from "@shopify/react-native-skia";
const useAnimatedPath = (from: SkPath, to: SkPath) => {
const progress = useSharedValue(0);
const path = useDerivedValue(() => {
return from.interpolate(to, progress.value)!;
});
const animateTo = useCallback((target: number, duration: number = 300) => {
progress.value = withTiming(target, { duration });
}, []);
return { path, animateTo, progress };
};
// Usage
const PathMorphDemo = () => {
const circle = Skia.Path.Make();
circle.addCircle(128, 128, 64);
const rect = Skia.Path.Make();
rect.addRect(Skia.XYWHRect(64, 64, 128, 128));
const { path, animateTo } = useAnimatedPath(circle, rect);
return (
<View>
<Canvas style={{ width: 256, height: 256 }}>
<Path path={path} color="purple" />
</Canvas>
<Button title="To Rect" onPress={() => animateTo(1)} />
<Button title="To Circle" onPress={() => animateTo(0)} />
</View>
);
};
useClockValue
Create time-based animations:
import { useFrameCallback, useSharedValue } from "react-native-reanimated";
const useClockValue = () => {
const time = useSharedValue(0);
useFrameCallback((frameInfo) => {
time.value = frameInfo.timestamp / 1000; // Convert to seconds
});
return time;
};
// Usage
const ClockDemo = () => {
const time = useClockValue();
const x = useDerivedValue(() => {
return 128 + Math.cos(time.value * 2) * 50;
});
const y = useDerivedValue(() => {
return 128 + Math.sin(time.value * 2) * 50;
});
return (
<Canvas style={{ width: 256, height: 256 }}>
<Circle cx={x} cy={y} r={20} color="red" />
</Canvas>
);
};
useAnimatedSwitch
Toggle between two values:
import { useSharedValue, withTiming } from "react-native-reanimated";
import { useState, useEffect } from "react";
const useAnimatedSwitch = (
offValue: number,
onValue: number,
initialState: boolean = false
) => {
const [isOn, setIsOn] = useState(initialState);
const value = useSharedValue(initialState ? onValue : offValue);
useEffect(() => {
value.value = withTiming(isOn ? onValue : offValue);
}, [isOn, offValue, onValue]);
const toggle = useCallback(() => {
setIsOn((prev) => !prev);
}, []);
return { value, isOn, toggle, setIsOn };
};
// Usage
const SwitchDemo = () => {
const { value, toggle } = useAnimatedSwitch(0, 1);
return (
<View>
<Canvas style={{ width: 256, height: 256 }}>
<Circle cx={128} cy={128} r={50} color="blue" opacity={value} />
</Canvas>
<Button title="Toggle" onPress={toggle} />
</View>
);
};
useSequence
Chain animations in sequence:
import { useSharedValue, withSequence, withTiming } from "react-native-reanimated";
import { useEffect } from "react";
const useSequence = (values: number[], duration: number = 300) => {
const value = useSharedValue(values[0] || 0);
useEffect(() => {
const animations = values.map((v) => withTiming(v, { duration }));
value.value = withSequence(...animations);
}, [values, duration]);
return value;
};
// Usage
const SequenceDemo = () => {
const scale = useSequence([1, 1.5, 1.2, 1], 200);
return (
<Canvas style={{ width: 256, height: 256 }}>
<Group transform={[{ scale }]}>
<Circle cx={128} cy={128} r={50} color="orange" />
</Group>
</Canvas>
);
};
useGesturePosition
Track gesture position:
import { useSharedValue } from "react-native-reanimated";
import { Gesture } from "react-native-gesture-handler";
const useGesturePosition = (initialX: number = 0, initialY: number = 0) => {
const x = useSharedValue(initialX);
const y = useSharedValue(initialY);
const gesture = Gesture.Pan()
.onChange((e) => {
x.value += e.changeX;
y.value += e.changeY;
});
return { x, y, gesture };
};
// Usage
const GesturePositionDemo = () => {
const { x, y, gesture } = useGesturePosition(128, 128);
return (
<GestureDetector gesture={gesture}>
<Canvas style={{ width: 256, height: 256 }}>
<Circle cx={x} cy={y} r={30} color="cyan" />
</Canvas>
</GestureDetector>
);
};
Cleanup Animations
Always clean up animations on unmount:
import { useEffect } from "react";
import { useSharedValue, withRepeat, withTiming, cancelAnimation } from "react-native-reanimated";
const CleanupDemo = () => {
const rotation = useSharedValue(0);
useEffect(() => {
rotation.value = withRepeat(
withTiming(2 * Math.PI, { duration: 2000 }),
-1,
false
);
// Cleanup on unmount
return () => {
cancelAnimation(rotation);
};
}, []);
return (
<Canvas style={{ flex: 1 }}>
<Group transform={[{ rotate: rotation }]}>
<Rect x={108} y={108} width={40} height={40} color="red" />
</Group>
</Canvas>
);
};
Best Practices
- Always use
useSharedValue for animation values
- Use
useDerivedValue for computed values
- Clean up animations on component unmount
- Avoid creating new objects in derived values
- Use
cancelAnimation to stop running animations
- Leverage
useFrameCallback for frame-based animations
- Keep worklets pure and avoid side effects