Handle lifecycle and events
Show loading and error states, track unsaved edits, save, and clean up the Editor.
Connect each Editor signal to one application state: loading, ready, unsaved, saved, or unmounted.
Follow the lifecycle
Select a stage. The preview shows what your application should do and the code that drives it.
new SuperDoc()Show a loading state
Keep document actions disabled while the DOCX opens.
const superdoc = new SuperDoc({ selector: '#editor', document: '/sample.docx',});Wire the same states
Replace the controls and mount point from the Editor quickstart:
<button id="save-docx" type="button" disabled>Save DOCX</button>
<output id="editor-status">Opening…</output>
<div id="editor"></div>Then connect them to /sample.docx. Replace /api/documents/42 with your save endpoint:
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';
function requireElement<ElementType extends Element>(selector: string) {
const element = document.querySelector<ElementType>(selector);
if (!element) throw new Error(`${selector} not found.`);
return element;
}
const status = requireElement<HTMLOutputElement>('#editor-status');
const saveButton = requireElement<HTMLButtonElement>('#save-docx');
let isReady = false;
let editRevision = 0;
function showLoadError(error: unknown) {
console.error('SuperDoc error', error);
if (isReady) return;
status.value = 'Could not open the document';
saveButton.disabled = true;
}
const superdoc = new SuperDoc({
selector: '#editor',
document: '/sample.docx',
onReady: () => {
isReady = true;
status.value = 'Ready';
saveButton.disabled = false;
},
onEditorUpdate: () => {
editRevision += 1;
status.value = 'Unsaved changes';
},
onContentError: ({ error }) => showLoadError(error),
onException: ({ error }) => showLoadError(error),
});
async function saveDocument() {
if (!isReady) return;
const savedRevision = editRevision;
saveButton.disabled = true;
status.value = 'Saving…';
try {
const file = await superdoc.export({
exportType: ['docx'],
triggerDownload: false,
});
if (!(file instanceof Blob)) throw new Error('Expected one DOCX file.');
const response = await fetch('/api/documents/42', {
method: 'PUT',
headers: {
'content-type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
},
body: file,
});
if (!response.ok) throw new Error(`Save failed with ${response.status}.`);
status.value = editRevision === savedRevision ? 'Saved' : 'Unsaved changes';
} catch (error) {
status.value = 'Save failed';
console.error('The document was not saved.', error);
} finally {
saveButton.disabled = false;
}
}
saveButton.addEventListener('click', saveDocument);
export function unmountEditor() {
saveButton.removeEventListener('click', saveDocument);
superdoc.destroy();
}
Call unmountEditor() from the route or component that owns the Editor. If a listener belongs to a temporary panel,
register it with on() when the panel opens and remove it with off() when the panel closes.
Check the flow
With your save endpoint running, reload the page. Save DOCX should stay disabled until the document opens. Make an edit, then save. The status should change from Unsaved changes to Saved only after the request succeeds.
Temporarily change /sample.docx to a missing URL. The page should show a load error and keep Save DOCX disabled.
Go deeper
- Load and save documents connects the Editor to your backend storage.
- Connect to a collaboration room adds the separate collaboration-ready boundary.
- Tune performance for large documents shows how to react to pagination without treating layout as an edit.