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.