Serialization
Export and import boards with the engine's JSON Canvas document API.
Save and load boards as JSON Canvas documents. Runtime snapshots are for rendering and tests; persisted documents go through engine.exportDocument() and engine.loadDocument().
What to persist
Persist the object returned by engine.exportDocument(). Serialize it when your storage requires a string. Do not persist
getState(), Vue refs, or DOM state.
| Data | Persisted by exportDocument() |
|---|---|
| JSON Canvas nodes | Yes |
| JSON Canvas edges | Yes, when connections are installed |
| Camera and grid settings | Yes, under x-lupinum-board |
| Selection | Yes, under x-lupinum-board |
| Z-order, lock state, visibility | Yes, under x-lupinum-board |
| Active pointer/text interaction | No |
| Runtime snap guides | No |
Quick export/import
The engine exports the canonical persisted document shape:
// Export
const document = engine.exportDocument()
localStorage.setItem('board', JSON.stringify(document))
// Import
const stored = localStorage.getItem('board')
if (stored) {
engine.loadDocument(JSON.parse(stored), { mode: 'replace' })
}loadDocument() validates and clones imported data before it changes the board.
To keep imports safe on the browser main thread, one document supports up to
10,000 nodes, 20,000 edges, 10,000 selected node IDs, 64 nested JSON levels,
250,000 JSON values, and 8 million string characters. Also limit the raw byte
size before calling JSON.parse() when documents come from an untrusted source.
Connections
const engine = createBoardEngine({
plugins: [connectionsPlugin()],
})
const document = engine.exportDocument()
engine.loadDocument(document, { mode: 'replace' })Install the same first-party plugins before importing documents that use them. When the connections plugin is installed, it owns edge export and import. Core validates the JSON Canvas document and passes plugin-owned fields to installed plugins; core does not persist hidden connection state by itself.
Node colors
Node colors are exported as top-level JSON Canvas node color fields:
{
"id": "card-1",
"type": "text",
"x": 80,
"y": 80,
"width": 260,
"height": 140,
"color": "5",
"text": "Draft release notes"
}Import accepts valid preset IDs and six-digit hex colors into BoardNode.color. Edge colors stay on JSON Canvas edge records owned by the connections package.
The x-lupinum-board extension
Board-specific metadata that has no JSON Canvas top-level field is stored under x-lupinum-board. Imports still accept the legacy x-vue-board key from 0.1 documents. Exports always use the new key.
Implementation detail
Document structure
{
"nodes": [...],
"edges": [...],
"x-lupinum-board": {
"camera": { "x": 0, "y": 0, "z": 1 },
"grid": { "size": 20, "majorEvery": 5, "snap": true, "pattern": "line" },
"nextZIndex": 5,
"nodes": {
"node-1": { "zIndex": 1, "locked": false, "visible": true },
"node-2": { "zIndex": 2, "parentId": "group-1" }
}
}
}Import modes
// Replace all state
engine.loadDocument(document, { mode: 'replace' })
// Merge into existing board
engine.loadDocument(document, { mode: 'merge' })| Mode | Use when |
|---|---|
'replace' | Loading a saved board document or resetting the whole scene. |
'merge' | Importing another document into the current board without clearing it. |
loadDocument() validates the document. Invalid node fields, invalid node colors,
missing edge endpoints, or unsupported plugin-owned edge data fail the import
instead of silently creating a partial board.