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 integrates seamlessly with Reanimated 2+ to create high-performance animations that run on the UI thread.
Basic Concept
Use Reanimated’s useSharedValue and animation functions with Skia components. The values update on the UI thread for smooth 60fps+ animations.
import { useSharedValue, withRepeat, withTiming } from "react-native-reanimated";
import { Canvas, Circle } from "@shopify/react-native-skia";
import { useEffect } from "react";
export default function AnimatedCircle() {
const cx = useSharedValue(100);
useEffect(() => {
cx.value = withRepeat(
withTiming(200, { duration: 1000 }),
-1,
true
);
}, []);
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={cx} cy={150} r={50} color="blue" />
</Canvas>
);
}
Shared Values
useSharedValue
Create a value that can be animated on the UI thread:
import { useSharedValue } from "react-native-reanimated";
const x = useSharedValue(0);
const y = useSharedValue(0);
const rotation = useSharedValue(0);
const scale = useSharedValue(1);
Setting Values
// Direct assignment
x.value = 100;
// With animation
x.value = withTiming(100, { duration: 500 });
x.value = withSpring(100);
x.value = withDecay({ velocity: 1000 });
Animation Functions
withTiming
Animate to a value over time:
import { withTiming, Easing } from "react-native-reanimated";
// Basic timing
position.value = withTiming(100, { duration: 500 });
// With easing
position.value = withTiming(100, {
duration: 500,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
});
// With callback
position.value = withTiming(100, { duration: 500 }, (finished) => {
if (finished) {
console.log("Animation completed");
}
});
withSpring
Animate with spring physics:
import { withSpring } from "react-native-reanimated";
// Default spring
position.value = withSpring(100);
// Custom spring config
position.value = withSpring(100, {
damping: 10,
mass: 1,
stiffness: 100,
overshootClamping: false,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 2,
});
withRepeat
Repeat an animation:
import { withRepeat, withTiming } from "react-native-reanimated";
// Infinite loop
rotation.value = withRepeat(
withTiming(Math.PI * 2, { duration: 2000 }),
-1 // -1 for infinite
);
// Repeat 3 times with reverse
position.value = withRepeat(
withTiming(200, { duration: 1000 }),
3,
true // reverse on alternate cycles
);
withSequence
Chain animations:
import { withSequence, withTiming, withDelay } from "react-native-reanimated";
opacity.value = withSequence(
withTiming(1, { duration: 500 }),
withDelay(1000, withTiming(0, { duration: 500 }))
);
withDecay
Animate with decay (deceleration):
import { withDecay } from "react-native-reanimated";
position.value = withDecay({
velocity: 1000,
deceleration: 0.998,
});
Derived Values
useDerivedValue
Compute values based on other shared values:
import { useDerivedValue } from "react-native-reanimated";
const progress = useSharedValue(0);
const scale = useDerivedValue(() => {
return 1 + progress.value * 0.5;
});
const rotation = useDerivedValue(() => {
return progress.value * Math.PI * 2;
});
Gestures
Combine with Reanimated gestures:
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { useSharedValue } from "react-native-reanimated";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function DraggableCircle() {
const x = useSharedValue(150);
const y = useSharedValue(150);
const gesture = Gesture.Pan()
.onChange((e) => {
x.value += e.changeX;
y.value += e.changeY;
});
return (
<GestureDetector gesture={gesture}>
<Canvas style={{ flex: 1 }}>
<Circle cx={x} cy={y} r={50} color="red" />
</Canvas>
</GestureDetector>
);
}
Using onSize
Get canvas dimensions reactively:
import { useSharedValue } from "react-native-reanimated";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function ResponsiveCircle() {
const size = useSharedValue({ width: 0, height: 0 });
return (
<Canvas style={{ flex: 1 }} onSize={size}>
<Circle
cx={() => size.value.width / 2}
cy={() => size.value.height / 2}
r={50}
color="blue"
/>
</Canvas>
);
}
Worklet Functions
Use worklet functions for complex animations:
import { useDerivedValue } from "react-native-reanimated";
const progress = useSharedValue(0);
const animatedPath = useDerivedValue(() => {
"worklet";
const path = Skia.Path.Make();
const r = 100 + progress.value * 50;
path.addCircle(150, 150, r);
return path;
});
<Path path={animatedPath} color="green" />
Examples
Fade In Animation
import { useSharedValue, withTiming } from "react-native-reanimated";
import { useEffect } from "react";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function FadeIn() {
const opacity = useSharedValue(0);
useEffect(() => {
opacity.value = withTiming(1, { duration: 1000 });
}, []);
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={150} cy={150} r={100} color="blue" opacity={opacity} />
</Canvas>
);
}
Continuous Rotation
import { useSharedValue, withRepeat, withTiming } from "react-native-reanimated";
import { useEffect } from "react";
import { Canvas, Rect } from "@shopify/react-native-skia";
export default function RotatingRect() {
const rotation = useSharedValue(0);
useEffect(() => {
rotation.value = withRepeat(
withTiming(Math.PI * 2, { duration: 2000 }),
-1
);
}, []);
return (
<Canvas style={{ flex: 1 }}>
<Rect
x={0}
y={0}
width={100}
height={100}
color="red"
origin={{ x: 50, y: 50 }}
transform={[{ rotate: rotation }]}
/>
</Canvas>
);
}
Bouncing Animation
import { useSharedValue, withRepeat, withSequence, withTiming } from "react-native-reanimated";
import { useEffect } from "react";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function BouncingBall() {
const y = useSharedValue(100);
useEffect(() => {
y.value = withRepeat(
withSequence(
withTiming(300, { duration: 500 }),
withTiming(100, { duration: 500 })
),
-1
);
}, []);
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={150} cy={y} r={30} color="orange" />
</Canvas>
);
}
Interactive Scale
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { useSharedValue, withSpring } from "react-native-reanimated";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function ScalableCircle() {
const scale = useSharedValue(1);
const savedScale = useSharedValue(1);
const pinch = Gesture.Pinch()
.onUpdate((e) => {
scale.value = savedScale.value * e.scale;
})
.onEnd(() => {
savedScale.value = scale.value;
});
return (
<GestureDetector gesture={pinch}>
<Canvas style={{ flex: 1 }}>
<Circle
cx={150}
cy={150}
r={() => 50 * scale.value}
color="purple"
/>
</Canvas>
</GestureDetector>
);
}
- All animations run on the UI thread - no bridge crossing
- Use worklet functions for complex calculations
- Avoid using JavaScript values inside worklets
- Mark functions with
"worklet" directive when needed
- Use
useDerivedValue for computed values
- Batch updates when animating multiple values