Skip to main content
Add Word-style commenting to documents with threaded discussions, replies, and resolution workflows.

Quick start

const superdoc = new SuperDoc({
  selector: "#editor",
  document: "contract.docx",
  user: {
    name: "John Smith",
    email: "john@company.com",
  },
  modules: {
    comments: {
      allowResolve: true,
      element: "#comments",
    },
  },
  onCommentsUpdate: ({ type, comment }) => {
    console.log("Comment event:", type);
  },
});
The comments module is enabled by default. To disable it entirely, set modules.comments to false:
modules: {
  comments: false;
}

Configuration

modules.comments.readOnly
boolean
default:"false"
View-only mode, prevents new comments
modules.comments.allowResolve
boolean
default:"true"
Allow marking comments as resolved
modules.comments.element
string | HTMLElement
Container for comments sidebar
modules.comments.useInternalExternalComments
boolean
default:"false"
Enable dual internal/external comment system
modules.comments.suppressInternalExternal
boolean
default:"false"
Hide internal comments from view
modules.comments.showResolved
boolean
default:"false"
Show resolved comments in the comments list
modules.comments.permissionResolver
function
Comments-only override for permission checks. See Permission Resolver.
modules.comments.highlightColors
Object
Custom colors for comment highlights.
modules.comments.highlightOpacity
Object
Opacity values for comment highlights (0–1).
modules.comments.highlightHoverColor
string
Hover highlight color for comment marks
modules.comments.trackChangeHighlightColors
Object
Colors for tracked change highlights.
modules.comments.trackChangeActiveHighlightColors
Object
Colors for the active tracked change highlight. Same properties as trackChangeHighlightColors. Defaults to trackChangeHighlightColors values when not set.

Viewing mode visibility

Comments are hidden by default when documentMode is viewing. Use the top-level comments.visible and trackChanges.visible flags to control what renders in read-only mode.
new SuperDoc({
  selector: "#viewer",
  document: "contract.docx",
  documentMode: "viewing",
  comments: { visible: true },      // Standard comment threads
  trackChanges: { visible: false }, // Tracked-change markup + threads
});

Setting up the comments UI

During initialization:
modules: {
  comments: {
    element: "#comments-sidebar";
  }
}
After initialization:
superdoc.on("ready", () => {
  superdoc.addCommentsList("#comments-sidebar");
});

Permission resolver

Customize who can resolve comments or accept tracked changes. The resolver receives the permission type, current user, and any tracked-change metadata. Return false to block the action.
modules: {
  comments: {
    permissionResolver: ({
      permission,
      trackedChange,
      currentUser,
      defaultDecision,
    }) => {
      if (
        permission === "RESOLVE_OTHER" &&
        trackedChange?.attrs?.authorEmail !== currentUser?.email
      ) {
        return false; // Block accepting suggestions from other authors
      }
      return defaultDecision;
    },
  },
}
You can set a global resolver with the top-level permissionResolver config. Module-level resolvers take precedence when both are defined.

Word import/export

Word comments are automatically imported with the document and marked with importedId. When exporting, use the commentsType option:
// Include comments in export
const blob = await superdoc.export({ commentsType: "external" });

// Remove all comments
const cleanBlob = await superdoc.export({ commentsType: "clean" });

API methods

These methods are available on the active editor’s commands:

addComment

Add a comment to the current text selection. Requires a text selection.
// Simple usage
superdoc.activeEditor.commands.addComment("Review this section");

// With options
superdoc.activeEditor.commands.addComment({
  content: "Please clarify this section",
  author: "John Smith",
  authorEmail: "john@company.com",
  isInternal: true,
});
contentOrOptions
string | Object
Comment content as a string, or an options object with:

addCommentReply

Add a reply to an existing comment or tracked change.
superdoc.activeEditor.commands.addCommentReply({
  parentId: "comment-123",
  content: "I agree with this suggestion",
});
options
Object
required

removeComment

superdoc.activeEditor.commands.removeComment({
  commentId: "comment-123",
});

setActiveComment

Highlight and focus a comment.
superdoc.activeEditor.commands.setActiveComment({
  commentId: "comment-123",
});

resolveComment

superdoc.activeEditor.commands.resolveComment({
  commentId: "comment-123",
});

setCommentInternal

Toggle a comment between internal and external visibility.
superdoc.activeEditor.commands.setCommentInternal({
  commentId: "comment-123",
  isInternal: true,
});

setCursorById

Navigate the cursor to a comment’s position in the document.
superdoc.activeEditor.commands.setCursorById("comment-123");

Events

onCommentsUpdate

Fired for all comment changes.
type
string
required
Event type: pending, add, update, deleted, resolved, selected, change-accepted, or change-rejected
comment
Comment
required
The Comment object
meta
Object
Additional metadata
onCommentsUpdate: ({ type, comment, meta }) => {
  switch(type) {
    case 'add':
      await saveComment(comment);
      break;
    case 'resolved':
      await markResolved(comment.id);
      break;
  }
}

Comment data structure

comment
Object