# Load and save a DOCX

> Load a DOCX from your application and save the edited file back to your backend.



Continue with `/sample.docx` from the [Quickstart](/editor/quickstart). Replace the local file with a backend round trip:

1. Fetch the DOCX from your API as a `Blob`.
2. Open and edit the `Blob` in SuperDoc.
3. Export the edited DOCX as a new `Blob` and send it back to your API.

SuperDoc handles the DOCX in the browser. Your application owns the API and storage.

## 1. Add a document endpoint [#1-add-a-document-endpoint]

The examples use `/api/documents/sample`. This is an application route, not a SuperDoc route.

| Request | Your endpoint must                                                          |
| ------- | --------------------------------------------------------------------------- |
| `GET`   | Return the current DOCX bytes                                               |
| `PUT`   | Store the request body and return a success status after the write finishes |

The Quickstart Vite project does not create this endpoint. Connect the example to your application API, or add a local
endpoint with this contract.

The endpoint can read from a database or an object store such as Amazon S3. The browser code stays the same. Keep
storage credentials and access checks in your backend.

## 2. Add a save action [#2-add-a-save-action]

For Vanilla, replace the Quickstart controls with a save action and status:

```html
<button id="save-docx" type="button" disabled>Save DOCX</button>
<output id="document-status">Opening…</output>
<div id="editor"></div>
```

The React example below renders its own controls.

## 3. Load and save the document [#3-load-and-save-the-document]

The examples show the complete storage flow. Add any user, mode, or viewing options you chose on the previous pages to
the Editor configuration.

**Vanilla — `src/main.ts`**

```ts
import { DOCX, 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 saveButton = requireElement<HTMLButtonElement>('#save-docx');
const status = requireElement<HTMLOutputElement>('#document-status');

const endpoint = '/api/documents/sample';
let isReady = false;
let editRevision = 0;
let superdoc: SuperDoc | undefined;

function showOpenError(error: unknown) {
  console.error('Could not open the document.', error);
  if (isReady) return;

  status.value = 'Could not open the document. Reload to try again.';
  saveButton.disabled = true;
}

try {
  const response = await fetch(endpoint);
  if (!response.ok) throw new Error(`Could not load the document: ${response.status}`);

  const docx = new Blob([await response.arrayBuffer()], { type: DOCX });
  superdoc = new SuperDoc({
    selector: '#editor',
    document: docx,
    onReady: () => {
      isReady = true;
      status.value = 'Ready';
      saveButton.disabled = false;
    },
    onEditorUpdate: () => {
      editRevision += 1;
      status.value = 'Unsaved changes';
    },
    onContentError: ({ error }) => showOpenError(error),
    onException: ({ error }) => showOpenError(error),
  });
} catch (error) {
  showOpenError(error);
}

saveButton.addEventListener('click', async () => {
  if (!superdoc || !isReady) return;

  const savedRevision = editRevision;
  saveButton.disabled = true;
  status.value = 'Saving…';

  try {
    const editedDocx = await superdoc.export({
      exportType: ['docx'],
      triggerDownload: false,
    });
    if (!(editedDocx instanceof Blob)) throw new Error('Expected one DOCX file.');

    const saveResponse = await fetch(endpoint, {
      method: 'PUT',
      headers: { 'content-type': DOCX },
      body: editedDocx,
    });
    if (!saveResponse.ok) throw new Error(`Could not save the document: ${saveResponse.status}`);
    status.value = editRevision === savedRevision ? 'Saved' : 'Unsaved changes';
  } catch (error) {
    status.value = 'Save failed. Try again.';
    console.error('The document was not saved.', error);
  } finally {
    saveButton.disabled = false;
  }
});

window.addEventListener('beforeunload', () => superdoc?.destroy());

```

**React — `src/App.tsx`**

```tsx
import { useEffect, useRef, useState } from 'react';
import { SuperDocEditor, type SuperDocRef } from '@superdoc/react';
import '@superdoc/react/style.css';

const endpoint = '/api/documents/sample';
const docxType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';

export default function App() {
  const editorRef = useRef<SuperDocRef>(null);
  const editRevisionRef = useRef(0);
  const savingRef = useRef(false);
  const [document, setDocument] = useState<Blob>();
  const [loadError, setLoadError] = useState(false);
  const [ready, setReady] = useState(false);
  const [saving, setSaving] = useState(false);
  const [saveStatus, setSaveStatus] = useState('');

  useEffect(() => {
    const controller = new AbortController();

    void fetch(endpoint, { signal: controller.signal })
      .then(async (response) => {
        if (!response.ok) throw new Error(`Could not load the document: ${response.status}`);
        setDocument(new Blob([await response.arrayBuffer()], { type: docxType }));
      })
      .catch((error: unknown) => {
        if (!controller.signal.aborted) {
          setLoadError(true);
          console.error(error);
        }
      });

    return () => controller.abort();
  }, []);

  function showOpenError(error: unknown) {
    console.error('Could not open the document.', error);
    setReady(false);
    setLoadError(true);
  }

  async function saveDocument() {
    if (savingRef.current) return;

    const superdoc = editorRef.current?.getInstance();
    if (!superdoc) return;

    const savedRevision = editRevisionRef.current;
    savingRef.current = true;
    setSaving(true);
    setSaveStatus('Saving…');
    try {
      const editedDocx = await superdoc.export({
        exportType: ['docx'],
        triggerDownload: false,
      });
      if (!(editedDocx instanceof Blob)) throw new Error('Expected one DOCX file.');

      const response = await fetch(endpoint, {
        method: 'PUT',
        headers: { 'content-type': docxType },
        body: editedDocx,
      });
      if (!response.ok) throw new Error(`Could not save the document: ${response.status}`);
      setSaveStatus(editRevisionRef.current === savedRevision ? 'Saved' : 'Unsaved changes');
    } catch (error) {
      setSaveStatus('Save failed. Try again.');
      console.error('The document was not saved.', error);
    } finally {
      savingRef.current = false;
      setSaving(false);
    }
  }

  if (loadError) return <p>Could not open the document. Reload to try again.</p>;
  if (!document) return <p>Opening document…</p>;

  return (
    <>
      <button disabled={!ready || saving} onClick={() => void saveDocument()} type='button'>
        Save DOCX
      </button>
      <output aria-live='polite'>{saveStatus}</output>
      <SuperDocEditor
        document={document}
        onContentError={({ error }) => showOpenError(error)}
        onEditorUpdate={() => {
          editRevisionRef.current += 1;
          setSaveStatus('Unsaved changes');
        }}
        onException={({ error }) => showOpenError(error)}
        onReady={() => setReady(true)}
        ref={editorRef}
      />
    </>
  );
}

```


Both examples:

* fetch the DOCX before mounting the Editor;
* enable saving after `onReady`;
* call `export({ triggerDownload: false })` to receive the edited `Blob`;
* show **Saved** only after the `PUT` succeeds. Edits made during that request remain unsaved.

## Use another source or destination [#use-another-source-or-destination]

| Task                           | Use                                                        |
| ------------------------------ | ---------------------------------------------------------- |
| Open a public or signed URL    | `document: url`                                            |
| Open a file selected by a user | `document: file`                                           |
| Switch the mounted Editor      | `await superdoc.replaceFile(nextDocx)`                     |
| Download instead of saving     | `await superdoc.export({ exportedName: 'sample-edited' })` |

A URL from another origin must allow the browser request through CORS. Fetch the file yourself and pass a `Blob` when
the request needs custom headers.

## Verify the round trip [#verify-the-round-trip]

Change the effective date from `September 1, 2026` to `October 1, 2026`. Select **Save DOCX**, then reload the page. The
new date should still be present. If the `PUT` fails, the status should show **Save failed** and the stored file should
remain unchanged.

## Continue from here [#continue-from-here]

* [Version history](/editor/version-history) keeps earlier saves so users can restore them.
* [Export options](/editor/export-options) changes the filename, comment policy, or related files.
* If your content starts as HTML or Markdown, see [rich content](/document-api/rich-content) and
  [output projections](/document-api/output-projections). These APIs do not open either format as a DOCX file.
