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.
| Owner | Responsibility |
|---|---|
platejs/suggestion and platejs/suggestion/react | Supply suggestion modes, semantic change attributes, exact-view activation, and change and card hooks |
platejs/authored | Store proposed edits and their identities in the document, group them into review cards, and decide them |
platejs/comments and platejs/comments/react | Own comment records, actions, mapped ranges, decoration, and activation |
Copied suggestion item | Configure the package plugin's content attributes and compose SuggestionKit |
Copied discussion item | Join 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. Review marks stay on the document's nodes and review records in its envelope. Comment threads contain semantic targets; current placement comes from attachment(id) and may refer to a stable authored change ID.
Install the combined view and the comment toolbar action. The registry command copies its Comment and Suggestion UI dependencies.
DiscussionKit installs Comments and the complete review surface. Add SuggestionKit to editors that review authored changes.
Pass the verified revision's CommentsJSON to CommentsPlugin. EditorKit includes the copied SuggestionKit; a smaller kit can install it explicitly.
'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,
userId,
}: {
initialValue: EditorValueInput;
initialComments: CommentsJSON;
users: Record<string, CommentUser>;
userId: string;
}) {
const editor = useCreateEditor({
plugins: [
...EditorKit,
...DiscussionKit,
CommentsPlugin.configure({
initialState: { initialComments, users },
}),
],
initialValue,
userId,
});
return (
<EditorRoot editor={editor}>
<EditorContainer>
<Toolbar>
<AllCommentsButton />
</Toolbar>
<Editor />
</EditorContainer>
</EditorRoot>
);
}'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.
Discussion groups unresolved records by block and sorts each block chronologically:
useSuggestionGroups(blockKey) supplies the review cards touching each block. A card's body reads its changes a page at a time with read.changes({ group: card.id }). Discussion derives dates, labels, ranges, and card text in copied registry code, then joins change-targeted threads from Comments.
Review cards decide the card they show:
import { AuthoredPlugin } from 'platejs/authored';
const authored = editor.plugin(AuthoredPlugin);
const result = authored.update.decide({
action: 'accept',
groups: [card],
});import { AuthoredPlugin } from 'platejs/authored';
const authored = editor.plugin(AuthoredPlugin);
const result = authored.update.decide({
action: 'accept',
groups: [card],
});Use action: 'reject' to reject the card. Keep a card open for stale or blocked results so the reviewer can read it again or decide the related changes result.with names together with it.
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 its card 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.
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 is no longer pending. If undo or a document update restores that change ID, its unresolved replies appear again.
SuggestionPlugin does not depend on Comments. An editor can use suggestion modes, semantic decorations, active-card state, useSuggestionChanges(path), and useSuggestionGroups(blockKey) 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.
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.
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.
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.