Skip to main content
SuperDoc provides @superdoc-dev/react - a first-party wrapper with proper lifecycle management, SSR safety, and React Strict Mode compatibility.

Installation

npm install @superdoc-dev/react
superdoc is included as a dependency - you don’t need to install it separately.

Quick Start

import { SuperDocEditor } from '@superdoc-dev/react';
import '@superdoc-dev/react/style.css';

function App() {
  return (
    <SuperDocEditor
      document={file}
      documentMode="editing"
      onReady={() => console.log('Editor ready!')}
    />
  );
}
That’s it! You now have a fully functional DOCX editor in your React app.

Core Concepts

The Component

<SuperDocEditor> handles everything for you:
  • Mounting - Creates a SuperDoc instance when the component mounts
  • Updates - Rebuilds automatically when the document prop changes
  • Cleanup - Properly destroys the instance on unmount
  • SSR-Safe - Renders container structure on server, initializes SuperDoc after hydration

Document Modes

SuperDoc supports three editing modes:
ModeDescriptionUse Case
editingFull editing capabilitiesDefault editing experience
viewingRead-only presentationDocument preview
suggestingTrack changes modeCollaborative review
<SuperDocEditor document={file} documentMode="editing" />
Changing documentMode via props is efficient - the component calls setDocumentMode() internally without rebuilding. You can also use getInstance()?.setDocumentMode() directly if preferred.

User Roles

Roles control what actions a user can perform:
RoleCan EditCan SuggestCan View
editorYesYesYes
suggesterNoYesYes
viewerNoNoYes
<SuperDocEditor document={file} role="editor" />

Working with Refs

For programmatic control, use a ref to access the SuperDoc instance:
import { useRef } from 'react';
import { SuperDocEditor } from '@superdoc-dev/react';
import '@superdoc-dev/react/style.css';

function App() {
  const editorRef = useRef(null);

  const handleExport = async () => {
    // Export as DOCX and trigger download
    await editorRef.current?.getInstance()?.export({ triggerDownload: true });
  };

  const handleModeSwitch = () => {
    // Switch mode without rebuilding
    editorRef.current?.getInstance()?.setDocumentMode('suggesting');
  };

  return (
    <>
      <button onClick={handleExport}>Download DOCX</button>
      <button onClick={handleModeSwitch}>Review Mode</button>
      <SuperDocEditor
        ref={editorRef}
        document={file}
        user={{ name: 'John', email: 'john@company.com' }}
        onReady={({ superdoc }) => console.log('Ready', superdoc)}
      />
    </>
  );
}

Ref API

The ref exposes a single method:
MethodReturnsDescription
getInstance()SuperDoc | nullAccess the underlying SuperDoc instance
getInstance() returns null before the editor is ready. Use optional chaining (?.) for safe access.

Available Instance Methods

Once you have the SuperDoc instance via getInstance(), you can call any SuperDoc method:
MethodReturnsDescription
setDocumentMode(mode)voidChange mode without rebuild
export(options?)Promise<Blob | void>Export document as DOCX
getHTML(options?)string[]Get document as HTML
focus()voidFocus the editor
search(text)SearchResult[]Search document content
goToSearchResult(match)voidNavigate to a search result
setLocked(locked)voidLock/unlock editing
toggleRuler()voidToggle ruler visibility
save()Promise<void[]>Save (in collaboration mode)

Common Patterns

File Upload

import { useState, useRef } from 'react';
import { SuperDocEditor } from '@superdoc-dev/react';
import '@superdoc-dev/react/style.css';

function FileEditor() {
  const [file, setFile] = useState(null);
  const editorRef = useRef(null);

  const handleFileChange = (e) => {
    const selected = e.target.files?.[0];
    if (selected) setFile(selected);
  };

  const handleExport = async () => {
    const blob = await editorRef.current?.getInstance()?.export({ triggerDownload: false });
    // Use blob for custom handling...
  };

  return (
    <div>
      <input type="file" accept=".docx" onChange={handleFileChange} />
      {file && (
        <>
          <button onClick={handleExport}>Export</button>
          <SuperDocEditor
            ref={editorRef}
            document={file}
            user={{ name: 'User', email: 'user@company.com' }}
          />
        </>
      )}
    </div>
  );
}

Loading State

Show a custom loading indicator while SuperDoc initializes:
<SuperDocEditor
  document={file}
  renderLoading={() => (
    <div className="loading-spinner">
      Loading document...
    </div>
  )}
  onReady={() => console.log('Ready!')}
/>

Document Switching

The editor automatically rebuilds when the document prop changes:
function MultiDocEditor() {
  const [currentDoc, setCurrentDoc] = useState(doc1);

  return (
    <div>
      <button onClick={() => setCurrentDoc(doc1)}>Document 1</button>
      <button onClick={() => setCurrentDoc(doc2)}>Document 2</button>
      <SuperDocEditor document={currentDoc} />
    </div>
  );
}

View-Only Mode

<SuperDocEditor
  document={file}
  documentMode="viewing"
  role="viewer"
  hideToolbar
/>

With User Information

<SuperDocEditor
  document={file}
  user={{
    name: 'John Doe',
    email: 'john@example.com',
    image: 'https://example.com/avatar.jpg'
  }}
  users={[
    { name: 'Jane Smith', email: 'jane@example.com' },
    { name: 'Bob Wilson', email: 'bob@example.com' },
  ]}
/>

TypeScript Support

The wrapper includes full TypeScript support:
import { useRef } from 'react';
import { SuperDocEditor } from '@superdoc-dev/react';
import type { SuperDocRef } from '@superdoc-dev/react';
import '@superdoc-dev/react/style.css';

interface EditorProps {
  document: string | File | Blob;
  userId: string;
}

function DocEditor({ document, userId }: EditorProps) {
  const editorRef = useRef<SuperDocRef>(null);

  const handleReady = ({ superdoc }: { superdoc: any }) => {
    console.log('SuperDoc ready');
  };

  const handleExport = async () => {
    const blob = await editorRef.current?.getInstance()?.export({
      triggerDownload: true
    });
    return blob;
  };

  return (
    <SuperDocEditor
      ref={editorRef}
      document={document}
      documentMode="editing"
      role="editor"
      user={{
        name: userId,
        email: `${userId}@company.com`
      }}
      onReady={handleReady}
    />
  );
}

Exported Types

import type {
  SuperDocEditorProps,
  SuperDocRef,
  DocumentMode,
  UserRole,
  SuperDocUser,
  SuperDocModules,
  SuperDocConfig,
  SuperDocInstance,
} from '@superdoc-dev/react';

Framework Integration

Next.js (App Router)

The wrapper handles SSR automatically. For additional control:
'use client';

import dynamic from 'next/dynamic';

const SuperDocEditor = dynamic(
  () => import('@superdoc-dev/react').then(mod => mod.SuperDocEditor),
  {
    ssr: false,
    loading: () => <div>Loading editor...</div>
  }
);

export default function EditorPage() {
  return <SuperDocEditor document="/api/document" />;
}

Next.js (Pages Router)

import dynamic from 'next/dynamic';

const SuperDocEditor = dynamic(
  () => import('@superdoc-dev/react').then(mod => mod.SuperDocEditor),
  { ssr: false }
);

export default function EditorPage() {
  return <SuperDocEditor document="/api/document" />;
}

Vite / Create React App

Works out of the box - just import and use:
import { SuperDocEditor } from '@superdoc-dev/react';
import '@superdoc-dev/react/style.css';

function App() {
  return <SuperDocEditor document={file} />;
}

Advanced Features

Real-time Collaboration

import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

function CollaborativeEditor() {
  const ydoc = useMemo(() => new Y.Doc(), []);
  const provider = useMemo(
    () => new WebsocketProvider('wss://your-server.com', 'doc-id', ydoc),
    [ydoc]
  );

  return (
    <SuperDocEditor
      document={file}
      modules={{
        collaboration: {
          ydoc,
          provider,
        },
      }}
    />
  );
}

AI Features

<SuperDocEditor
  document={file}
  modules={{
    ai: {
      apiKey: 'your-api-key',
      endpoint: 'https://api.example.com/ai',
    },
  }}
/>

Search and Navigate

const editorRef = useRef(null);

const handleSearch = (query) => {
  const instance = editorRef.current?.getInstance();
  const results = instance?.search(query);
  if (results?.length) {
    instance?.goToSearchResult(results[0]);
  }
};

Export to HTML

const editorRef = useRef(null);

const getHtmlContent = () => {
  const htmlArray = editorRef.current?.getInstance()?.getHTML();
  console.log(htmlArray); // Array of HTML strings per section
};

Props Reference

Document Props

PropTypeDefaultDescription
documentFile | Blob | string | objectrequiredDocument to load
documentMode'editing' | 'viewing' | 'suggesting''editing'Initial editing mode
role'editor' | 'viewer' | 'suggester''editor'User’s permission level

User Props

PropTypeDescription
user{ name, email?, image? }Current user info
usersArray<{ name, email, image? }>All users (for @-mentions)

UI Props

PropTypeDefaultDescription
idstringauto-generatedCustom container ID
hideToolbarbooleanfalseHide the toolbar
rulersboolean-Show/hide rulers (SuperDoc default)
classNamestring-CSS class for wrapper
styleCSSProperties-Inline styles
renderLoading() => ReactNode-Custom loading UI

Event Callbacks

PropTypeDescription
onReady({ superdoc }) => voidEditor initialized
onEditorCreate({ editor }) => voidProseMirror editor created
onEditorDestroy() => voidEditor destroyed
onEditorUpdate({ editor }) => voidContent changed
onContentError(event) => voidDocument parsing error
onException({ error }) => voidRuntime error

Advanced Props

PropTypeDescription
modulesobjectConfigure collaboration, AI, comments
All SuperDoc config options are available as props. The component extends SuperDocConfig, so any option from the core package can be passed directly.

Props That Trigger Rebuild

These props trigger a full instance rebuild when changed:
PropReason
documentNew document to load
userUser identity changed
usersUsers list changed
modulesModule configuration changed
rolePermission level changed
hideToolbarToolbar visibility changed
Other props like documentMode and callbacks are handled efficiently without rebuild.

Troubleshooting

”document is not defined” (SSR)

The component handles SSR internally, but if you still see errors:
// Use dynamic import in Next.js
const SuperDocEditor = dynamic(
  () => import('@superdoc-dev/react').then(mod => mod.SuperDocEditor),
  { ssr: false }
);

React Strict Mode Double-Mount

The component handles React 18 Strict Mode correctly. The internal cleanup flag prevents issues from double-invocation during development.

Document Not Loading

  1. Verify the file is a valid .docx document
  2. Check that document prop is a File, Blob, URL string, or config object
  3. Listen for onContentError events for parsing errors

Changing Document Mode

The component handles documentMode prop changes efficiently without rebuilding:
const [mode, setMode] = useState('editing');

// Just update state - no rebuild, no flicker
<button onClick={() => setMode('viewing')}>View</button>
<SuperDocEditor documentMode={mode} />
You can also use the imperative API if preferred:
editorRef.current?.getInstance()?.setDocumentMode('viewing');

Requirements

RequirementVersion
React16.8.0+
Node.js16+

Next Steps