Skip to main content
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.
redraw
() => void
Forces the canvas to redraw
getNativeId
() => number
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);
  }
};

Image Formats

ImageFormat.PNG
number
PNG format - lossless compression, supports transparency
ImageFormat.JPEG
number
JPEG format - lossy compression, smaller file size, no transparency
ImageFormat.WEBP
number
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>
    </>
  );
}

Performance Considerations

  • 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();
};

Build docs developers (and LLMs) love