React Native WebGPU

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 conceptReact Native WebGPU
<canvas> in HTML<Canvas> view
canvas.getContext("webgpu")ref.current.getContext("webgpu")
Browser presents framesYou 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 only

present() 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 / clientHeight are the on-screen (layout) size in logical points. They follow the view's layout and update automatically when it resizes.
  • width / height are 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