The fastest way to add discussion functionality is with the DiscussionKit, which includes the pre-configured discussionPlugin along with its Plate UI components.
'use client';
import { definePlatePlugin } from 'platejs/react';
import { BlockDiscussion } from '@/components/editor/block-discussion';
import type { TComment } from '@/components/editor/comment';
export type TDiscussion = {
id: string;
comments: TComment[];
createdAt: Date;
isResolved: boolean;
userId: string;
documentContent?: string;
};
export type DiscussionPluginState = {
currentUserId: string;
discussions: TDiscussion[];
users: Record<
string,
{ id: string; avatarUrl: string; name: string; hue?: number }
>;
};
const BLOCK_SUGGESTION_SELECTOR = '[data-block-suggestion="true"]';
const PLAYGROUND_DISCUSSION_CREATED_AT = 1_704_067_200_000;
const playgroundDiscussionDate = (offset = 0) =>
new Date(PLAYGROUND_DISCUSSION_CREATED_AT + offset);
const getTargetElement = (target: EventTarget | null) => {
if (target instanceof HTMLElement) return target;
if (target instanceof Node) return target.parentElement;
return null;
};
export const getDiscussionClickTarget = ({
selector,
target,
}: {
selector: string;
target: EventTarget | null;
}) => {
const element = getTargetElement(target);
if (!element) return null;
return element.closest(selector);
};
export const getDiscussionBlockClickTarget = ({
selector = BLOCK_SUGGESTION_SELECTOR,
target,
}: {
selector?: string;
target: EventTarget | null;
}) =>
getDiscussionClickTarget({
selector,
target,
});
const discussionsData: TDiscussion[] = [
{
id: 'discussion1',
comments: [
{
id: 'comment1',
contentRich: [
{
children: [
{
text: 'Comments are a great way to provide feedback and discuss changes.',
},
],
type: 'paragraph',
},
],
createdAt: playgroundDiscussionDate(-600_000),
discussionId: 'discussion1',
isEdited: false,
userId: 'charlie',
},
{
id: 'comment2',
contentRich: [
{
children: [
{
text: 'Agreed! The link to the docs makes it easy to learn more.',
},
],
type: 'paragraph',
},
],
createdAt: playgroundDiscussionDate(-500_000),
discussionId: 'discussion1',
isEdited: false,
userId: 'bob',
},
],
createdAt: playgroundDiscussionDate(),
documentContent: 'comments',
isResolved: false,
userId: 'charlie',
},
{
id: 'discussion2',
comments: [
{
id: 'comment1',
contentRich: [
{
children: [
{
text: 'Nice demonstration of overlapping annotations with both comments and suggestions!',
},
],
type: 'paragraph',
},
],
createdAt: playgroundDiscussionDate(-300_000),
discussionId: 'discussion2',
isEdited: false,
userId: 'bob',
},
{
id: 'comment2',
contentRich: [
{
children: [
{
text: 'This helps users understand how powerful the editor can be.',
},
],
type: 'paragraph',
},
],
createdAt: playgroundDiscussionDate(-200_000),
discussionId: 'discussion2',
isEdited: false,
userId: 'charlie',
},
],
createdAt: playgroundDiscussionDate(),
documentContent: 'overlapping',
isResolved: false,
userId: 'bob',
},
];
const avatarUrl = (seed: string) =>
`https://api.dicebear.com/9.x/glass/svg?seed=${seed}`;
const usersData: Record<
string,
{ id: string; avatarUrl: string; name: string; hue?: number }
> = {
alice: {
id: 'alice',
avatarUrl: avatarUrl('alice6'),
name: 'Alice',
},
bob: {
id: 'bob',
avatarUrl: avatarUrl('bob4'),
name: 'Bob',
},
charlie: {
id: 'charlie',
avatarUrl: avatarUrl('charlie2'),
name: 'Charlie',
},
};
const initialState: DiscussionPluginState = {
currentUserId: 'alice',
discussions: discussionsData,
users: usersData,
};
// This plugin is purely UI. It's only used to store the discussions and users data
export const discussionPlugin = definePlatePlugin('discussion', {
initialState,
selectors: {
currentUser: (state) => state.users[state.currentUserId],
user: (state, id: string) => state.users[id],
},
}).configure({
render: { aboveNodes: BlockDiscussion },
});
export const DiscussionKit = [discussionPlugin];'use client';
import { definePlatePlugin } from 'platejs/react';
import { BlockDiscussion } from '@/components/editor/block-discussion';
import type { TComment } from '@/components/editor/comment';
export type TDiscussion = {
id: string;
comments: TComment[];
createdAt: Date;
isResolved: boolean;
userId: string;
documentContent?: string;
};
export type DiscussionPluginState = {
BlockDiscussion: Renders discussion UI above nodesimport { createPlateEditor } from 'platejs/react';
import { DiscussionKit } from '@/components/editor/discussion';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
...DiscussionKit,
],
});import { createPlateEditor } from 'platejs/react';
import { DiscussionKit } from '@/components/editor/discussion';
const editor = createPlateEditor
import { definePlatePlugin } from 'platejs/react';
import { BlockDiscussion } from '@/components/editor/block-discussion';
export interface TDiscussion {
id: string;
comments: TComment[];
createdAt: Date;
isResolved: boolean;
userId: string;
documentContent?: string;
}
const usersData = {
alice: {
id: 'alice',
avatarUrl: 'https://api.dicebear.com/9.x/glass/svg?seed=alice6',
name: 'Alice',
},
import { definePlatePlugin } from 'platejs/react';
import { BlockDiscussion } from '@/components/editor/block-discussion';
export interface TDiscussion {
id: string;
comments: TComment[];
createdAt: Date;
isResolved: boolean;
userId: string;
documentContent?: string;
}
const usersData = {
alice: {
id: 'alice',
avatarUrl: 'https://api.dicebear.com/9.x/glass/svg?seed=alice6',
name: 'Alice'
initialState.currentUserId: ID of the current active userinitialState.discussions: Array of discussion data structuresinitialState.users: Object mapping user IDs to user datarender.aboveNodes: Renders BlockDiscussion above nodes for discussion UIselectors.currentUser: Gets the current user dataselectors.user: Gets user data by IDimport { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
discussionPlugin,
],
});import { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
discussionPlugin,
],
});Pure UI plugin for managing collaboration state including users and discussion data.
Gets the current user data.
Gets user data by ID.
Discussion data structure containing comments and metadata.
User information structure for collaboration.