Skip to content

Persistence and recovery

Persist versioned snapshots and durable pending mutations without confusing session state with document state.
On this page
Load, mount, and queue
const loaded = validateWorkbookSnapshot(await (await fetch("/documents/products")).json());
if (!loaded.ok) throw new Error(`Invalid snapshot: ${loaded.errors.length} error(s)`);
documentId: "products",
});
saveButton.onclick = () => void sync.sendNext();
ABCD
1Notebook38$24.00
2Pen52$10.00
Result: restored Products snapshot, with no local edits queued
Snapshot / session stateImmediately after opening this example
Loaded documentproducts, version 7 (the example response's version)
Restored cellsA1:C2 contain the product inputs shown above. D1 and D2 retain =B1*C1 and =B2*C2, with bold currency formatting.
Outgoing queueEmpty: hydration creates no local change event or undo entry.
Next local editQueued until the host calls sync.sendNext(), here through the Save button.

This preview assumes /documents/products returns a valid snapshot at version 7 containing those cells and styles. It is not a built-in endpoint or a save acknowledgement: mounting a snapshot does not prove that a later edit has been persisted.

Try it live: Database showcase — edit cells, hold commits, and reload to inspect recovery. The showcase uses IndexedDB in your browser, not your server.

Full setup: use the host and initialization from Your first grid, then the complete HTTP adapter and load/mount flow. You supply adapter, the document endpoint, and saveButton; serverVersion must come from the loaded snapshot.

Sheetwrite supplies contracts, not a database or endpoint. A PersistenceAdapter loads a schema-versioned WorkbookSnapshot and commits immutable PendingCommit records carrying a stable clientMutationId and baseVersion.

What your host implements

PartYou provideSheetwrite provides
LoadPersistenceAdapter.load returns a validated WorkbookSnapshot with its version.createGridFromSnapshot, validateWorkbookSnapshot
SavePersistenceAdapter.commit appends one version in one database transaction.SyncCoordinator queues, sends, and acknowledges commits in order.
Large undorestoreBlock in your operation handler. Optional: PersistenceAdapter.commitBatch.The Grid encodes large restores and, if needed, splits them into atomic batches.
Offline workNothing, in the browser.IndexedDbPendingCommitStorage keeps pending commits across reloads.
ConflictsA recovery UX for conflicts that rebase cannot resolve.Conservative rebase and typed conflict events.

The collaboration guide has a complete HTTP adapter, the server table design, and the commit steps.

A commit response is applied, duplicate, or conflict. Applied and duplicate acknowledgements advance the server version. A conflict may provide ordered operations since the base or a current snapshot. Ambiguous structural or formula conflicts require host UX rather than silent merge.

IndexedDbPendingCommitStorage from @sheetwrite/core/browser can retain optimistic work across reloads. Its typed errors report blocked upgrades, unsupported schemas, quota, transaction, and abort failures. The core continues without durable browser storage when no pending-storage adapter is supplied.

Serializable documents

Use WorkbookSnapshot plus validateWorkbookSnapshot() at persistence boundaries. schemaVersion: 1 rejects unsupported future schemas with structured errors. DocumentOp is the exhaustive reducer operation union, including metadata and sheet lifecycle operations.

CallBehavior
createGridFromSnapshot(host, snapshot, options)Validates and hydrates every sheet before mounting. Hydration emits no change event or undo entry.
grid.exportSnapshot()Uses bulk sheet reads and returns deterministic sparse blocks.
grid.applyRemoteOperations(operations)Emits a change with source: "remote". It stays outside local undo history and outside SyncCoordinator's outgoing queue.
new SyncCoordinator(grid, adapter, options)Queues each non-empty local transaction as an immutable mutation record. Its serverVersion option is required and should come from the loaded snapshot.
sync.sendNext() / sync.retry(id)Host-controlled sends. Retries keep the original ID.
sync.subscribe(source)Accepts a transport-neutral callback source and validates strict version order.

Use MemoryPersistenceAdapter as an executable, server-sequenced reference, not as durable storage. Adapter methods accept AbortSignal. Transport failures use PersistenceError, while version conflicts are typed commit responses that keep local work.

Document state and session state

Document snapshots include workbook and sheet data, formula source, formatting, validation, protection metadata, notes, names, filters, and related document state.

Session state never belongs in a snapshot: selection, scroll position, caret state, search overlays and temporary highlights, host read-only policy, local zoom, and session-only grid options such as renderer and readOnly.

Large undo

Undo of a large clear can be larger than one transaction allows. Sheetwrite 0.5.0 sends such a restore as one compressed restoreBlock operation. Your server must accept that operation before it accepts 0.5.0 clients. When even the compressed restore needs more than one server version, the Grid uses an atomic batch, which needs commitBatch. Without commitBatch, only that undo is rejected; everything else works. See Large undo and atomic batches.

Edge cases and limits

  • A local commit costs the same at any offline queue depth. SyncCoordinator does not walk every pending commit to pick a base version or report its state.
  • Remote operations never enter the outgoing queue, so they are not sent back.
  • Version conflicts keep local work; recover them with host UX.

See also

Documentation