Persistence
The whole document round-trips as plain JSON. There’s no serialization
format to learn: getSnapshot() gives you an object, JSON.stringify it,
store it anywhere.
Save on change, restore on load
const { store } = board.editor
// restore (source 'remote' keeps the load out of the user's undo history)
const saved = localStorage.getItem('doc')
if (saved) {
store.loadSnapshot(JSON.parse(saved), 'remote')
board.editor.fitContent()
}
// save, debounced — snapshots are cheap but not free
let t
store.listen(() => {
clearTimeout(t)
t = setTimeout(() => {
localStorage.setItem('doc', JSON.stringify(store.getSnapshot()))
}, 400)
})In React the same pattern is two props:
<Quickdraw
snapshot={saved}
autoFit
onChange={(diff, source, editor) =>
debouncedSave(editor.store.getSnapshot())}
/>To a database
The snapshot is a JSON object — it drops into a jsonb column, a document
store, or an object bucket unchanged. A typical API round trip:
// save
await fetch(`/api/boards/${id}`, {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(store.getSnapshot()),
})
// load
const snap = await fetch(`/api/boards/${id}`).then((r) => r.json())
store.loadSnapshot(snap, 'remote')Incremental saves
For very large boards you can persist the diff stream instead of full
snapshots — every change is a small JSON diff, and replaying diffs over the
last snapshot reconstructs the document. See
The data model for the diff shape and
applyDiff/composeDiff.
Images
Pasted and dropped images are stored in the document as downscaled data
URLs, so a snapshot is always self-contained — no separate asset store to
keep consistent. The trade-off is snapshot size; if your users paste large
screenshots often, prefer database persistence over localStorage (which
caps around 5 MB in most browsers).