React Native
A native canvas on iOS and Android with react-native-svg, multi-touch, pen pressure and native text input, sharing the same engine as the web.
canvasflow/react-native renders the same editor with react-native-svg. Documents, shapes, tools, actions, history and sync are the very same code as on the web, so a board drawn on a phone opens unchanged in a browser.
Install#
npm install canvasflow react-native-svg
With Expo, prefer npx expo install react-native-svg so you get the version that matches your SDK.
A canvas#
import { CanvasFlowView } from 'canvasflow/react-native'
export default function App() {
return <CanvasFlowView style={{ flex: 1 }} onReady={(editor) => editor.setTool('draw')} />
}
CanvasFlowView takes every editor option plus:
| Prop | What it does |
|---|---|
editor |
Use an editor you created (for example, to set up sync first) |
themes |
{ light, dark } themes |
fonts |
The font family to use for each font token (hand, sans, serif, mono) |
pointerType |
Treat touches as a pen, for stylus-first apps |
onReady |
Called with the editor once it's mounted. Return a cleanup |
children |
Your native UI, rendered above the canvas. Hooks work inside |
Your own toolbar#
There's no default UI on native: you know your app's look better than any default could. The hooks are the same as on the web:
import { Pressable, Text, View } from 'react-native'
import { useEditor, useValue } from 'canvasflow/react-native'
const TOOLS = ['select', 'draw', 'rectangle', 'arrow', 'eraser']
export function Toolbar() {
const editor = useEditor()
const tool = useValue((e) => e.state.toolId)
return (
<View style={{ position: 'absolute', bottom: 32, alignSelf: 'center', flexDirection: 'row', gap: 6 }}>
{TOOLS.map((id) => (
<Pressable key={id} onPress={() => editor.setTool(id)} style={{ padding: 12, borderRadius: 12, backgroundColor: tool === id ? '#4fe0c0' : '#222' }}>
<Text style={{ color: tool === id ? '#04211b' : '#fff' }}>{id}</Text>
</Pressable>
))}
</View>
)
}
<CanvasFlowView style={{ flex: 1 }}>
<Toolbar />
</CanvasFlowView>
Touch and pen#
- One finger draws with the current tool. Two fingers pan and pinch to zoom.
- A long press opens the context menu event (
contextmenu), so you can show a native sheet. - Apple Pencil and other styluses report pressure, which shapes freehand strokes.
- In pen mode (
editor.updateState({ isPenMode: true })), fingers always pan and the pen always draws, so a resting palm never leaves a mark. It turns on by itself the first time a pen touches the screen.
Fonts#
React Native can't read CSS font stacks, so the view maps each font token to one installed family. Load your fonts with expo-font, then pass them in:
<CanvasFlowView style={{ flex: 1 }} fonts={{ hand: 'Virgil', sans: 'Inter', serif: 'Georgia', mono: 'Menlo' }} />
Saving and sync#
Everything from Saving and storage and Collaboration works on native. Use an AsyncStorage-backed adapter for storage, and the WebSocket transport (React Native has WebSocket built in):
import AsyncStorage from '@react-native-async-storage/async-storage'
import type { StorageAdapter } from 'canvasflow'
export const asyncStorage: StorageAdapter = {
get: (key) => AsyncStorage.getItem(key),
set: (key, value) => AsyncStorage.setItem(key, value),
remove: (key) => AsyncStorage.removeItem(key),
}
Sharing a web UI instead#
If you'd rather reuse a web interface on mobile (with Expo DOM components, say), render the web build inside it. CanvasFlow works the same there. Use the native view when you want native gestures and native performance; use the web build when you want one UI everywhere.