Bundle size
How CanvasFlow keeps your bundle small, what each import costs, and how the budgets are enforced.
CanvasFlow is one package with several entry points and no side effects at module level. Your bundler keeps what you import and drops the rest, down to individual functions.
What each import costs#
Each row is a small app that imports only that, bundled against the published package and minified (before gzip):
| You import | Size |
|---|---|
Vec from canvasflow |
1.9 KB |
SyncDoc from canvasflow |
3.5 KB |
Convex functions from canvasflow/convex |
7.2 KB |
The WebSocket server from canvasflow/server |
7.8 KB |
CollabClient and a transport from canvasflow/collab |
15.7 KB |
createEditor from canvasflow |
173 KB |
createCanvasFlow from canvasflow/dom |
229 KB |
The headless canvas of any binding (<CanvasFlowCanvas/>, createFlowCanvas, mountCanvasFlowCanvas) |
230 KB |
canvasflow/dom with canvasflow/ui |
303 KB |
| The canvas with the default UI, in any binding | 304 to 305 KB |
The binding itself costs next to nothing: the headless canvas is the same size in React, Vue, Solid, Svelte, Angular, Preact and ShadowJS, within a kilobyte. The framework is a peer, so it is never counted in this table.
A full editor with every shape, tool, action, the renderer, input handling, text editing and the default UI is about 300 KB before compression, with zero dependencies behind it. Compressed with gzip it is a fraction of that: this site's own landing page measures it when it is built.
The guarantees#
- A web build never contains React Native.
canvasflow/react-nativeis its own entry, and only Metro reads itsreact-nativecondition. - The core, the DOM host, the UI, collaboration and Convex never import a framework. Each binding imports its own framework and nothing else: the Vue entry never imports React, and the React entry never imports Vue.
- A headless canvas doesn't pull in the UI.
<CanvasFlowCanvas/>and its siblings in every binding never referencecanvasflow/ui. (<CanvasFlow ui={false}/>mounts no UI, but still references it.) - A server pays for what it uses. Syncing on Convex costs the CRDT, not the editor.
How it's enforced#
The CanvasFlow repository runs a tree-shaking check on every change. It builds each of the apps above against the built package and fails when:
- an entry imports something it must never import (React Native in a web build, a framework in the core, React in the Vue entry, Node built-ins in browser code),
- a layer leaks into a bundle that shouldn't contain it (the UI in a headless build, the DOM host in a server build),
- any bundle grows past its budget.
Contributors keep it green with two habits: no top-level work at module scope, and /* @__PURE__ */ on any top-level call that builds a value. Both are written down in the package's AGENTS.md.
Tips for your app#
- Import from the narrowest entry:
canvasflowfor the editor and types,canvasflow/domfor the web,canvasflow/collabonly where you sync. - Load the canvas lazily on pages where it's below the fold (
import('canvasflow/dom')inside an effect), like the demos on this site do. - Type-only imports (
import type { Editor } from 'canvasflow') cost nothing.