Plate
PlateEditorsTemplates
GitHub16kGitHub
DiscordDiscord
    • Stream
    • Copilot
  • Comments
  • Discussion
  • Suggestions
    • Basic Blocks
      • Blockquote
      • Heading
      • Horizontal Rule
    • Callout
    • Code Block
    • Column
    • Date
    • Equation
    • Link
    • Media
    • MentionElement
    • Table
    • Table of Contents
    • Footnote
    • Details
  • Marks
    • Bold
    • Italic
    • Underline
    • Code
    • Highlight
    • Keyboard Input
    • Strikethrough
    • Subscript
    • Superscript
      • Font
      • Line Height
      • Text Align
    • Indent
    • List
      • Exit Break
      • Single Block
      • Trailing Block
    • Autoformat
    • Block Menu
    • Block Placeholder
    • Combobox
      • Emoji
      • MentionElement
      • Slash Command
    • Drag & Drop
    • Navigation Feedback
    • Tabbable
    • Toolbar
    • Yjs
    • Multi SelectEditor
    • CSV
    • DOCX
    • HTML
    • Markdown

Discussion

PreviousNext

Review each block's comments and suggestions in Floating Discussion.

CommentsSuggestions
Loading…
CommentsSuggestion

On This Page

OwnershipInstallationUsageReview modelFloating DiscussionChange commentsWithout CommentsRead-only and static renderingAPI ReferenceDiscussionKitDiscussionSlots
Build your editor
Production-ready AI template and reusable components.
Get all-access

Discussion is copied UI that opens a block's comments and authored changes in one anchored popover. The suggestion package supplies reusable change state and activation. Comments remains optional and owns its thread records.

Ownership

OwnerResponsibility
platejs/suggestion and platejs/suggestion/reactSupply suggestion modes, semantic change attributes, exact-view activation, and stable change hooks
platejs/authoredStore proposed edits, identities, dependencies, decisions, and retained content in the document
platejs/comments and platejs/comments/reactOwn comment records, actions, mapped ranges, decoration, and activation
Copied suggestion itemConfigure the package plugin's content attributes and compose SuggestionKit
Copied discussion itemJoin authored changes to optional comment threads, then render block triggers, cards, and Floating Discussion

The application pairs CommentsJSON with a document revision, provides users, and authorizes durable writes through initialState.mutate. Authored metadata stays in the document envelope. Comment threads contain semantic targets; current placement comes from attachment(id) and may refer to a stable authored change ID.

Installation

Install the combined view and the comment toolbar action. The registry command copies its Comment and Suggestion UI dependencies.

pnpm dlx shadcn@latest add https://platejs.org/r/discussion.json https://platejs.org/r/comment-toolbar-button.json
pnpm dlx shadcn@latest add https://platejs.org/r/discussion.json https://platejs.org/r/comment-toolbar-button.json

DiscussionKit installs Comments and the complete review surface. Add SuggestionKit to editors that review authored changes.

Usage

Pass the verified revision's CommentsJSON to CommentsPlugin. EditorKit includes the copied SuggestionKit; a smaller kit can install it explicitly.

components/editor/discussion-editor.tsx
'use client';
 
import type { EditorValueInput } from 'platejs';
import type { CommentsJSON, CommentUser } from 'platejs/comments';
import { CommentsPlugin } from 'platejs/comments/react';
import { EditorRoot, useCreateEditor } from 'platejs/react';
 
import { AllCommentsButton } from '@/components/editor/comment-toolbar-button';
import { DiscussionKit } from '@/components/editor/discussion';
import { Editor, EditorContainer } from '@/components/editor/editor';
import { EditorKit } from '@/components/editor/plugins';
import { Toolbar } from '@/components/editor/toolbar';
 
export function DiscussionEditor({
  initialValue,
  initialComments,
  users,
  currentUserId,
}: {
  initialValue: EditorValueInput;
  initialComments: CommentsJSON;
  users: Record<string, CommentUser>;
  currentUserId: string;
}) {
  const editor = useCreateEditor({
    plugins: [
      ...EditorKit,
      ...DiscussionKit,
      CommentsPlugin.configure({
        initialState: { initialComments, users, currentUserId },
      }),
    ],
    initialValue,
    userId: currentUserId,
  });
 
  return (
    <EditorRoot editor={editor}>
      <EditorContainer>
        <Toolbar>
          <AllCommentsButton />
        </Toolbar>
        <Editor />
      </EditorContainer>
    </EditorRoot>
  );
}
components/editor/discussion-editor.tsx
'use client';
 
import type { EditorValueInput } from 'platejs';
import type { CommentsJSON, CommentUser } from 'platejs/comments';
import { CommentsPlugin } from 'platejs/comments/react';
import { EditorRoot, useCreateEditor } from 'platejs/react';
 
import { AllCommentsButton } from '@/components/editor/comment-toolbar-button';
import { DiscussionKit } from '@/components/editor/discussion';
import { Editor, EditorContainer } from '@/components/editor/editor';
import { EditorKit } from '@/components/editor/plugins';
import { Toolbar } from '@/components/editor/toolbar';
 
export
































For a smaller editor composition, configure SuggestionPlugin from platejs/suggestion/react, or use SuggestionKit with its default styles. DiscussionSlots reads suggestion state from the package hooks; it does not require a registry-local semantic plugin.

Review model

Discussion groups unresolved records by block and sorts each block chronologically:

  • root comment threads appear as comment cards;
  • pending authored changes appear as review cards with Accept and Reject actions;
  • threads whose target is a change appear inside that review card;
  • resolving a thread removes its comment decoration;
  • accepting or rejecting a change hides its card and replies;
  • document undo reverses an accept or reject decision without changing messages or thread resolution;
  • resolving or reopening a thread stays outside document undo.

useSuggestionChanges(path) supplies native AuthoredChange records for each block. Discussion derives dates, labels, ranges, and card text in copied registry code, then joins change-targeted threads from Comments. Editing a comment body does not rebuild suggestion decorations or wake unrelated blocks.

Review cards call authored directly:

import { DefaultAuthoredPlugin } from 'platejs/authored';
 
const authored = editor.plugin(DefaultAuthoredPlugin);
const selection = authored.read.select({ ids: [changeId] });
const result = authored.update.decide({
  action: 'accept',
  selection,
});
import { DefaultAuthoredPlugin } from 'platejs/authored';
 
const authored = editor.plugin(DefaultAuthoredPlugin);
const selection = authored.read.select({ ids: [changeId] });
const result = authored.update.decide({
  action: 'accept',
  selection,
});

Use action: 'reject' to reject the change. Keep a card open for stale or blocked results so the reviewer can refresh the record or resolve its dependencies explicitly.

Floating Discussion

A block trigger opens every unresolved comment and pending suggestion currently attached to that block. Clicking inline comment text opens the records at that point. Clicking an authored-change marker activates that change in the mounted suggestion view. These live entry points open the same copied popover and cards.

When comment ranges overlap, clicking the shared text opens every thread at that point in source order. The selected group survives edits and source reordering until another activation or thread removal changes it. Clicking unmarked editor content clears the active review.

When attachment(id) reports unavailable, Discussion removes its inline decoration and block count. It does not remember the previous block or move the thread to nearby text. All comments keeps the conversation and original excerpt reachable independently of block placement. Live-session undo can restore exact mapped coverage without rewinding the conversation.

Resolved conversations also leave Floating Discussion and remain available through the Resolved filter in All comments. The dialog belongs to comment-toolbar-button; it works with Comments alone and locates authored-change targets only when Authored is installed in that editor view.

Change comments

Target an authored change ID to attach a conversation:

import { CommentsPlugin } from 'platejs/comments/react';
 
const comments = editor.plugin(CommentsPlugin).api;
const result = await comments.createThread({
  body,
  excerpt: 'insert suggestion',
  target: { id: changeId, type: 'change' },
});
 
if (result.status === 'applied') {
  comments.setActive([result.value]);
}
import { CommentsPlugin } from 'platejs/comments/react';
 
const comments = editor.plugin(CommentsPlugin).api;
const result = await comments.createThread({
  body,
  excerpt: 'insert suggestion',
  target: { id: changeId, type: 'change' },
});
 
if (result.status === 'applied') {
  comments.setActive([result.value]);
}

Preserve input for non-applied results and thrown errors. Replies use the change's location without allocating a comment anchor. Comment records retain their bodies and resolution when the change leaves the active projection. If undo or a document update restores that change ID, its unresolved replies appear again.

Without Comments

SuggestionPlugin does not depend on Comments. An editor can use suggestion modes, semantic decorations, active-change state, and useSuggestionChanges(path) without installing CommentsPlugin or DiscussionKit.

Add Discussion only when the product needs cards, replies, authors, dates, or popovers. Those presentation and thread-joining choices remain in copied registry code.

Read-only and static rendering

Pass initialComments to each fresh editor with its matching document value. Use CommentsPlugin for a read-only React editor and BaseCommentsPlugin with copied static comment styling for EditorStatic. Each editor binds its own attachments and suggestion view state; local undo starts empty.

Save editor.plugin(CommentsPlugin).api.toJSON() alongside editor.read.value() under the same revision. Observe conversation data through subscribeThreads and placement through subscribeAttachments. See Comments for loading, saving, historical target joins, and awaited actions, and Suggestions for modes and authored decisions.

API Reference

DiscussionKit

Install the copied kit in the editor plugin list. It includes CommentKit and the complete DiscussionSlots composition. Editors that review authored changes also install SuggestionKit or a configured SuggestionPlugin.

DiscussionSlots

For custom composition, pass the complete value to CommentsPlugin.configure({ slots: DiscussionSlots }). It owns the shared discussion index, block triggers, review cards, and Floating Discussion together.

function
DiscussionEditor
({
initialValue,
initialComments,
users,
currentUserId,
}: {
initialValue: EditorValueInput;
initialComments: CommentsJSON;
users: Record<string, CommentUser>;
currentUserId: string;
}) {
const editor = useCreateEditor({
plugins: [
...EditorKit,
...DiscussionKit,
CommentsPlugin.configure({
initialState: { initialComments, users, currentUserId },
}),
],
initialValue,
userId: currentUserId,
});
return (
<EditorRoot editor={editor}>
<EditorContainer>
<Toolbar>
<AllCommentsButton />
</Toolbar>
<Editor />
</EditorContainer>
</EditorRoot>
);
}