Vue
Use CanvasFlow in Vue 3 with a component, an event for the editor, and composables that return refs.
canvasflow/vue gives you <CanvasFlow>, <CanvasFlowCanvas> and composables that follow the editor as refs. It needs vue 3.3 or newer. Nothing else.
A board in one component#
<script setup lang="ts">
import { CanvasFlow } from 'canvasflow/vue'
</script>
<template>
<CanvasFlow persistence-key="board" style="height: 100vh" />
</template>
<CanvasFlow> mounts the canvas and the default UI into a div that fills its parent. Give it a height (here with style), because an editor needs room.
Props#
| Prop | What it does |
|---|---|
persistence-key |
Autosave under this key (IndexedDB by default). |
label |
The accessible name of the canvas. |
dir |
auto (the default), ltr or rtl. Follows later changes. See Right to left. |
readonly |
View only. Follows later changes. |
follow-system-theme |
Track the operating system's light and dark mode. |
ui |
false for no default UI, or an object that configures it (see Customizing the UI). |
options |
Everything else createCanvasFlow accepts: plugins, shapes, tools, storage, themes, assets. |
Options are read once, when the canvas mounts. To start over with different ones, change the component's key.
Get the editor#
The component emits mount with the editor, the app and the UI:
<script setup lang="ts">
import { CanvasFlow, type Editor } from 'canvasflow/vue'
function ready(editor: Editor) {
editor.zoomToFit()
}
</script>
<template>
<CanvasFlow persistence-key="board" style="height: 100vh" @mount="ready" />
</template>
Your own panels#
Children render inside the canvas once it is ready, so composables work in them. They are plain Vue: the values are refs.
<script setup lang="ts">
import { useSelectedIds, useValue } from 'canvasflow/vue'
const selected = useSelectedIds()
const shapes = useValue((editor) => editor.getCurrentPageShapes().length)
</script>
<template>
<div class="badge">{{ selected.length }} selected of {{ shapes }}</div>
</template>
<template>
<CanvasFlow style="height: 100vh">
<SelectionCount />
</CanvasFlow>
</template>
useValue(selector, isEqual?) re-reads on every change and updates the ref only when the result differs, by Object.is or the comparison you pass. It stops with the component. useEditorEvent('tool', handler) listens to an event for the component's lifetime. useEditor() returns the editor itself.
Headless#
<CanvasFlowCanvas> is the canvas without the default UI, and never references it, so your bundle does not carry the toolbar. Build your own with the editor:
<script setup lang="ts">
import { useEditor, useTool } from 'canvasflow/vue'
const editor = useEditor()
const tool = useTool()
const tools = ['select', 'draw', 'rectangle', 'arrow', 'text']
</script>
<template>
<div role="toolbar">
<button v-for="id in tools" :key="id" :aria-pressed="tool === id" @click="editor.setTool(id)">
{{ id }}
</button>
</div>
</template>
<template>
<CanvasFlowCanvas style="height: 100vh">
<Toolbar />
</CanvasFlowCanvas>
</template>
See Building your own UI for the full tour.
Nuxt and server rendering#
The component renders an empty div on the server and mounts the canvas in the browser, so it is safe in Nuxt. In Nuxt, wrap it in <ClientOnly> if you want the placeholder to be yours.