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 enables rich multimedia experiences by allowing you to load video frames as images. Video frames can be used anywhere a Skia image is accepted: Image, ImageShader, and Atlas components.
Requirements
- React Native Reanimated version 3 or higher
- Android API level 26 or higher
- Web Fully supported
useVideo Hook
The useVideo hook loads video files and provides the current frame as a shared value.
Basic Example
import { Canvas, Image, useVideo } from "@shopify/react-native-skia";
import { useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
const VideoExample = () => {
const { width, height } = useWindowDimensions();
const { currentFrame } = useVideo("https://bit.ly/skia-video");
return (
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
</Canvas>
);
};
Returned Values
The useVideo hook returns an object with:
| Property | Type | Description |
|---|
| currentFrame | SharedValue<SkImage | null> | Current video frame or null if not loaded |
| currentTime | SharedValue<number> | Current playback position in milliseconds |
| duration | number | Total video duration in milliseconds |
| framerate | number | Video frame rate (frames per second) |
| rotation | 0 | 90 | 180 | 270 | Video rotation in degrees |
| size | { width: number, height: number } | Video dimensions |
Using Returned Values
import { Canvas, Image, useVideo, Text } from "@shopify/react-native-skia";
import { useDerivedValue } from "react-native-reanimated";
const VideoWithInfo = () => {
const { currentFrame, currentTime, duration, framerate, size } = useVideo(
"https://bit.ly/skia-video"
);
const timeText = useDerivedValue(
() => `${Math.floor(currentTime.value / 1000)}s / ${Math.floor(duration / 1000)}s`
);
return (
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={size.width}
height={size.height}
fit="contain"
/>
<Text x={10} y={30} text={timeText} />
</Canvas>
);
};
Playback Options
Control video playback with options:
| Option | Type | Description |
|---|
| paused | SharedValue<boolean> | Controls pause state |
| looping | SharedValue<boolean> | Enables looping |
| seek | SharedValue<number | null> | Seek to specific time in milliseconds |
| volume | SharedValue<number> | Volume level (0-1, where 0 is muted) |
Pause and Play
import { Canvas, Image, useVideo } from "@shopify/react-native-skia";
import { Pressable } from "react-native";
import { useSharedValue } from "react-native-reanimated";
const PauseableVideo = () => {
const paused = useSharedValue(false);
const { currentFrame } = useVideo(
"https://bit.ly/skia-video",
{ paused }
);
return (
<Pressable
style={{ flex: 1 }}
onPress={() => {
paused.value = !paused.value;
}}
>
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={400}
height={300}
fit="cover"
/>
</Canvas>
</Pressable>
);
};
Looping Video
import { useSharedValue } from "react-native-reanimated";
const LoopingVideo = () => {
const looping = useSharedValue(true);
const { currentFrame } = useVideo(
"https://bit.ly/skia-video",
{ looping }
);
return (
<Canvas style={{ flex: 1 }}>
<Image image={currentFrame} x={0} y={0} width={400} height={300} />
</Canvas>
);
};
Seeking
import { Canvas, Image, useVideo } from "@shopify/react-native-skia";
import { Pressable } from "react-native";
import { useSharedValue } from "react-native-reanimated";
const SeekableVideo = () => {
const seek = useSharedValue<number | null>(null);
const { currentFrame, currentTime } = useVideo(
"https://bit.ly/skia-video",
{ seek, looping: true }
);
return (
<Pressable
style={{ flex: 1 }}
onPress={() => {
// Seek to 2 seconds
seek.value = 2000;
}}
>
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={400}
height={300}
fit="cover"
/>
</Canvas>
</Pressable>
);
};
Volume Control
import { useSharedValue } from "react-native-reanimated";
import { Slider } from "react-native";
const VideoWithVolume = () => {
const volume = useSharedValue(1.0);
const { currentFrame } = useVideo(
"https://bit.ly/skia-video",
{ volume }
);
return (
<>
<Canvas style={{ flex: 1 }}>
<Image image={currentFrame} x={0} y={0} width={400} height={300} />
</Canvas>
<Slider
value={volume.value}
onValueChange={(value) => {
volume.value = value;
}}
minimumValue={0}
maximumValue={1}
/>
</>
);
};
Loading Videos
From Network
const { currentFrame } = useVideo("https://example.com/video.mp4");
From Local File
const { currentFrame } = useVideo("file:///path/to/video.mp4");
From Assets with Expo
import { useVideo } from "@shopify/react-native-skia";
import { useAssets } from "expo-asset";
const useVideoFromAsset = (
mod: number,
options?: Parameters<typeof useVideo>[1]
) => {
const [assets, error] = useAssets([mod]);
if (error) {
throw error;
}
return useVideo(assets ? assets[0].localUri : null, options);
};
// Usage
const VideoFromAsset = () => {
const { currentFrame } = useVideoFromAsset(
require("./assets/BigBuckBunny.mp4")
);
return (
<Canvas style={{ flex: 1 }}>
<Image image={currentFrame} x={0} y={0} width={400} height={300} />
</Canvas>
);
};
Handling Rotation
Videos can have rotation metadata. Use the fitbox function to handle rotation:
import { Canvas, Image, useVideo, fitbox, rect } from "@shopify/react-native-skia";
import { useWindowDimensions } from "react-native";
const RotatedVideo = () => {
const { width, height } = useWindowDimensions();
const { currentFrame, rotation, size } = useVideo(
"https://bit.ly/skia-video"
);
const src = rect(0, 0, size.width, size.height);
const dst = rect(0, 0, width, height);
const transform = fitbox("cover", src, dst, rotation);
return (
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="none"
transform={transform}
/>
</Canvas>
);
};
Using with Shaders
Video frames can be used as shader inputs:
import {
Canvas,
Fill,
ImageShader,
ColorMatrix,
useVideo,
} from "@shopify/react-native-skia";
import { useWindowDimensions } from "react-native";
const VideoWithShader = () => {
const { width, height } = useWindowDimensions();
const { currentFrame } = useVideo("https://bit.ly/skia-video");
return (
<Canvas style={{ flex: 1 }}>
<Fill>
<ImageShader
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
<ColorMatrix
matrix={[
0.95, 0, 0, 0, 0.05,
0.65, 0, 0, 0, 0.15,
0.15, 0, 0, 0, 0.5,
0, 0, 0, 1, 0,
]}
/>
</Fill>
</Canvas>
);
};
Complete Player Example
import { Canvas, Image, useVideo } from "@shopify/react-native-skia";
import { View, Pressable, Text, StyleSheet } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import { useState } from "react";
const VideoPlayer = () => {
const [isPlaying, setIsPlaying] = useState(true);
const paused = useSharedValue(false);
const looping = useSharedValue(true);
const { currentFrame, currentTime, duration, size } = useVideo(
"https://bit.ly/skia-video",
{ paused, looping }
);
const togglePlay = () => {
paused.value = !paused.value;
setIsPlaying(!isPlaying);
};
return (
<View style={styles.container}>
<Canvas style={styles.canvas}>
<Image
image={currentFrame}
x={0}
y={0}
width={size.width}
height={size.height}
fit="contain"
/>
</Canvas>
<View style={styles.controls}>
<Pressable style={styles.button} onPress={togglePlay}>
<Text>{isPlaying ? "Pause" : "Play"}</Text>
</Pressable>
<Text>
{Math.floor(currentTime.value / 1000)}s / {Math.floor(duration / 1000)}s
</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
canvas: {
flex: 1,
},
controls: {
flexDirection: "row",
justifyContent: "space-between",
padding: 10,
backgroundColor: "#000",
},
button: {
padding: 10,
backgroundColor: "#007AFF",
borderRadius: 5,
},
});
- Use appropriate video resolution for your use case
- Consider using lower frame rates for better performance
- Limit the number of simultaneous videos
- Pause videos when not visible
- Use hardware acceleration when available
Video Encoding
To encode videos from Skia images:
Supported formats vary by platform:
iOS:
Android:
Web:
Troubleshooting
Video Not Playing
- Check that the video URL is accessible
- Verify the video format is supported
- Ensure Reanimated is properly installed
- Check Android API level (26+)
- Reduce video resolution
- Lower frame rate
- Use hardware-accelerated formats
- Limit number of simultaneous videos
Audio Issues
- Check volume settings
- Verify audio is present in source file
- Test with different video files
See Also