Skip to content

Quick Start

npm install @woven-canvas/vue

Create a Vue component that renders the canvas:

<script setup lang="ts">
import { WovenCanvas } from "@woven-canvas/vue";
import "@woven-canvas/vue/style.css";
</script>
<template>
<WovenCanvas
:background="{ kind: 'dots' }"
style="width: 100%; height: 100vh"
/>
</template>

That’s all you need for a fully functional infinite canvas with:

  • Pan & Zoom — Scroll to pan, pinch or ctrl+scroll to zoom
  • Selection — Click to select, shift+click for multi-select, drag to box-select
  • Shapes — Rectangles, ellipses, triangles, and more
  • Text — Rich text with formatting options
  • Images — Drag and drop or paste from clipboard
  • Pen — Freehand drawing with pressure sensitivity
  • Arrows — Connect blocks with elbow arrows

Woven Canvas persistence is built to be local-first. All changes are saved instantly locally — your canvas works offline, survives page reloads, and syncs automatically when connectivity is restored.

<script setup lang="ts">
import { WovenCanvas } from "@woven-canvas/vue";
</script>
<template>
<WovenCanvas
:background="{ kind: 'dots' }"
:store="{
// Save to IndexedDB for offline persistence
persistence: {
documentId: 'my-canvas', // Unique ID for this document
},
}"
/>
</template>

Add real-time collaboration by connecting to a sync server.

<script setup lang="ts">
import { WovenCanvas } from "@woven-canvas/vue";
const clientId = crypto.randomUUID(); // Unique ID for this client
</script>
<template>
<WovenCanvas
:background="{ kind: 'dots' }"
:store="{
...
// Real-time sync with other users
websocket: {
url: 'wss://your-sync-server.com', // WebSocket server URL
documentId: 'shared-canvas',
clientId,
},
}"
/>
</template>

With this setup, users can work offline and their changes automatically merge when they reconnect. Woven Canvas uses @woven-ecs/canvas-store to handle syncing, see the Canvas Store docs for more details on configuring the server and customizing sync behavior.

Woven Canvas exposes Vue composables for reactive access to ECS state. Use these inside components rendered within <WovenCanvas>.

Track global state with useSingleton, or read a specific entity’s component with useComponent:

<script setup lang="ts">
import { watchEffect } from "vue";
import { useSingleton, useComponent } from "@woven-canvas/vue";
import { Camera, Block, type EntityId } from "@woven-canvas/core";
const props = defineProps<{ blockId: EntityId }>();
// Track global editor state
const camera = useSingleton(Camera);
// Track a specific block's data
const block = useComponent(() => props.blockId, Block);
watchEffect(() => {
console.log(`Zoom: ${camera.value.zoom}`);
console.log(`Block position: ${block.value?.position}`);
});
</script>

Query entities by their components with useQuery. The result updates automatically as entities are added, removed, or modified:

<script setup lang="ts">
import { watchEffect } from "vue";
import { useQuery } from "@woven-canvas/vue";
import { Block, Selected } from "@woven-canvas/core";
// Query all selected blocks
const selectedBlocks = useQuery([Block, Selected]);
watchEffect(() => {
console.log(`${selectedBlocks.value.length} blocks selected`);
});
</script>

Now that you have a working canvas, learn how it works:

Or jump straight to examples: