Integration

Manage document files

Replace the active DOCX, export for download or persistence, and define explicit storage ownership.

SuperDoc edits the active document in the browser. Your application owns where the source comes from, when changes are persisted, and who may read the result.

import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

const fileInput = document.querySelector<HTMLInputElement>('#document-file');
const saveButton = document.querySelector<HTMLButtonElement>('#save-document');
const status = document.querySelector<HTMLOutputElement>('#document-status');
if (!fileInput || !saveButton || !status) throw new Error('The document-management controls are incomplete.');

let documentReady = false;
let requiresRecreation = false;
let superdoc: SuperDoc | null = null;

const setControlsBusy = () => {
  fileInput.disabled = true;
  saveButton.disabled = true;
};

const setControlsIdle = () => {
  fileInput.disabled = false;
  saveButton.disabled = !documentReady;
};

const showError = (error: unknown) => {
  status.value = error instanceof Error ? error.message : 'The document operation failed.';
};

const handleRuntimeError = (error: unknown) => {
  showError(error);
  if (documentReady) return;
  requiresRecreation = true;
  setControlsIdle();
};

const openDocument = (document: string | File) => {
  superdoc?.destroy();
  documentReady = false;
  requiresRecreation = false;
  setControlsBusy();
  status.value = 'Opening document…';

  try {
    superdoc = new SuperDoc({
      selector: '#editor',
      document,
      onReady: () => {
        documentReady = true;
        setControlsIdle();
        status.value = 'Document ready.';
      },
      onContentError: ({ error }) => {
        handleRuntimeError(error);
      },
      onException: ({ error }) => {
        handleRuntimeError(error);
      },
    });
  } catch (error) {
    superdoc = null;
    handleRuntimeError(error);
  }
};

const replaceDocument = async () => {
  const file = fileInput.files?.[0];
  if (!file) return;
  if (requiresRecreation) {
    openDocument(file);
    return;
  }

  setControlsBusy();
  try {
    status.value = 'Opening document…';
    if (!superdoc) throw new Error('SuperDoc is not initialized.');
    const result = await superdoc.replaceFile(file);
    const replacementResult = result && typeof result === 'object' ? result : null;
    const replacementState = replacementResult && 'state' in replacementResult ? replacementResult.state : null;
    if (replacementState && replacementState !== 'review-ready' && replacementState !== 'editing-ready') {
      if (replacementResult && 'mount' in replacementResult && replacementResult.mount === null) {
        documentReady = false;
        requiresRecreation = true;
      }
      showError(new Error('SuperDoc could not open the selected DOCX.'));
      return;
    }
    documentReady = true;
    status.value = 'Document ready.';
  } catch (error) {
    documentReady = false;
    requiresRecreation = true;
    showError(error);
  } finally {
    setControlsIdle();
  }
};

const uploadDocument = async () => {
  setControlsBusy();
  try {
    status.value = 'Preparing DOCX…';
    if (!superdoc) throw new Error('SuperDoc is not initialized.');
    const result = await superdoc.export({ exportType: ['docx'], triggerDownload: false });
    if (!(result instanceof Blob)) throw new Error('SuperDoc did not return a DOCX blob.');

    const body = new FormData();
    body.set('document', result, 'contract.docx');
    const response = await fetch('/api/documents/contract', { method: 'PUT', body });
    if (!response.ok) throw new Error(`Upload failed with ${response.status}.`);
    status.value = 'Saved.';
  } catch (error) {
    showError(error);
  } finally {
    setControlsIdle();
  }
};

fileInput.addEventListener('change', replaceDocument);
saveButton.addEventListener('click', uploadDocument);
openDocument('/contract.docx');

window.addEventListener('beforeunload', () => {
  fileInput.removeEventListener('change', replaceDocument);
  saveButton.removeEventListener('click', uploadDocument);
  superdoc?.destroy();
});

Choose a source deliberately

At construction, document accepts a URL, File, or Blob. A URL is fetched by the browser and must satisfy CORS, authentication, and Content Security Policy requirements. A local File or Blob stays in browser memory unless your application, collaboration provider, proofing provider, or other integration sends its contents elsewhere.

Use replaceFile() to swap the active file while preserving the mounted SuperDoc instance when the runtime supports it. Await completion before enabling dependent controls. For a full application route change or a different Editor configuration, destroying and creating a new instance may be clearer.

Export once for the intended destination

export() downloads by default. Set triggerDownload: false to receive the Blob for upload, storage, or another browser workflow. Choose comment and tracked-change export policy explicitly when producing a clean or final document.

An export proves that SuperDoc produced bytes, not that your backend stored them. Check the persistence response and update application save state only after the trusted service succeeds.

For the shorter end-to-end workflow, read Load and save documents. For server-side processing, use Agents & automation.

On this page