The Canvas component is the root container for all Skia drawing operations. It provides a surface where you can render shapes, images, text, and other graphical elements.
Basic Usage
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function App() {
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={100} cy={100} r={50} color="blue" />
</Canvas>
);
}
Props
React Native style object for positioning and sizing the canvas
mode
'default' | 'continuous'
default:"'default'"
Rendering mode:
default: Re-renders only when content changes
continuous: Continuously re-renders every frame (use for animations without Reanimated)
Enables debug mode which displays frame timing information in development
If true, the canvas background is opaque. Can improve performance by skipping alpha blending.
Reanimated shared value that will be updated with the canvas size. Useful for responsive layouts.
colorSpace
'srgb' | 'p3'
default:"'p3'"
Color space for rendering:
srgb: Standard RGB color space
p3: Display P3 color space (wider gamut on supported devices)
Reference to access canvas methods like makeImageSnapshot()
Android only. Pre-initializes Skia context for faster first render.
Canvas Ref
Access canvas methods using a ref:
import { useRef } from "react";
import { Canvas, Circle, useCanvasRef } from "@shopify/react-native-skia";
export default function App() {
const ref = useCanvasRef();
const captureImage = () => {
const snapshot = ref.current?.makeImageSnapshot();
if (snapshot) {
const base64 = snapshot.encodeToBase64();
console.log("Captured:", base64);
}
};
return (
<Canvas ref={ref} style={{ width: 300, height: 300 }}>
<Circle cx={150} cy={150} r={100} color="red" />
</Canvas>
);
}
Ref Methods
makeImageSnapshot
(rect?: SkRect) => SkImage
Synchronously captures the canvas content as an image. Optionally specify a rectangle to capture only a portion.const snapshot = ref.current.makeImageSnapshot();
const partial = ref.current.makeImageSnapshot(rect(0, 0, 100, 100));
makeImageSnapshotAsync
(rect?: SkRect) => Promise<SkImage>
Asynchronously captures the canvas content. Preferred for animations or when rendering is in progress.const snapshot = await ref.current.makeImageSnapshotAsync();
Forces the canvas to redraw immediately
Returns the native view ID of the canvas
measure
(callback: MeasureOnSuccessCallback) => void
Measures the canvas dimensions and positionref.current.measure((x, y, width, height, pageX, pageY) => {
console.log({ width, height });
});
measureInWindow
(callback: MeasureInWindowOnSuccessCallback) => void
Measures the canvas position relative to the window
Getting Canvas Size
Using onSize (Recommended)
Works on both old and new React Native architecture:
import { useSharedValue } from "react-native-reanimated";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function ResponsiveCanvas() {
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>
);
}
Using useCanvasSize Hook
import { Canvas, Circle, useCanvasSize } from "@shopify/react-native-skia";
export default function SizedCanvas() {
const { ref, size } = useCanvasSize();
return (
<Canvas ref={ref} style={{ flex: 1 }}>
{size.width > 0 && (
<Circle
cx={size.width / 2}
cy={size.height / 2}
r={50}
color="green"
/>
)}
</Canvas>
);
}
Rendering Modes
Default Mode
Re-renders only when props change (recommended for most use cases):
<Canvas style={{ flex: 1 }}>
<Circle cx={100} cy={100} r={50} color="blue" />
</Canvas>
Continuous Mode
Re-renders every frame (use for animations without Reanimated):
import { useState, useEffect } from "react";
import { Canvas, Circle } from "@shopify/react-native-skia";
export default function ContinuousAnimation() {
const [x, setX] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setX((prev) => (prev + 1) % 300);
}, 16);
return () => clearInterval(interval);
}, []);
return (
<Canvas mode="continuous" style={{ flex: 1 }}>
<Circle cx={x} cy={150} r={30} color="red" />
</Canvas>
);
}
Opaque Canvas
Improve performance when transparency is not needed:
<Canvas opaque style={{ flex: 1 }}>
{/* No transparency, faster rendering */}
<Rect x={0} y={0} width={300} height={300} color="white" />
</Canvas>
Android Warmup
Pre-initialize Skia on Android for faster first render:
<Canvas androidWarmup style={{ flex: 1 }}>
{/* First render will be faster */}
</Canvas>
Color Space
Choose appropriate color space for your content:
// For photos and rich graphics
<Canvas colorSpace="p3" style={{ flex: 1 }}>
{/* Wider color gamut on supported devices */}
</Canvas>
// For standard content
<Canvas colorSpace="srgb" style={{ flex: 1 }}>
{/* Standard sRGB colors */}
</Canvas>
Multiple Canvases
You can use multiple Canvas components in the same view:
import { View } from "react-native";
import { Canvas, Circle, Rect } from "@shopify/react-native-skia";
export default function MultiCanvas() {
return (
<View style={{ flex: 1 }}>
<Canvas style={{ flex: 1, backgroundColor: "#f0f0f0" }}>
<Circle cx={100} cy={100} r={50} color="red" />
</Canvas>
<Canvas style={{ flex: 1, backgroundColor: "#e0e0e0" }}>
<Rect x={50} y={50} width={100} height={100} color="blue" />
</Canvas>
</View>
);
}
Debug Mode
Enable debug information during development:
<Canvas debug style={{ flex: 1 }}>
{/* Shows frame timing and other debug info */}
<Circle cx={100} cy={100} r={50} color="purple" />
</Canvas>
Common Patterns
Responsive Design
import { useSharedValue } from "react-native-reanimated";
import { Canvas, Rect } from "@shopify/react-native-skia";
export default function ResponsiveRect() {
const size = useSharedValue({ width: 0, height: 0 });
return (
<Canvas style={{ flex: 1 }} onSize={size}>
<Rect
x={0}
y={0}
width={() => size.value.width}
height={() => size.value.height}
color="cyan"
/>
</Canvas>
);
}
Screenshot Functionality
import { Button, View } from "react-native";
import { Canvas, Circle, useCanvasRef } from "@shopify/react-native-skia";
export default function Screenshot() {
const ref = useCanvasRef();
const takeScreenshot = async () => {
const snapshot = await ref.current?.makeImageSnapshotAsync();
const base64 = snapshot?.encodeToBase64();
// Share or save the image
};
return (
<View style={{ flex: 1 }}>
<Canvas ref={ref} style={{ flex: 1 }}>
<Circle cx={150} cy={150} r={100} color="orange" />
</Canvas>
<Button title="Take Screenshot" onPress={takeScreenshot} />
</View>
);
}