# 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 [#follow-the-lifecycle]

Select a stage. The preview shows what your application should do and the code that drives it.

> **Interactive model: the Editor lifecycle in your application**
>
> The preview moves `/sample.docx` through the application states that matter to a user.
>
> 1. **Mount — `new SuperDoc()`:** Show a loading state. Keep document actions disabled while the DOCX opens.
> 2. **Ready — `onReady`:** Enable document actions. The document is available. Enable Save or Export and run document queries.
> 3. **Edit — `onEditorUpdate`:** Mark the document unsaved. Update your dirty state after an edit. Debounce autosave work if you start it here.
> 4. **Save — `export() + fetch()`:** Wait for storage. Export produces DOCX bytes. Mark the document saved only after your backend accepts them.
> 5. **Unmount — `destroy()`:** Release the Editor. Call destroy() when the route or component that owns the Editor unmounts.
>
> **Load fails — `onContentError / onException`:** Show a useful error. Keep document actions disabled. Show a retry path instead of an empty mount point.


## Wire the same states [#wire-the-same-states]

Replace the controls and mount point from the [Editor quickstart](/editor/quickstart):

```html
<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:

```ts
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 [#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 [#go-deeper]

* [Load and save documents](/editor/load-and-save-documents) connects the Editor to your backend storage.
* [Connect to a collaboration room](/editor/collaboration) adds the separate collaboration-ready boundary.
* [Tune performance for large documents](/editor/performance-and-large-documents) shows how to react to pagination without treating layout as an edit.
