content/docs/(plugins)/(functionality)/block-menu.mdx
Block Menu adds a right-click menu on top of block selection. BlockMenuPlugin owns the open state and pointer position; BlockSelectionPlugin decides which blocks the menu edits. The registry BlockContextMenu renders the menu actions.
openId and position.data-plate-open-context-menu.BlockMenuKit spreads BlockSelectionKit and renders BlockContextMenu above the editable.
import { createPlateEditor } from 'platejs/react';
import { BlockMenuKit } from '@/components/editor/plugins/block-menu-kit';
export const editor = createPlateEditor({
plugins: BlockMenuKit,
});
block-context-menu is the registry UI used by the kit.
| Surface | Owner | What It Does |
|---|---|---|
BlockMenuPlugin | @platejs/selection/react | Stores openId and pointer position, then exposes menu show/hide APIs. |
BlockSelectionPlugin | @platejs/selection/react | Selects the block under the context-menu event and applies actions to selected blocks. |
BlockSelectionKit | Registry | Enables context-menu selection and filters non-selectable blocks such as columns, code lines, and table cells. |
BlockMenuKit | Registry | Combines BlockSelectionKit with BlockMenuPlugin.render.aboveEditable. |
BlockContextMenu | Registry UI | Renders Radix context-menu items and calls block-selection transforms. |
block-menu-demo | Registry example | Shows the default menu in the full editor. |
block-menu-pro | Plus example | Adds drag-handle entry, nested filtering, colors, comments, and AI actions. |
The menu is UI state, not document state. The document stores block ids and node properties; it does not store whether a menu is open.
npm install @platejs/selection @platejs/ai
@platejs/ai is needed only when you keep the Ask AI item from the registry menu.
Use BlockSelectionKit when you want to keep the registry selection behavior but replace the menu wiring.
import { BlockMenuPlugin } from '@platejs/selection/react';
import { createPlateEditor } from 'platejs/react';
import { BlockSelectionKit } from '@/components/editor/plugins/block-selection-kit';
import { BlockContextMenu } from '@/components/ui/block-context-menu';
export const editor = createPlateEditor({
plugins: [
...BlockSelectionKit,
BlockMenuPlugin.configure({
render: { aboveEditable: BlockContextMenu },
}),
],
});
Use this lower-level shape only when you are replacing both registry kits.
import {
BlockMenuPlugin,
BlockSelectionPlugin,
} from '@platejs/selection/react';
import { createPlateEditor } from 'platejs/react';
import { BlockContextMenu } from '@/components/ui/block-context-menu';
export const editor = createPlateEditor({
plugins: [
BlockSelectionPlugin.configure({
options: {
enableContextMenu: true,
},
}),
BlockMenuPlugin.configure({
render: { aboveEditable: BlockContextMenu },
}),
],
});
| Case | Behavior |
|---|---|
| Right-click on a selectable block | Selects that block, then opens the context menu at the pointer position. |
| Shift + right-click | Adds the block to the current block selection. |
| Right-click inside a focused text selection | Leaves the browser context menu unless the block is already selected, void, or explicitly opted in. |
| Left-click while the menu is open | Prevents the click and hides the menu. |
| Touch device | Renders children without the context-menu wrapper. |
| Read-only editor | Prevents the context menu. |
Disable the Plate context menu for a specific surface with data-plate-open-context-menu={false}.
<PlateElement data-plate-open-context-menu={false} {...props}>
{children}
</PlateElement>
Force it open from a focused block with data-plate-open-context-menu="true" when the block should bypass the focused-selection guard.
BlockContextMenu acts on the current block selection.
| Action | Source |
|---|---|
| Ask AI | Opens AIChatPlugin after the menu closes. |
| Delete | Calls editor.getTransforms(BlockSelectionPlugin).blockSelection.removeNodes(). |
| Duplicate | Calls editor.getTransforms(BlockSelectionPlugin).blockSelection.duplicate(). |
| Turn into | Calls the registry setBlockType helper for paragraph, headings, blockquote, and code drawing. |
| Indent / Outdent | Calls blockSelection.setIndent(1) or blockSelection.setIndent(-1). |
| Align | Calls blockSelection.setNodes({ align }). |
The menu focuses block selection after close so keyboard selection remains active.
| API | Package | Use |
|---|---|---|
BLOCK_CONTEXT_MENU_ID | @platejs/selection/react | Built-in open id for the registry context menu. |
BlockMenuPlugin | @platejs/selection/react | Menu state plugin with openId and position options. |
api.blockMenu.hide() | @platejs/selection/react | Closes the menu and moves its stored position offscreen. |
api.blockMenu.show(id, position?) | @platejs/selection/react | Opens a menu by id and optionally sets pointer coordinates. |
api.blockMenu.showContextMenu(blockId, position) | @platejs/selection/react | Selects one block by id, then opens the context menu at the pointer coordinates. |
BlockSelectionPlugin.options.enableContextMenu | @platejs/selection/react | Enables block selection from right-click events. |
api.blockSelection.addOnContextMenu | @platejs/selection/react | Shared right-click handler used by selectable block node props. |
BlockContextMenu | Registry UI | Default context menu component used by BlockMenuKit. |