The fastest way to add exit break functionality is with the ExitBreakKit, which includes pre-configured ExitBreakPlugin with keyboard shortcuts.
'use client';
import { ExitBreakPlugin } from 'platejs';
export const ExitBreakKit = [
ExitBreakPlugin.configure({
shortcuts: {
insert: { keys: 'mod+enter' },
insertBefore: { keys: 'mod+shift+enter' },
},
}),
];'use client';
import { ExitBreakPlugin } from 'platejs';
export const ExitBreakKit = [
ExitBreakPlugin.configure({
shortcuts: {
insert: { keys: 'mod+enter' },
insertBefore: { keys: 'mod+shift+enter' },
},
}),
];import { createEditor } from 'platejs/react';
import { ExitBreakKit } from '@/components/editor/exit-break';
const editor = createEditor({
plugins: [
// ...otherPlugins,
...ExitBreakKit,
],
});import { createEditor } from 'platejs/react';
import { ExitBreakKit } from '@/components/editor/exit-break';
const editor =
import { ExitBreakPlugin } from 'platejs';
import { createEditor } from 'platejs/react';
const editor = createEditor({
plugins: [
// ...otherPlugins,
ExitBreakPlugin,
],
});import { ExitBreakPlugin } from 'platejs';
import { createEditor } from 'platejs/react';
const editor = createEditor({
plugins: [
// ...otherPlugins,
ExitBreakPlugin,
],
});import { ExitBreakPlugin } from 'platejs';
import { createEditor } from 'platejs/react';
const editor = createEditor({
plugins: [
// ...otherPlugins,
ExitBreakPlugin.configure({
shortcuts: {
insert: { keys: 'mod+enter' },
insertBefore: { keys: 'mod+shift+enter' },
},
}),
],
});
| Key | Description |
|---|---|
| Cmd + Enter | Exit the current block structure and insert a new block after. |
| Cmd + Shift + Enter | Exit the current block structure and insert a new block before. |
Provides shortcuts that leave the current block structure through tx.break.exit.
Exit break leaves the nearest structure around the caret. A code block, callout, image or divider is a structure of its own, so exit inserts the new block right after it, inside the same container. From any other block, exit leaves the nearest container that can take a paragraph beside it: a paragraph in a quote leaves the quote, and a paragraph in a column leaves the whole column group, because the group holds only columns. The new block is the default that the structure's parent declares.
When you trigger exit break:
ownsExit, such as a code block or a calloutExit passes over a block that owns its exit, and leaves the container instead, when its parent cannot put a plain text block beside it. That happens when the parent has no room or no default block, or when its default needs a property nothing supplies, holds no text, or owns its exit too. Below the top level, the walk also passes a container whose default would put the caret back into a container of the same type.
Once exit stops at a container, it never climbs further. When that container's parent declares no default block, has no room for another one, or has a default that needs a property nothing supplies or holds no text, nothing changes.
Code Block:
<codeBlock> // ← Exit point (top-level block)
code| // ← Starting position
</codeBlock>
<paragraph>|</paragraph> // ← New paragraph inserted here<codeBlock> // ← Exit point (top-level block)
code| // ← Starting position
</codeBlock>
<paragraph>|</paragraph> // ← New paragraph inserted hereCode Block in Column (stays in the column):
<columnGroup>
<column>
<codeBlock> // ← Exit point (owns its exit)
code| // ← Starting position
</codeBlock>
<paragraph>|</paragraph> // ← New paragraph inserted here
</column>
</columnGroup><columnGroup>
<column>
<codeBlock> // ← Exit point (owns its exit)
code| // ← Starting position
</codeBlock>
<paragraph>|</paragraph> // ← New paragraph inserted here
</column>
</columnGroup>Image or Divider in Column:
<columnGroup>
<column>
<image url="...">caption|</image> // ← Exit point and starting position
<paragraph>|</paragraph> // ← New paragraph inserted here
</column>
</columnGroup><columnGroup>
<column>
<image url="...">caption|</image> // ← Exit point and starting position
<paragraph>|</paragraph> // ← New paragraph inserted here
</column>
</columnGroup>Paragraph in Column (leaves the column group):
<columnGroup> // ← Exit point (top-level block)
<column> // group allows columns only
<paragraph>content|</paragraph> // ← Starting position
</column>
</columnGroup>
<paragraph>|</paragraph> // ← New paragraph inserted here<columnGroup> // ← Exit point (top-level block)
<column> // group allows columns only
<paragraph>content|</paragraph> // ← Starting position
</column>
</columnGroup>
<paragraph>|</paragraph> // ← New paragraph inserted hereTable in Column (exits at table level):
// First exit
<columnGroup>
<column>
<table> // ← Exit point (column allows blocks)
<tableRow> // table allows rows only
<tableCell> // row allows cells only
<paragraph>content|</paragraph>// ← Starting position
</tableCell>
</tableRow>
</table>
<paragraph>|</paragraph> // ← New paragraph inserted here
</column>
</columnGroup>
// Second exit
<columnGroup> // ← Exit point (top-level block)
<column> // group allows columns only
<table>
<tableRow>
<tableCell>
<paragraph>content</paragraph>
</tableCell>
</tableRow>
</table>
<paragraph>|</paragraph> // ← Starting position
</column>
</columnGroup>
<paragraph>|</paragraph> // ← New paragraph inserted here// First exit
<columnGroup>
<column>
<table> // ← Exit point (column allows blocks)
<tableRow> // table allows rows only
<tableCell> // row allows cells only
<paragraph>content|</paragraph>// ← Starting position
</tableCell>
</tableRow>
</table>
<paragraph>|</paragraph> // ← New paragraph inserted here
</column>
</columnGroup>
// Second exit
<columnGroup> // ← Exit point (top-level block)
<
Declare each custom element's allowed child content and its default. An exit passes ancestors whose parent cannot hold a paragraph, such as a cell in a row, and inserts the default that the landing parent declares:
import { definePlugin, schema } from 'platejs';
// Table structure
const CustomTablePlugin = definePlugin('table', {
schema: {
element: {
content: schema.content.type('tr', { min: 1 }),
},
},
});
const CustomTableRowPlugin = definePlugin('tr', {
schema: {
element: {
content: schema.content.types(['td', 'th'], {
default: { type: 'td' },
min: 1,
}),
},
},
});
const CustomTableCellPlugin = definePlugin('td', {
schema: {
element: {
content: schema.content.group('block', {
default: { type: 'paragraph' },
min: 1,
}),
},
},
});import { definePlugin, schema } from 'platejs';
// Table structure
const CustomTablePlugin = definePlugin('table', {
schema: {
element: {
content: schema.content.type('tr', { min: 1 }),
},
},
});
const CustomTableRowPlugin = definePlugin('tr', {
schema: {
element: {
content: schema.content.types(['td', 'th'], {
default: { type: 'td' },
min: 1,
}),
},
A text block that exit should leave before its container, such as a custom code-like block or a callout whose Enter rule exits, declares ownsExit. Without it, the block behaves like a paragraph and exit leaves its container:
import { definePlugin, schema } from 'platejs';
const NoticePlugin = definePlugin('notice', {
rules: { break: { emptyLineEnd: 'deleteExit' } },
schema: {
element: schema.element.textBlock({ ownsExit: true }),
},
});import { definePlugin, schema } from 'platejs';
const NoticePlugin = definePlugin('notice', {
rules: { break: { emptyLineEnd: 'deleteExit' } },
schema: {
element: schema.element.textBlock({ ownsExit: true }),
},
});Only a text block can declare ownsExit. Voids and objects always own their exit.