React Native Skia provides powerful snapshot capabilities that allow you to capture the rendered content of a Canvas component as an image. This is useful for saving drawings, sharing content, or creating thumbnails.
Making Snapshots
Use the makeImageSnapshot method on a canvas ref to capture the current frame:
import { useRef } from "react";
import { Button, View } from "react-native";
import { Canvas, Circle, useCanvasRef } from "@shopify/react-native-skia";
export default function SnapshotExample() {
const ref = useCanvasRef();
const captureSnapshot = () => {
if (ref.current) {
const snapshot = ref.current.makeImageSnapshot();
// snapshot is an SkImage
const base64 = snapshot.encodeToBase64();
console.log("Captured image:", base64);
}
};
return (
<View style={{ flex: 1 }}>
<Canvas ref={ref} style={{ flex: 1 }}>
<Circle cx={150} cy={150} r={100} color="blue" />
</Canvas>
<Button title="Capture" onPress={captureSnapshot} />
</View>
);
}
Canvas Ref Methods
makeImageSnapshot
(rect?: SkRect) => SkImage
Captures the current frame synchronously and returns an SkImage. Optionally specify a rectangle to capture only a portion of the canvas.
makeImageSnapshotAsync
(rect?: SkRect) => Promise<SkImage>
Asynchronously captures the current frame. Useful for capturing during animations or when rendering is in progress.
Forces the canvas to redraw
Returns the native ID of the canvas view
Partial Snapshots
Capture only a specific region:
import { Canvas, Circle, rect, useCanvasRef } from "@shopify/react-native-skia";
export default function PartialSnapshot() {
const ref = useCanvasRef();
const captureRegion = () => {
if (ref.current) {
// Capture only a 200x200 region starting at (50, 50)
const region = rect(50, 50, 200, 200);
const snapshot = ref.current.makeImageSnapshot(region);
const base64 = snapshot.encodeToBase64();
console.log("Partial snapshot:", base64);
}
};
return (
<Canvas ref={ref} style={{ width: 300, height: 300 }}>
<Circle cx={150} cy={150} r={100} color="red" />
</Canvas>
);
}
Async Snapshots
For capturing during animations:
import { useEffect } from "react";
import { Canvas, Circle, useCanvasRef, useValue } from "@shopify/react-native-skia";
import { useSharedValue, withRepeat, withTiming } from "react-native-reanimated";
export default function AsyncSnapshot() {
const ref = useCanvasRef();
const cx = useSharedValue(100);
useEffect(() => {
cx.value = withRepeat(withTiming(200, { duration: 1000 }), -1, true);
}, []);
const captureAsync = async () => {
if (ref.current) {
const snapshot = await ref.current.makeImageSnapshotAsync();
const base64 = snapshot.encodeToBase64();
console.log("Async snapshot:", base64);
}
};
return (
<Canvas ref={ref} style={{ flex: 1 }}>
<Circle cx={cx} cy={150} r={50} color="green" />
</Canvas>
);
}
Encoding Images
Once you have an SkImage from a snapshot, you can encode it in various formats:
Base64 Encoding
const snapshot = ref.current.makeImageSnapshot();
const base64 = snapshot.encodeToBase64(ImageFormat.PNG, 100);
// Use base64 string
Byte Array Encoding
import { ImageFormat } from "@shopify/react-native-skia";
const snapshot = ref.current.makeImageSnapshot();
const bytes = snapshot.encodeToBytes(ImageFormat.JPEG, 90);
// bytes is a Uint8Array
Saving to File
import RNFS from "react-native-fs";
import { ImageFormat } from "@shopify/react-native-skia";
const saveSnapshot = async () => {
if (ref.current) {
const snapshot = ref.current.makeImageSnapshot();
const base64 = snapshot.encodeToBase64(ImageFormat.PNG, 100);
const path = `${RNFS.DocumentDirectoryPath}/snapshot.png`;
await RNFS.writeFile(path, base64, "base64");
console.log("Saved to:", path);
}
};
PNG format - lossless compression, supports transparency
JPEG format - lossy compression, smaller file size, no transparency
WebP format - modern format with better compression
Sharing Images
Integrate with React Native Share:
import Share from "react-native-share";
import { ImageFormat } from "@shopify/react-native-skia";
const shareSnapshot = async () => {
if (ref.current) {
const snapshot = ref.current.makeImageSnapshot();
const base64 = snapshot.encodeToBase64(ImageFormat.PNG, 100);
await Share.open({
url: `data:image/png;base64,${base64}`,
title: "Share Snapshot",
});
}
};
Using Snapshots in Canvas
You can use captured snapshots as images in other canvases:
import { useState } from "react";
import { Canvas, Circle, Image, useCanvasRef } from "@shopify/react-native-skia";
export default function SnapshotAsImage() {
const sourceRef = useCanvasRef();
const [capturedImage, setCapturedImage] = useState(null);
const capture = () => {
if (sourceRef.current) {
const snapshot = sourceRef.current.makeImageSnapshot();
setCapturedImage(snapshot);
}
};
return (
<>
{/* Source canvas */}
<Canvas ref={sourceRef} style={{ width: 200, height: 200 }}>
<Circle cx={100} cy={100} r={80} color="purple" />
</Canvas>
{/* Display canvas */}
<Canvas style={{ width: 400, height: 400 }}>
{capturedImage && (
<Image
image={capturedImage}
x={0}
y={0}
width={400}
height={400}
fit="contain"
/>
)}
</Canvas>
</>
);
}
- Synchronous snapshots (
makeImageSnapshot) can block the UI thread for large canvases
- Use async snapshots when possible, especially during animations
- Snapshots create new image buffers in memory - dispose of them when no longer needed
- Consider reducing quality for JPEG snapshots to reduce memory usage
- Partial snapshots are more efficient than capturing the entire canvas
Common Use Cases
Signature Capture
const saveSignature = async () => {
const snapshot = canvasRef.current.makeImageSnapshot();
const base64 = snapshot.encodeToBase64(ImageFormat.PNG);
await uploadToServer(base64);
};
Screenshot for Support
const reportBug = async () => {
const snapshot = await canvasRef.current.makeImageSnapshotAsync();
const base64 = snapshot.encodeToBase64(ImageFormat.JPEG, 80);
await sendBugReport({ screenshot: base64 });
};
Create Thumbnails
const createThumbnail = () => {
const fullSnapshot = canvasRef.current.makeImageSnapshot();
// Scale down by drawing to a smaller surface
const thumbnail = scaleImage(fullSnapshot, 100, 100);
return thumbnail.encodeToBase64();
};