'use client';
import * as React from 'react';
import { Plate, usePlateEditor } from 'platejs/react';
import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { DEMO_VALUES } from './values/demo-values';
export default function Demo({ id }: { id: string }) {
  const editor = usePlateEditor({
    plugins: EditorKit,
    value: DEMO_VALUES[id],
  });
  return (
    <Plate editor={editor}>
      <EditorContainer variant="demo">
        <Editor />
      </EditorContainer>
    </Plate>
  );
}
Features
- Text Suggestions: Add suggestions as text marks with inline annotations
- Block Suggestions: Create suggestions for entire blocks of content
- State Tracking: Track suggestion state and user interactions
- Undo/Redo Support: Full undo/redo support for suggestion changes
- Discussion Integration: Works with discussion plugin for complete collaboration
Kit Usage
Installation
The fastest way to add suggestion functionality is with the SuggestionKit, which includes pre-configured SuggestionPlugin and related components along with their Plate UI components.
'use client';
 
import type { ExtendConfig, Path } from 'platejs';
 
import {
  type BaseSuggestionConfig,
  BaseSuggestionPlugin,
} from '@platejs/suggestion';
import { isSlateEditor, isSlateString } from 'platejs';
import { toTPlatePlugin } from 'platejs/react';
 
import {
  SuggestionLeaf,
  SuggestionLineBreak,
} from '@/components/ui/suggestion-node';
 
import { discussionPlugin } from './discussion-kit';
 
export type SuggestionConfig = ExtendConfig<
  BaseSuggestionConfig,
  {
    activeId: string | null;
    hoverId: string | null;
    uniquePathMap: Map<string, Path>;
  }
>;
 
export const suggestionPlugin = toTPlatePlugin<SuggestionConfig>(
  BaseSuggestionPlugin,
  ({ editor }) => ({
    options: {
      activeId: null,
      currentUserId: editor.getOption(discussionPlugin, 'currentUserId'),
      hoverId: null,
      uniquePathMap: new Map(),
    },
  })
).configure({
  handlers: {
    // unset active suggestion when clicking outside of suggestion
    onClick: ({ api, event, setOption, type }) => {
      let leaf = event.target as HTMLElement;
      let isSet = false;
 
      const isBlockLeaf = leaf.dataset.blockSuggestion === 'true';
 
      const unsetActiveSuggestion = () => {
        setOption('activeId', null);
        isSet = true;
      };
 
      if (!isSlateString(leaf) && !isBlockLeaf) {
        unsetActiveSuggestion();
      }
 
      while (leaf.parentElement && !isSlateEditor(leaf.parentElement)) {
        const isBlockSuggestion = leaf.dataset.blockSuggestion === 'true';
 
        if (leaf.classList.contains(`slate-${type}`) || isBlockSuggestion) {
          const suggestionEntry = api.suggestion!.node({
            isText: !isBlockSuggestion,
          });
 
          if (!suggestionEntry) {
            unsetActiveSuggestion();
 
            break;
          }
 
          const id = api.suggestion!.nodeId(suggestionEntry[0]);
          setOption('activeId', id ?? null);
 
          isSet = true;
 
          break;
        }
 
        leaf = leaf.parentElement;
      }
 
      if (!isSet) unsetActiveSuggestion();
    },
  },
  render: {
    belowNodes: SuggestionLineBreak as any,
    node: SuggestionLeaf,
  },
});
 
export const SuggestionKit = [suggestionPlugin];- SuggestionLeaf: Renders suggestion text marks
- BlockSuggestion: Renders block-level suggestions
- SuggestionLineBreak: Handles line breaks in suggestions
Add Kit
import { createPlateEditor } from 'platejs/react';
import { SuggestionKit } from '@/components/editor/plugins/suggestion-kit';
 
const editor = createPlateEditor({
  plugins: [
    // ...otherPlugins,
    ...SuggestionKit,
  ],
});Manual Usage
Installation
pnpm add @platejs/suggestion
Extend Suggestion Plugin
Create the suggestion plugin with extended configuration for state management:
import {
  type ExtendConfig,
  type Path,
  isSlateEditor,
  isSlateElement,
  isSlateString,
} from 'platejs';
import {
  type BaseSuggestionConfig,
  BaseSuggestionPlugin,
} from '@platejs/suggestion';
import { createPlatePlugin, toTPlatePlugin } from 'platejs/react';
import { BlockSuggestion } from '@/components/ui/block-suggestion';
import { SuggestionLeaf } from '@/components/ui/suggestion-node';
 
export type SuggestionConfig = ExtendConfig<
  BaseSuggestionConfig,
  {
    activeId: string | null;
    hoverId: string | null;
    uniquePathMap: Map<string, Path>;
  }
>;
 
export const suggestionPlugin = toTPlatePlugin<SuggestionConfig>(
  BaseSuggestionPlugin,
  ({ editor }) => ({
    options: {
      activeId: null,
      currentUserId: 'alice', // Set your current user ID
      hoverId: null,
      uniquePathMap: new Map(),
    },
    render: {
      node: SuggestionLeaf,
      belowRootNodes: ({ api, element }) => {
        if (!api.suggestion!.isBlockSuggestion(element)) {
          return null;
        }
 
        return <BlockSuggestion element={element} />;
      },
    },
  })
);- options.activeId: Currently active suggestion ID for visual highlighting
- options.currentUserId: ID of the current user creating suggestions
- options.hoverId: Currently hovered suggestion ID for hover effects
- options.uniquePathMap: Map tracking unique paths for suggestion resolution
- render.node: Assigns- SuggestionLeafto render suggestion text marks
- render.belowRootNodes: Renders- BlockSuggestionfor block-level suggestions
Add Click Handler
Add click handling to manage active suggestion state:
export const suggestionPlugin = toTPlatePlugin<SuggestionConfig>(
  BaseSuggestionPlugin,
  ({ editor }) => ({
    handlers: {
      // Unset active suggestion when clicking outside of suggestion
      onClick: ({ api, event, setOption, type }) => {
        let leaf = event.target as HTMLElement;
        let isSet = false;
 
        const unsetActiveSuggestion = () => {
          setOption('activeId', null);
          isSet = true;
        };
 
        if (!isSlateString(leaf)) unsetActiveSuggestion();
 
        while (
          leaf.parentElement &&
          !isSlateElement(leaf.parentElement) &&
          !isSlateEditor(leaf.parentElement)
        ) {
          if (leaf.classList.contains(`slate-${type}`)) {
            const suggestionEntry = api.suggestion!.node({ isText: true });
 
            if (!suggestionEntry) {
              unsetActiveSuggestion();
              break;
            }
 
            const id = api.suggestion!.nodeId(suggestionEntry[0]);
            setOption('activeId', id ?? null);
            isSet = true;
            break;
          }
 
          leaf = leaf.parentElement;
        }
 
        if (!isSet) unsetActiveSuggestion();
      },
    },
    // ... previous options and render
  })
);The click handler tracks which suggestion is currently active:
- Detects suggestion clicks: Traverses DOM to find suggestion elements
- Sets active state: Updates activeIdwhen clicking on suggestions
- Clears state: Unsets activeIdwhen clicking outside suggestions
- Visual feedback: Enables hover/active styling in suggestion components
Add Plugins
import { createPlateEditor, createPlatePlugin } from 'platejs/react';
import { SuggestionLineBreak } from '@/components/ui/suggestion-node';
 
const suggestionLineBreakPlugin = createPlatePlugin({
  key: 'suggestionLineBreak',
  render: { belowNodes: SuggestionLineBreak as any },
});
 
const editor = createPlateEditor({
  plugins: [
    // ...otherPlugins,
    suggestionPlugin,
    suggestionLineBreakPlugin,
  ],
});- render.belowNodes: Renders- SuggestionLineBreakbelow nodes to handle line break suggestions
Enable Suggestion Mode
Use the plugin's API to control suggestion mode:
import { useEditorRef, usePluginOption } from 'platejs/react';
 
function SuggestionToolbar() {
  const editor = useEditorRef();
  const isSuggesting = usePluginOption(suggestionPlugin, 'isSuggesting');
 
  const toggleSuggesting = () => {
    editor.setOption(suggestionPlugin, 'isSuggesting', !isSuggesting);
  };
 
  return (
    <button onClick={toggleSuggesting}>
      {isSuggesting ? 'Stop Suggesting' : 'Start Suggesting'}
    </button>
  );
}Add Toolbar Button
You can add SuggestionToolbarButton to your Toolbar to toggle suggestion mode in the editor.
Discussion Integration
The suggestion plugin works with the discussion plugin for complete collaboration:
const editor = createPlateEditor({
  plugins: [
    // ...otherPlugins,
    discussionPlugin,
    suggestionPlugin.configure({
      options: {
        currentUserId: 'alice',
      },
    }),
    suggestionLineBreakPlugin,
  ],
});Keyboard Shortcuts
| Key | Description | 
|---|---|
| Cmd + Shift + S | Add a suggestion on the selected text. | 
Plate Plus
- Full stack example for Suggestion and Comment
- Floating comments & suggestions UI with better user experience
- Comment rendered with Plate editor
- Discussion list in the sidebar
Plugins
SuggestionPlugin
Plugin for creating and managing text and block suggestions with state tracking and discussion integration.
API
api.suggestion.dataList
Gets suggestion data from a text node.
api.suggestion.isBlockSuggestion
Checks if a node is a block suggestion element.
api.suggestion.node
Gets a suggestion node entry.
api.suggestion.nodeId
Gets the ID of a suggestion from a node.
api.suggestion.nodes
Gets all suggestion node entries matching the options.
api.suggestion.suggestionData
Gets suggestion data from a node.
api.suggestion.withoutSuggestions
Temporarily disables suggestions while executing a function.
Types
TSuggestionText
Text nodes that can contain suggestions.
TSuggestionElement
Block elements that contain suggestion metadata.
TInlineSuggestionData
Data structure for inline text suggestions.
TSuggestionData
Data structure for block-level suggestions.
On This Page
FeaturesKit UsageInstallationAdd KitManual UsageInstallationExtend Suggestion PluginAdd Click HandlerAdd PluginsEnable Suggestion ModeAdd Toolbar ButtonDiscussion IntegrationKeyboard ShortcutsPlate PlusPluginsSuggestionPluginAPIapi.suggestion.dataListapi.suggestion.isBlockSuggestionapi.suggestion.nodeapi.suggestion.nodeIdapi.suggestion.nodesapi.suggestion.suggestionDataapi.suggestion.withoutSuggestionsTypesTSuggestionTextTSuggestionElementTInlineSuggestionDataTSuggestionData