Trailing Block inserts a required block when the last node at a target level is missing or has the wrong type. The standard registry editor plugins include TrailingBlockPlugin so full Plate editors always end with a paragraph. Single-block and single-line editors disable it because they intentionally keep one root block.
Add TrailingBlockPlugin when users need a safe place to continue typing after blocks such as headings, tables, media, or columns.
import { TrailingBlockPlugin } from 'platejs';
import { createEditor } from 'platejs/react';
export const editor = createEditor({
plugins: [TrailingBlockPlugin],
});import { TrailingBlockPlugin } from 'platejs';
import { createEditor } from 'platejs/react';
export const editor = createEditor({
plugins: [TrailingBlockPlugin],
});TrailingBlockPlugin defaults to the editor's paragraph type.
| Layer | Owner | What It Does |
|---|---|---|
TrailingBlockPlugin | platejs | Owns the correction that checks and inserts the trailing block. |
tx.nodes.last([], { level }) | Plate transaction | Finds the last node at the configured depth. |
NodeApi.matches(...) | platejs | Applies the optional last-node match. |
editor.ts plugins | Registry | Add TrailingBlockPlugin after editing plugins. |
There is no dedicated trailing-block UI. The plugin is a normalizer.
Use type when the trailing block should be something other than the default paragraph.
import {
BaseParagraphPlugin,
ElementApi,
TrailingBlockPlugin,
} from 'platejs';
export const trailingBlockPlugin = TrailingBlockPlugin.configure(
({ editor }) => ({
initialState: {
type: editor.plugin(BaseParagraphPlugin).schema.type,
},
})
);import {
BaseParagraphPlugin,
ElementApi,
TrailingBlockPlugin,
} from 'platejs';
export const trailingBlockPlugin = TrailingBlockPlugin.configure(
({ editor }) => ({
initialState: {
type: editor.plugin(BaseParagraphPlugin).schema.type,
},
})
);The default is already editor.plugin(BaseParagraphPlugin).schema.type, so most editors can use the plugin directly.
Use match to limit insertion based on the current last block.
import { HeadingPlugin } from 'platejs/react';
import {
BaseHeadingPlugin,
BaseParagraphPlugin,
TrailingBlockPlugin,
} from 'platejs';
export const trailingBlockPlugins = [
HeadingPlugin,
TrailingBlockPlugin.configure(({ editor }) => ({
initialState: {
match: (node) =>
ElementApi.isElement(node) &&
node.type === editor.plugin(BaseHeadingPlugin).schema.type,
type: editor.plugin(BaseParagraphPlugin).schema.type,
},
})),
] as const;import { HeadingPlugin } from 'platejs/react';
import {
BaseHeadingPlugin,
BaseParagraphPlugin,
TrailingBlockPlugin,
} from 'platejs';
export const trailingBlockPlugins = [
HeadingPlugin,
TrailingBlockPlugin.configure(({ editor }) => ({
initialState: {
match: (node) =>
ElementApi.isElement(node) &&
node.type === editor.plugin(BaseHeadingPlugin).schema.type,
type: editor.plugin(BaseParagraphPlugin).schema.type,
},
})),
] asInstall the referenced H1 capability beside the configured plugin. A trailing
paragraph is then inserted when the last block is an H1. Without match, any
last block whose type differs from the trailing type can trigger insertion.
level changes where the plugin looks for the last node.
level | Target |
|---|---|
0 | Last root block. |
1 | Last child inside the last root-level container. |
TrailingBlockPlugin.configure({
initialState: {
level: 1,
type: 'paragraph',
},
});TrailingBlockPlugin.configure({
initialState: {
level: 1,
type: 'paragraph',
},
});Use nested levels when a constrained container must always end with a text block.
| Case | Result |
|---|---|
| Empty editor | Inserts a block at [0]. |
Last node already matches type | Falls through to the base normalizeNode. |
Last node has another type and matches initialState.match | Inserts the trailing block at PathApi.next(lastChildPath). |
Last node does not match initialState.match | Does not insert. |
The correction inserts { children: [{ text: '' }], type: trailingType }
through the active transaction.
| API | Package | Use |
|---|---|---|
TrailingBlockPlugin | platejs | Normalizer that ensures a trailing block exists. |
TrailingBlockPluginState.type | platejs | Block type to insert. Defaults to the editor paragraph type. |
TrailingBlockPluginState.level | platejs | Depth used by tx.nodes.last. Defaults to 0. |
TrailingBlockPluginState.insert | platejs | Custom wrapper around the generated insertion. |
TrailingBlockPluginState.match | platejs node match | Limits insertion to matching last nodes. |