Canvas
Render your first pixels.
The Canvas component is where you can access a WebGPU context and render pixels.
The API is symmetric with its Web counterpart, with a few differences.
For instance on React Native you must call context.present() after every device.queue.submit() to present the frame to the screen.
On Android, the canvas is backed by a regular view by default. The Android rendering options explain the trade-offs and how to pick a faster one.
| Web concept | React Native WebGPU |
|---|---|
<canvas> in HTML | <Canvas> view |
canvas.getContext("webgpu") | ref.current.getContext("webgpu") |
| Browser presents frames | You call context.present() after submit |
Basic usage
Mount a Canvas, keep a ref, and read the WebGPU context once the view is laid out:
import { , } from "react";
import { StyleSheet, } from "react-native";
import { , type CanvasRef } from "react-native-webgpu";
export function () {
const = <CanvasRef>(null);
(() => {
const = .?.("webgpu");
if (!) return;
// request device, configure context, start render loop…
}, []);
return (
< ={{ : 1 }}>
< ={} ={StyleSheet.} />
</>
);
}getContext("webgpu") is synchronous, like on the web. If the ref is not ready yet (zero layout size), retry on the next frame with requestAnimationFrame.
Frame presentations
On the Web, the browser swaps your rendered frame to the screen automatically when you submit GPU commands. In React Native there is no automatic swap: presentation is a manual step. After you submit your commands to the queue, call present() on the context to display the frame.
..([.()]);
.(); // React Native onlypresent() is a React Native only method (it is not part of the Web WebGPU spec). It runs synchronously on the calling thread, so the frame is presented from whichever thread did the rendering. This works the same on every runtime: the main JS runtime, the UI thread, and dedicated worklet runtimes. Always call it after submit(), never before.
Your loop sets the frame rate.
A loop on the Reanimated UI runtime or on a worklet runtime (see Worklets) runs at the display's rate, 120 Hz on a ProMotion iPhone or iPad, while React Native's requestAnimationFrame on the JS thread runs at 60 Hz on iOS.
An app created from an Expo or React Native template before React Native 0.82 may still carry iOS's 60 Hz cap for iPhone: Reanimated's performance guide covers the Info.plist opt-in.
Pixel density
Just like on the Web, a canvas has two independent sizes:
clientWidth/clientHeightare the on-screen (layout) size in logical points. They follow the view's layout and update automatically when it resizes.width/heightare the drawing-buffer size in physical pixels. This is the resolution WebGPU actually renders at.
The drawing buffer is not scaled to the device pixel ratio by default, exactly as on the Web. To render crisply on high-density screens, set the buffer size to the layout size multiplied by the pixel ratio. PixelRatio.get() is the React Native equivalent of window.devicePixelRatio.
const = . as HTMLCanvasElement;
. = . * .();
. = . * .();Because clientWidth / clientHeight track layout, recompute the buffer size whenever they change (for example on rotation or a resize).
Transparency
To render the canvas over the React Native views beneath it, configure the context with alphaMode: "premultiplied" and clear with a color whose alpha is 0, so that WebGPU produces a transparent frame:
<Canvas ref={ref} style={StyleSheet.absoluteFill} />Platform note
On iOS alphaMode alone controls it. On Android the alphaMode passed to configure() is ignored: the backing view decides, and the default TextureView composites with alpha unless android={{ opaque: true }} asks it not to, while a SurfaceView is opaque unless asked otherwise. See the Android rendering options.
Color space
By default the canvas's values are sRGB. Pass colorSpace: "display-p3" to configure() to write Display P3 values instead, as you would in a browser. iOS tags the layer accordingly, so colors outside sRGB show on a wide-gamut display. Android ignores it for now.
context.configure({ device, format, colorSpace: "display-p3" });HDR
On an rgba16float canvas, values above 1.0 are clamped to the display's SDR range by default. Pass toneMapping: { mode: "extended" } to configure() to let them use the display's extended range (HDR) instead, as in a browser. iOS 16 and later opts the layer into extended dynamic range content. Android ignores it for now.
context.configure({
device,
format: "rgba16float",
toneMapping: { mode: "extended" },
});See also
- Canvas - the component and ref reference
- Native APIs - the React Native specific differences
- React APIs - hooks and the device provider