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 supports animated image formats including GIF and animated WebP. You can play, pause, and control animation playback with precise frame control.
Using Reanimated (Recommended)
The easiest way to display animated images is with the useAnimatedImageValue hook, which integrates seamlessly with React Native Reanimated.
Basic Usage
import { Canvas, Image, useAnimatedImageValue } from "@shopify/react-native-skia";
const AnimatedImages = () => {
// Automatically updates on every frame
const bird = useAnimatedImageValue(
require("./assets/birdFlying.gif")
);
return (
<Canvas style={{ width: 320, height: 180 }}>
<Image
image={bird}
x={0}
y={0}
width={320}
height={180}
fit="contain"
/>
</Canvas>
);
};
The useAnimatedImageValue hook returns a shared value that:
- Starts as
null while loading
- Updates with a new
SkImage on every frame once loaded
- Automatically loops the animation
Controlling Playback
Use a shared value to pause and resume the animation:
import { Canvas, Image, useAnimatedImageValue } from "@shopify/react-native-skia";
import { Pressable } from "react-native";
import { useSharedValue } from "react-native-reanimated";
const AnimatedImagesWithControl = () => {
const isPaused = useSharedValue(false);
const bird = useAnimatedImageValue(
require("./assets/birdFlying.gif"),
isPaused
);
return (
<Pressable onPress={() => {
isPaused.value = !isPaused.value;
}}>
<Canvas style={{ width: 320, height: 180 }}>
<Image
image={bird}
x={0}
y={0}
width={320}
height={180}
fit="contain"
/>
</Canvas>
</Pressable>
);
};
Complete Example with State
import { Canvas, Image, useAnimatedImageValue } from "@shopify/react-native-skia";
import { Pressable, StyleSheet, Text } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import { useState } from "react";
const AnimatedImagePlayer = () => {
const [isPlaying, setIsPlaying] = useState(true);
const isPaused = useSharedValue(false);
const animation = useAnimatedImageValue(
require("./assets/animation.gif"),
isPaused
);
const togglePlayback = () => {
isPaused.value = !isPaused.value;
setIsPlaying(!isPlaying);
};
return (
<>
<Canvas style={styles.canvas}>
<Image
image={animation}
x={0}
y={0}
width={320}
height={180}
fit="contain"
/>
</Canvas>
<Pressable style={styles.button} onPress={togglePlayback}>
<Text>{isPlaying ? "Pause" : "Play"}</Text>
</Pressable>
</>
);
};
const styles = StyleSheet.create({
canvas: {
width: 320,
height: 180,
},
button: {
padding: 10,
backgroundColor: "#007AFF",
alignItems: "center",
marginTop: 10,
},
});
Manual API
For advanced control, use the useAnimatedImage hook which returns an SkAnimatedImage instance.
Loading Animated Images
import { useAnimatedImage } from "@shopify/react-native-skia";
const bird = useAnimatedImage(
require("./assets/birdFlying.gif")
);
SkAnimatedImage Methods
The SkAnimatedImage instance provides several methods:
| Method | Returns | Description |
|---|
getCurrentFrame() | SkImage | Returns the current frame as a regular SkImage |
decodeNextFrame() | number | Decodes and advances to the next frame. Returns -1 if finished |
currentFrameDuration() | number | Returns the duration of the current frame in milliseconds |
getFrameCount() | number | Returns the total number of frames in the animation |
Manual Frame Control
import { useAnimatedImage } from "@shopify/react-native-skia";
const ManualControl = () => {
const bird = useAnimatedImage(
require("./assets/birdFlying.gif")
);
if (!bird) {
return null;
}
// Get the current frame as an SkImage
const image = bird.getCurrentFrame();
// Advance to next frame
const nextFrame = bird.decodeNextFrame();
if (nextFrame === -1) {
console.log("Animation finished");
}
// Get animation info
const duration = bird.currentFrameDuration();
const frameCount = bird.getFrameCount();
console.log({
frameCount,
currentDuration: duration,
image,
});
};
Custom Animation Loop
import { Canvas, Image, useAnimatedImage } from "@shopify/react-native-skia";
import { useDerivedValue, useSharedValue, useFrameCallback } from "react-native-reanimated";
import { useEffect } from "react";
const CustomAnimationLoop = () => {
const animatedImage = useAnimatedImage(
require("./assets/animation.gif")
);
const currentFrame = useSharedValue<SkImage | null>(null);
useFrameCallback(() => {
if (!animatedImage) return;
// Decode next frame
const result = animatedImage.decodeNextFrame();
// Loop back to start if finished
if (result === -1) {
// Animation finished, could restart or stop
}
// Get current frame
currentFrame.value = animatedImage.getCurrentFrame();
});
return (
<Canvas style={{ width: 320, height: 180 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={320}
height={180}
fit="contain"
/>
</Canvas>
);
};
Frame-by-Frame Control
import { Canvas, Image, useAnimatedImage } from "@shopify/react-native-skia";
import { Button, View } from "react-native";
import { useState } from "react";
const FrameByFrame = () => {
const animatedImage = useAnimatedImage(
require("./assets/animation.gif")
);
const [frame, setFrame] = useState(0);
if (!animatedImage) return null;
const nextFrame = () => {
const result = animatedImage.decodeNextFrame();
if (result !== -1) {
setFrame(f => f + 1);
}
};
const image = animatedImage.getCurrentFrame();
const totalFrames = animatedImage.getFrameCount();
return (
<View>
<Canvas style={{ width: 320, height: 180 }}>
<Image
image={image}
x={0}
y={0}
width={320}
height={180}
fit="contain"
/>
</Canvas>
<Button
title={`Next Frame (${frame + 1}/${totalFrames})`}
onPress={nextFrame}
/>
</View>
);
};
React Native Skia supports:
- GIF - Animated GIF images
- WebP - Animated WebP images
Both formats support:
- Variable frame durations
- Looping
- Transparency
Loading Animated Images
From Bundle
const animation = useAnimatedImageValue(
require("./assets/animation.gif")
);
From Network
const animation = useAnimatedImageValue(
"https://example.com/animation.gif"
);
From Native Resources
const animation = useAnimatedImageValue(
"AnimationName" // iOS/Android bundle
);
Frame Rate
- Animations decode one frame at a time
- Heavy animations may impact performance
- Consider reducing frame count for better performance
- Use lower resolution animations when possible
Memory Usage
- Each frame is decoded on demand
- Large animations consume more memory
- Consider using video for long animations
- Monitor memory usage on lower-end devices
Optimization Tips
// Good: Conditional rendering
const OptimizedAnimation = () => {
const [showAnimation, setShowAnimation] = useState(false);
const animation = useAnimatedImageValue(
showAnimation ? require("./assets/heavy.gif") : null
);
return (
<Canvas style={{ flex: 1 }}>
{animation && (
<Image image={animation} x={0} y={0} width={320} height={180} />
)}
</Canvas>
);
};
Common Patterns
Loading State
const AnimationWithLoader = () => {
const animation = useAnimatedImageValue(
require("./assets/animation.gif")
);
return (
<Canvas style={{ width: 320, height: 180 }}>
{!animation ? (
<Text x={100} y={90} text="Loading..." />
) : (
<Image
image={animation}
x={0}
y={0}
width={320}
height={180}
fit="contain"
/>
)}
</Canvas>
);
};
Multiple Animations
const MultipleAnimations = () => {
const isPaused = useSharedValue(false);
const anim1 = useAnimatedImageValue(require("./assets/anim1.gif"), isPaused);
const anim2 = useAnimatedImageValue(require("./assets/anim2.gif"), isPaused);
return (
<Canvas style={{ width: 640, height: 180 }}>
<Image image={anim1} x={0} y={0} width={320} height={180} />
<Image image={anim2} x={320} y={0} width={320} height={180} />
</Canvas>
);
};
Conditional Playback
import { useSharedValue } from "react-native-reanimated";
import { useEffect } from "react";
const ConditionalPlayback = ({ isVisible }) => {
const isPaused = useSharedValue(!isVisible);
useEffect(() => {
isPaused.value = !isVisible;
}, [isVisible]);
const animation = useAnimatedImageValue(
require("./assets/animation.gif"),
isPaused
);
return (
<Canvas style={{ width: 320, height: 180 }}>
<Image image={animation} x={0} y={0} width={320} height={180} />
</Canvas>
);
};
Troubleshooting
Animation Not Playing
- Ensure the image loaded successfully (check if not
null)
- Verify the shared value for pausing is set correctly
- Check that the file is a valid animated format
- Reduce animation dimensions
- Decrease frame count
- Lower frame rate
- Consider using video instead
- Use smaller file sizes
Memory Issues
- Unload animations when not visible
- Use conditional rendering
- Limit number of simultaneous animations
- Monitor device memory usage
See Also