āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/udecode/plate/(plugins)/(functionality)/block-selection ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
title: Block Selection docs:
The Block Selection feature allows users to select and manipulate entire text blocks, as opposed to individual words or characters.
Cmd+A: Select all blocks.The fastest way to add Block Selection is with the BlockSelectionKit, which includes the pre-configured BlockSelectionPlugin and the BlockSelection UI component.
BlockSelection: Renders the selection rectangle around selected blocks.The BlockSelectionKit enables the context menu by default and provides a default isSelectable logic to exclude common non-selectable blocks like code lines and table cells.
import { createPlateEditor } from 'platejs/react';
import { BlockSelectionKit } from '@/components/editor/plugins/block-selection-kit';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
...BlockSelectionKit,
],
});
</Steps>
npm install @platejs/selection
import { BlockSelectionPlugin } from '@platejs/selection/react';
import { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
BlockSelectionPlugin,
],
});
Put this plugin before any other plugins overriding selectAll ā Cmd+A (code block, table, column, etc.) to avoid any conflicts.
You can control which blocks are selectable using options.isSelectable. This function receives an element and its path, and should return true if the block is selectable.
For example, to exclude code lines, columns, and table cells:
import { BlockSelectionPlugin } from '@platejs/selection/react';
BlockSelectionPlugin.configure({
options: {
isSelectable: (element, path) => {
if (['code_line', 'column', 'td'].includes(element.type)) {
return false;
}
// Exclude blocks inside table rows
if (editor.api.block({ above: true, at: path, match: { type: 'tr' } })) {
return false;
}
return true;
},
},
});
If your editor is inside a scrollable container, you may need to configure the selection area's boundaries and scroll speed.
id to your scroll container, e.g., id={editor.meta.uid}.position: relative on the container.areaOptions to configure the boundaries and scrolling behavior.BlockSelectionPlugin.configure({
options: {
areaOptions: {
boundaries: `#${editor.meta.uid}`,
container: `#${editor.meta.uid}`,
behaviour: {
scrolling: {
// Recommended speed, close to native
speedDivider: 0.8,
},
// Threshold to start selection area
startThreshold: 4,
},
},
},
});
You can enable block selection for elements outside the <Editor /> component by adding the data-plate-selectable attribute.
<Cover data-plate-selectable />
<Sidebar data-plate-selectable />
To prevent unselecting blocks when clicking on certain elements (e.g., a toolbar button), add the data-plate-prevent-unselect attribute.
<YourToolbarButton data-plate-prevent-unselect />
To reset the selection when clicking outside selectable areas, you can use a click handler or call the API directly:
// 1. Direct API call
editor.api.blockSelection.deselect();
// 2. Click outside handler
const handleClickOutside = (event: MouseEvent) => {
if (!(event.target as HTMLElement).closest('[data-plate-selectable]')) {
editor.api.blockSelection.deselect();
}
};
</Steps>
Style the selection area by targeting the .slate-selection-area class, which is added to the editor container.
/* Example using Tailwind CSS utility classes */
'[&_.slate-selection-area]:border [&_.slate-selection-area]:border-primary [&_.slate-selection-area]:bg-primary/10'
Use the useBlockSelected hook to determine if a block is selected. You can render a visual indicator, like the BlockSelection component, which is designed for this purpose.
Plate UI renders this component for all selectable blocks using render.belowRootNodes:
render: {
belowRootNodes: (props) => {
if (!props.className?.includes('slate-selectable')) return null;
return <BlockSelection />;
},
},
BlockSelectionPluginPlugin for block selection functionality.
<API name="BlockSelectionPlugin"> <APIOptions> <APIItem name="areaOptions" type="PartialSelectionOptions" optional> Options for the selection area. See [SelectionJS docs](https://github.com/Simonwep/selection-js) for all available options.{
boundaries: [`#${editor.meta.uid}`],
container: [`#${editor.meta.uid}`],
selectables: [`#${editor.meta.uid} .slate-selectable`],
selectionAreaClass: 'slate-selection-area',
}
</APIItem>
<APIItem name="enableContextMenu" type="boolean" optional>
Enables or disables the context menu for block selection.
- **Default:** `false`
</APIItem>
<APIItem name="isSelecting" type="boolean" optional>
Indicates whether block selection is currently active.
- **Default:** `false`
</APIItem>
<APIItem name="onKeyDownSelecting" type="(e: KeyboardEvent) => void" optional>
A function to handle the keydown event when selecting.
</APIItem>
<APIItem name="query" type="QueryNodeOptions" optional>
Options for querying nodes during block selection.
- **Default:** `{ maxLevel: 1 }`
</APIItem>
<APIItem name="selectedIds" type="Set<string>" optional>
A set of IDs for the currently selected blocks.
- **Default:** `new Set()`
</APIItem>
<APIItem name="anchorId" type="string | null" optional>
(Internal) The ID of the anchor block in the current selection. Used for shift-based selection.
- **Default:** `null`
</APIItem>
<APIItem name="isSelectable" type="(element: TElement, path: Path) => boolean" optional>
Function to determine if a block element is selectable.
- **Default:** `() => true`
</APIItem>
</APIOptions>
</API>
api.blockSelection.addAdds one or more blocks to the selection.
<API name="add"> <APIParameters> <APIItem name="id" type="string | string[]"> The ID(s) of the block(s) to be selected. </APIItem> </APIParameters> </API>api.blockSelection.clearResets the set of selected IDs to an empty set.
api.blockSelection.deleteRemoves one or more blocks from the selection.
<API name="delete"> <APIParameters> <APIItem name="id" type="string | string[]"> The ID(s) of the block(s) to remove from selection. </APIItem> </APIParameters> </API>api.blockSelection.deselectDeselects all blocks and sets the isSelecting flag to false.
api.blockSelection.focusFocuses the block selection shadow input. This input handles copy, delete, and paste events for selected blocks.
api.blockSelection.getNodesGets the selected blocks in the editor.
<API name="getNodes"> <APIParameters> <APIItem name="options" type="{ selectionFallback?: boolean }" optional> Options for getting nodes. </APIItem> </APIParameters> <APIOptions type="object"> <APIItem name="selectionFallback" type="boolean" optional> If true, and no blocks are selected by block selection, the method will use the editor's original selection to retrieve blocks. - **Default:** `false` </APIItem> </APIOptions> <APIReturns type="NodeEntry[]"> Array of selected block entries. </APIReturns> </API>api.blockSelection.hasChecks if one or more blocks are selected.
<API name="has"> <APIParameters> <APIItem name="id" type="string | string[]"> The ID(s) of the block(s) to check. </APIItem> </APIParameters> <APIReturns> <APIItem type="boolean">Whether the block(s) are selected.</APIItem> </APIReturns> </API>api.blockSelection.isSelectableChecks if a block at a given path is selectable based on the isSelectable plugin option.
api.blockSelection.moveSelectionMoves the selection up or down to the next selectable block.
When moving up:
api.blockSelection.selectAllSelects all selectable blocks in the editor.
api.blockSelection.setSets the selection to one or more blocks, clearing any existing selection.
<API name="set"> <APIParameters> <APIItem name="id" type="string | string[]"> The ID(s) of the block(s) to be selected. </APIItem> </APIParameters> </API>api.blockSelection.shiftSelectionExpands or shrinks the selection based on the anchor block.
For Shift+ArrowDown:
Shift+ArrowUp:Shift+ArrowUpShift+ArrowDowntf.blockSelection.duplicateDuplicates the selected blocks.
tf.blockSelection.removeNodesRemoves the selected nodes from the editor.
tf.blockSelection.selectSelects the nodes returned by getNodes() in the editor and resets selected IDs.
tf.blockSelection.setNodesSets properties on the selected nodes.
<API name="setNodes"> <APIParameters> <APIItem name="props" type="Partial<NodeProps<TElement>>"> Properties to set on selected nodes. </APIItem> <APIItem name="options" type="SetNodesOptions" optional> Options for setting nodes. </APIItem> </APIParameters> </API>tf.blockSelection.setTextsSets text properties on the selected nodes.
<API name="setTexts"> <APIParameters> <APIItem name="props" type="Partial<NodeProps<TText>>"> Text properties to set on selected nodes. </APIItem> <APIItem name="options" type="Omit<SetNodesOptions, 'at'>" optional> Options for setting text nodes, excluding the 'at' property. </APIItem> </APIParameters> </API>useBlockSelectableA hook that provides props for making a block element selectable, including context menu behavior.
<API name="useBlockSelectable"> <APIReturns type="object"> <APIItem name="props" type="object"> Props to be spread on the block element. <APISubList> <APISubListItem parent="props" name="className" type="string"> Required class for selection functionality. - **Default:** `'slate-selectable'` </APISubListItem> <APISubListItem parent="props" name="onContextMenu" type="(event: React.MouseEvent) => void" > Handles right-click context menu behavior: - Opens context menu for selected blocks - Opens for void elements - Opens for elements with `data-plate-open-context-menu="true"` - Adds block to selection with Shift key for multi-select </APISubListItem> </APISubList> </APIItem> </APIReturns> </API>useBlockSelecteduseBlockSelectionNodesuseBlockSelectionFragmentuseBlockSelectionFragmentPropuseSelectionAreaInitialize and manage selection area functionality.
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā