[]previous and next page

Choose your framework

CanvasFlow runs in any framework. Each one has a small binding with the same pieces, and anything else can use the web host or the custom element.

CanvasFlow is not tied to a framework. The editor, the renderer and the default UI are plain TypeScript and DOM. A binding is a thin layer that mounts all of that for you and hands the editor's state to your framework in its own way: hooks in React, refs in Vue, signals in Solid and Angular, stores in Svelte.

Every binding has the same four pieces, so what you learn in one carries over to the next:

Piece What it is
A canvas <CanvasFlow /> (or an action, or mountCanvasFlow) mounts the canvas and the default UI.
A headless canvas <CanvasFlowCanvas /> mounts only the canvas. It never references the default UI, so bundlers drop the toolbar, the panels and their CSS. Build your own chrome.
The editor useEditor() gives you the editor inside a canvas. Everything in The editor works.
Derived state useValue(selector) follows any value of the editor and updates only when it changes. useSelectedIds, useTool, useCamera, useCanUndo, useCanRedo, useTheme and useDirection are ready-made.

Pick yours#

Framework Import State comes as Peer
React and Next.js canvasflow/react Hooks react, react-dom
Vue canvasflow/vue Refs vue 3.3 or newer
Solid canvasflow/solid Signals solid-js 1.8 or newer
Svelte canvasflow/svelte Stores none
Angular canvasflow/angular Signals @angular/core 17 or newer
Preact canvasflow/preact Hooks preact 10
ShadowJS canvasflow/shadow Signals @shadow-js/core
React Native canvasflow/react-native Hooks react-native, react-native-svg
Web component canvasflow/dom Events and properties none
Plain JavaScript canvasflow/dom, canvasflow/ui The editor's own API none

Peers are optional and only matter for the entry you import. A Vue app never installs React, and the other way round.

What is the same everywhere#

Options are read once. The canvas mounts with the options you give it, the way a ref works. To start over with different options, remount it (in React and Preact that means changing key). Two options follow later changes: dir and readonly.

You get the editor when it is ready. Every binding has a way to receive the editor once the canvas has mounted (onMount, a mount event, the returned ref). Return a function from it and it runs as a cleanup when the canvas goes away.

Server rendering is safe. Importing a binding on a server touches no browser globals. The canvas itself is client-only: the server renders an empty box, and the canvas mounts in the browser.

Headless is its own component. <CanvasFlow ui={false} /> works, but it still references the default UI, so a bundler keeps it. <CanvasFlowCanvas /> does not, and the bundle size checks prove it for every binding.

A framework that is not listed#

Use the web host directly. Three calls give you a complete editor in any environment that has a DOM:

main.tsts
import { createCanvasFlow } from 'canvasflow/dom'
import { mountUI } from 'canvasflow/ui'

const app = createCanvasFlow(document.getElementById('board')!)
const ui = mountUI(app)

// Later, when your framework unmounts the element:
ui.destroy()
app.destroy()

app.editor is the editor, and editor.subscribe(fn) tells you when anything changed. That is all a binding does, with the framework's lifecycle around it. The web component wraps the same thing as a custom element, which every framework can render.