Skip to main content
All configuration options, events, and hooks for real-time collaboration.

Collaboration Config

You manage the Yjs provider - works with SuperDoc Yjs, Liveblocks, Hocuspocus, etc.
import * as Y from "yjs";
import { LiveblocksYjsProvider } from "@liveblocks/yjs";

const ydoc = new Y.Doc();
const provider = new LiveblocksYjsProvider(room, ydoc);

new SuperDoc({
  selector: "#editor",
  modules: {
    collaboration: { ydoc, provider },
  },
});
modules.collaboration.ydoc
Y.Doc
required
Your Yjs document instance
modules.collaboration.provider
Object
required
Any Yjs-compatible provider

User Configuration

user
Object
required
Current user information for presence/awareness
user: {
  name: 'John Smith',
  email: '[email protected]',
  image: 'https://example.com/avatar.jpg'  // Optional
}
colors
string[]
Color palette for user cursors and selections
colors: ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"];
Users are automatically assigned colors from this palette.

Events

onReady

Fired when SuperDoc and collaboration are fully initialized.
new SuperDoc({
  // ...config
  onReady: ({ superdoc }) => {
    console.log("Ready!");

    // Access Yjs document
    const ydoc = superdoc.ydoc;

    // Access active editor
    const editor = superdoc.activeEditor;
  },
});

onAwarenessUpdate

Fired when users join, leave, or update their presence.
new SuperDoc({
  // ...config
  onAwarenessUpdate: ({ states, added, removed, superdoc }) => {
    // All connected users
    const users = states.filter((s) => s.user);

    // Users who just joined
    added.forEach((clientId) => {
      const user = states.find((s) => s.clientId === clientId);
      console.log("User joined:", user?.name);
    });

    // Users who just left
    removed.forEach((clientId) => {
      console.log("User left:", clientId);
    });
  },
});
User state properties:
PropertyTypeDescription
clientIdnumberUnique session ID
namestringUser’s display name
emailstringUser’s email
colorstringAssigned cursor color
cursorObjectCurrent cursor position
selectionObjectCurrent text selection

onEditorCreate

Fired when an editor instance is created.
new SuperDoc({
  // ...config
  onEditorCreate: ({ editor }) => {
    // Check if document is empty
    const isEmpty = !editor.state.doc.textContent.trim();

    if (isEmpty) {
      // Load default content
      await editor.replaceFile(defaultDocument);
    }
  }
});

onContentError

Fired when document content fails to load.
new SuperDoc({
  // ...config
  onContentError: ({ error, documentId }) => {
    console.error("Failed to load:", documentId, error);
    showErrorMessage("Document could not be loaded");
  },
});

Provider Events

When using the provider-agnostic API, listen to provider events directly:
// Sync status
provider.on("sync", (synced) => {
  if (synced) console.log("Document synced");
});

// For Hocuspocus/URL-based
provider.on("status", ({ status }) => {
  // 'connecting' | 'connected' | 'disconnected'
  updateConnectionIndicator(status);
});

provider.on("authenticationFailed", ({ reason }) => {
  if (reason === "token-expired") {
    refreshToken();
  }
});

Media Upload

Handle image uploads in collaborative documents:
new SuperDoc({
  // ...config
  handleImageUpload: (file) => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = (event) => {
        const dataUrl = event.target.result;
        const mediaPath = `word/media/${file.name}`;

        // Store in Yjs for sync
        if (superdoc.ydoc) {
          const mediaMap = superdoc.ydoc.getMap("media");
          mediaMap.set(mediaPath, dataUrl);
        }

        resolve(dataUrl);
      };

      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  },
});

SuperEditor Usage

For direct SuperEditor usage (without SuperDoc wrapper):
import { Editor } from "superdoc/super-editor";

const editor = new Editor({
  element: document.querySelector("#editor"),
  ydoc: ydoc,
  collaborationProvider: provider,
  user: { name: "John", email: "[email protected]" },
});
SuperEditor is the lower-level editor component. Most users should use SuperDoc, which provides document loading, toolbar, and additional features.

Complete Example

import { createClient } from "@liveblocks/client";
import { LiveblocksYjsProvider } from "@liveblocks/yjs";
import * as Y from "yjs";
import { SuperDoc } from "superdoc";

const client = createClient({ publicApiKey: "pk_..." });
const { room, leave } = client.enterRoom("my-document");
const ydoc = new Y.Doc();
const provider = new LiveblocksYjsProvider(room, ydoc);

let superdoc = null;

provider.on("sync", (synced) => {
  if (!synced) return;

  superdoc = new SuperDoc({
    selector: "#editor",
    documentMode: "editing",
    user: {
      name: "John Smith",
      email: "[email protected]",
    },
    colors: ["#FF6B6B", "#4ECDC4", "#45B7D1"],
    modules: {
      collaboration: { ydoc, provider },
    },
    onReady: ({ superdoc }) => {
      console.log("Collaboration ready");
    },
    onAwarenessUpdate: ({ states }) => {
      const users = states.filter((s) => s.user);
      renderUserList(users);
    },
    onContentError: ({ error }) => {
      console.error("Content error:", error);
    },
  });
});

// Cleanup
window.addEventListener("beforeunload", () => {
  superdoc?.destroy();
  provider.destroy();
  leave();
});