# Handle lifecycle and events

> Understand when the Editor is ready, what counts as an edit, and when to release it.



Your application needs to know when the document is usable, when it changes, and when to release the Editor.
Add those states to the [Quickstart](/editor/quickstart) before connecting storage or custom controls.

## Follow the lifecycle [#follow-the-lifecycle]

Select a stage to see its signal and application state. This is a simulated preview, not a live Editor.

> **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 Export and run document queries.
> 3. **Edit — `onEditorUpdate`:** Mark the document unsaved. Mark changes to document content as unsaved. Moving the cursor does not count as an edit.
> 4. **Export — `export()`:** Download a copy. Export downloads a DOCX. Changes remain unsaved in your application until your backend stores 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.


## Connect the signals [#connect-the-signals]

In the Vanilla Quickstart, replace the export button and mount point with this markup. Keep the script tag:

```html
<button id="export-docx" type="button" disabled>Export DOCX</button>
<output id="editor-status" aria-live="polite">Opening…</output>
<div id="editor"></div>
```

Replace `src/main.ts` with the example below. Keep the styles and `/sample.docx`; no backend is needed.
Copy any user or mode options you chose on the previous pages into this configuration.

```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 exportButton = requireElement<HTMLButtonElement>('#export-docx');

let isReady = false;
let hasUnsavedChanges = false;
let isExporting = false;
let isUnmounted = false;

function showLoadError(error: unknown) {
  console.error('SuperDoc error', error);
  if (isReady || isUnmounted) return;

  status.value = 'Could not open the document';
  exportButton.disabled = true;
}

const superdoc = new SuperDoc({
  selector: '#editor',
  document: '/sample.docx',
  onReady: () => {
    if (isUnmounted) return;
    isReady = true;
    status.value = 'Ready';
    exportButton.disabled = isExporting;
  },
  onEditorUpdate: () => {
    if (isUnmounted) return;
    hasUnsavedChanges = true;
    status.value = 'Unsaved changes';
  },
  onContentError: ({ error }) => showLoadError(error),
  onException: ({ error }) => showLoadError(error),
});

async function exportDocument(): Promise<void> {
  if (!isReady || isExporting || isUnmounted) return;

  isExporting = true;
  exportButton.disabled = true;
  try {
    await superdoc.export({ exportedName: 'sample-edited' });
    if (!isUnmounted) status.value = hasUnsavedChanges ? 'Unsaved changes' : 'Ready';
  } catch (error) {
    if (!isUnmounted) status.value = 'Export failed. Try again.';
    console.error('Could not export the document.', error);
  } finally {
    isExporting = false;
    if (!isUnmounted) exportButton.disabled = !isReady;
  }
}

exportButton.addEventListener('click', exportDocument);

export function unmountEditor(): void {
  isUnmounted = true;
  exportButton.disabled = true;
  exportButton.removeEventListener('click', exportDocument);
  superdoc.destroy();
}

```

`onReady` means the document is available for queries and export. `onEditorUpdate` reports document edits, not cursor
movement or pagination. Downloading a copy does not save changes to your application, so it does not clear the
**Unsaved changes** status.

## Release what you own [#release-what-you-own]

Call `unmountEditor()` when the owning route or component unmounts. It removes the button listener and calls
`destroy()`. The React Quickstart's `SuperDocEditor` component handles its own Editor cleanup.

For a temporary panel, use `on()` and `off()` with the same function. This fragment assumes the ready `superdoc`
instance from the example above:

```ts
import type { SuperDocZoomPayload } from 'superdoc';

const onZoom = ({ zoom }: SuperDocZoomPayload): void => {
  console.log('Zoom:', zoom);
};

superdoc.on('zoomChange', onZoom);
// When the panel closes:
superdoc.off('zoomChange', onZoom);
```

## Check the flow [#check-the-flow]

Reload the page. **Export DOCX** should stay disabled until the document opens. Edit the effective date, then export.
The browser should download `sample-edited.docx`, while the status remains **Unsaved changes**.

Temporarily change `/sample.docx` to a missing URL. The page should show a load error and keep **Export DOCX** disabled.

## Go deeper [#go-deeper]

* [Load and save documents](/editor/load-and-save-documents) connects the Editor to your backend storage.
* [Configure the Editor](/editor/configuration) lists all startup callbacks and their payload types.

## Export recent Interaction History [#export-recent-interaction-history]

The browser Editor keeps a bounded **Interaction History** in memory by default. When an edit looks wrong,
your application can request a snapshot without waiting for an exception:

```ts
const history = superdoc.diagnostics.getSnapshot();
const report = JSON.stringify(history);
// Let the customer review and share the report through your bug-report flow.
superdoc.diagnostics.clear();
```

The history records recent input attempts, commands, mutation receipts, selection changes, render progress,
and lifecycle failures. Collaboration sessions also record remote changes observed by this browser.
A snapshot is detached from the running recorder; capturing it does not query the worker or document.
You can also request one inside `onException`.

Configure retention when creating the Editor:

```ts
const superdoc = new SuperDoc({
  selector: '#editor',
  diagnostics: {
    history: {
      enabled: true,
      maxEvents: 500,
      maxBytes: 1_048_576,
      captureContent: false,
    },
  },
});
```

These are the defaults. The oldest events are removed when either limit is reached. `maxEvents` accepts
1–10,000; `maxBytes` accepts 1–16,777,216 and measures retained serialized UTF-16 payloads, excluding object
overhead. Invalid limits use the defaults. Individual events and nested payloads are also capped; snapshots
report eviction, truncation, and capture-failure counts. Set `enabled: false` to disable capture.

By default, the recorder selects metadata fields and excludes document text and error messages.
`captureContent: true` allows bounded text carried by observed events and API calls; treat those reports
as document content. Metadata can include document and transaction identifiers, so review reports before sharing.
This recorder does not upload or persist reports. Capture stops when the instance is destroyed; the history
belongs to that instance and does not survive a page reload. Capture failures are isolated from editor operations.

History is diagnostic evidence, not a complete replay or audit log. Selection events may be coalesced,
older events may be evicted, and each collaboration peer records its own observation order.
