Add version history
Keep each saved DOCX as an immutable version and restore one without deleting newer work.
Continue from Load and save. You already export a DOCX and wait for your backend to store it. Version history changes one rule: each save creates a new snapshot instead of overwriting the previous file.
Keep every saved version
Suppose a document has two versions:
Version 1 → Version 2 (current)Restoring Version 1 should create Version 3 with the same document content. Version 2 remains available:
Version 1 → Version 2 → Version 3 (restored from Version 1)SuperDoc produces and opens the DOCX files. Your backend owns the version IDs, timestamps, authors, retention rules, and current-version pointer.
Add version endpoints
Extend the document endpoint from the Load and save guide. These are application routes, not SuperDoc routes:
| Route | Job |
|---|---|
GET /api/documents/sample-nda | Return the current DOCX |
POST /api/documents/sample-nda/versions | Store a new immutable version and make it current |
GET /api/documents/sample-nda/versions | List version metadata, newest first |
GET /api/documents/sample-nda/versions/:version | Return one immutable DOCX snapshot |
Record the creation time and author from the trusted backend. Do not accept either value from browser input.
Send the current version ID with each save. The backend must compare it with the current pointer in the same operation
that makes the new version current. Return 409 Conflict if another tab or user saved first. Validate every supplied
version ID against this document.
Save, list, and restore
The following helper uses the running Editor from the Load and save guide:
import { DOCX, type SuperDoc } from 'superdoc';
const documentEndpoint = '/api/documents/sample-nda';
const versionsEndpoint = `${documentEndpoint}/versions`;
export type DocumentVersion = {
id: string;
createdAt: string;
createdBy: string;
restoredFromVersionId?: string;
};
export async function listVersions(): Promise<DocumentVersion[]> {
const response = await fetch(versionsEndpoint);
if (!response.ok) throw new Error(`Could not list versions: ${response.status}`);
return response.json() as Promise<DocumentVersion[]>;
}
async function exportDocx(superdoc: SuperDoc): Promise<Blob> {
const docx = await superdoc.export({
exportType: ['docx'],
triggerDownload: false,
});
if (!(docx instanceof Blob)) throw new Error('Expected one DOCX file.');
return docx;
}
export async function saveVersion(
superdoc: SuperDoc,
baseVersionId: string,
restoredFromVersionId?: string,
): Promise<DocumentVersion> {
const docx = await exportDocx(superdoc);
const response = await fetch(versionsEndpoint, {
method: 'POST',
headers: {
'content-type': DOCX,
'x-base-version-id': baseVersionId,
...(restoredFromVersionId ? { 'x-restored-from-version-id': restoredFromVersionId } : {}),
},
body: docx,
});
if (response.status === 409) throw new Error('A newer version exists. Reload before saving.');
if (!response.ok) throw new Error(`Could not save the version: ${response.status}`);
return response.json() as Promise<DocumentVersion>;
}
async function openDocument(superdoc: SuperDoc, docx: Blob): Promise<void> {
const result = await superdoc.replaceFile(docx);
const state = result && typeof result === 'object' ? ((result as { state?: unknown }).state ?? null) : null;
if (state !== null && state !== 'review-ready' && state !== 'editing-ready') {
throw new Error('SuperDoc could not open the DOCX file.');
}
}
export async function restoreVersion(
superdoc: SuperDoc,
versionId: string,
baseVersionId: string,
): Promise<DocumentVersion> {
const activeDocx = await exportDocx(superdoc);
const response = await fetch(`${versionsEndpoint}/${encodeURIComponent(versionId)}`);
if (!response.ok) throw new Error(`Could not restore the version: ${response.status}`);
const docx = new Blob([await response.arrayBuffer()], { type: DOCX });
try {
await openDocument(superdoc, docx);
return await saveVersion(superdoc, baseVersionId, versionId);
} catch (error) {
const current = await fetch(documentEndpoint).catch(() => null);
const rollbackDocx = current?.ok ? new Blob([await current.arrayBuffer()], { type: DOCX }) : activeDocx;
await openDocument(superdoc, rollbackDocx);
throw error;
}
}
In Vanilla, pass the superdoc instance you created earlier. In React, get that same instance from
editorRef.current?.getInstance().
To restore a version, the helper opens its snapshot first and then saves it as a new version. Bytes the Editor cannot open never become current. If the save is rejected, it reloads the backend's current document. If that request fails, it restores the document that was open before the attempt.
Refresh the version list after every save or restore. Disable version actions while a request is running. The backend version check handles saves from other tabs and users.
Verify the history
Save Version 1, edit the NDA, and save Version 2. Restore Version 1. The backend should create Version 3, the Editor should show the Version 1 text, and Version 2 should remain in the list. Reload the page to confirm Version 3 is current.
Open a second tab before saving Version 2. A save from that stale tab should return 409 and leave Version 3 current.
Use Secure integration to protect the version routes. Use Export options when saved versions need a specific comments or tracked-changes policy.