Skip to main content
Technical preview

Redraw is currently in technical preview, available to wcandillon.dev subscribers. API is unstable.

React Native

react-native-redraw mirrors the react-redraw API on top of react-native-webgpu: the same <RedrawCanvas>, <RedrawProvider>, and useDevice(). It requires react-native-webgpu as a peer dependency.

As on the web, every canvas and hook needs a <RedrawProvider> above it (or an explicit device via the device prop) and throws a clear error otherwise. Wrap your app, or the part of it that draws, once:

// App.tsx
import { StyleSheet } from "react-native";
import { RedrawCanvas, RedrawProvider } from "react-native-redraw";

import { library, render } from "./hello-world";

export function App() {
return (
<RedrawProvider>
<Hello />
</RedrawProvider>
);
}

function Hello() {
return (
<RedrawCanvas style={styles.canvas} library={library} render={render} />
);
}

const styles = StyleSheet.create({
canvas: {
flex: 1,
},
});

No matter how many canvases live under the provider, the app holds a single GPU device. The provider accepts its device-request options through the options prop, for example options={{ timestampQuery: true }} or an existing device with options={{ device }} (which stays yours).

Nesting

As on the web, nesting <RedrawProvider> is idempotent. To create an isolated device, use <LocalRedrawProvider>, which always owns its device even when nested.

RedrawCanvas

The component works exactly like on the web: the library prop declares the drawing vocabulary (read once on mount), and render runs every frame with a fresh recorder. Coordinates are logical (dp) pixels; the canvas transform is pre-scaled by PixelRatio.get(), and FrameInfo carries width, height, time, frame, and dpr.

Same props as the web (library, render, loop, paused, device, onError, onReady, onStats), plus two native-specific ones:

  • transparent (default true): whether the canvas is transparent where nothing is drawn, letting any view behind it show through. Set it to false to composite the canvas as opaque, a minor perf win when the scene always fills the frame.
  • format (default "rgba8unorm"): pixel format of the render target, read once on mount. "rgba16float" renders at 16-bit float precision and presents on the platform's preferred high-bit-depth swapchain (rgba16float on iOS, rgb10a2unorm on Android), which removes the banding that 8-bit quantization produces on slow gradients. Colors match the 8-bit path exactly; only the precision changes. On Android the 10-bit format leaves only 2 bits of alpha, so pair it with transparent={false}.

Internally the pipeline renders into an offscreen storage texture and presents it through a blit pass, since the native swapchain formats are not storage-bindable.

Loading and error states

The provider initializes asynchronously. By default children render immediately and canvases start drawing once the device is ready. To show loading and unsupported states instead, pass fallback and errorFallback:

<RedrawProvider
fallback={<ActivityIndicator />}
errorFallback={(error) => <Text>WebGPU is not available</Text>}
>
<RedrawCanvas library={library} render={render} />
</RedrawProvider>

If the device is ever lost, the provider notifies onError, shows fallback again, and re-initializes with a fresh device; canvases resume automatically.

useDevice

useDevice() hands you the GPUDevice directly for imperative work on the same device, e.g. loading textures with makeTexture. It suspends while the provider initializes, so it composes with your own <Suspense> and error boundaries.

Bring your own device

Pass a device via the device prop, which overrides any surrounding provider. Ownership is simple: whoever creates the device owns it. A canvas only ever destroys its own resources; the provider destroys the device it created on unmount; a device you pass in stays yours.

Lifecycle callbacks

  • On the provider, onError fires when initialization fails or when the device is lost (a loss triggers automatic re-initialization).
  • On a canvas, onError fires when the device is lost or when rendering a frame throws; the animation loop stops first.
  • On a canvas, onReady receives the GPUDevice once it is ready, before the first frame. It is safe under React StrictMode.
  • On a canvas, onStats receives, after each frame, the frame's CPU time plus the per-pass GPU times. The GPU fields require options={{ timestampQuery: true }} on the provider and a device that supports the timestamp-query feature (note: the iOS Simulator does not); without it they are null and a one-time warning is logged.

Without an onError handler, errors are logged to the console.