Horizontal Rule adds a void horizontalRule block for separating sections. The package owns the element type, HTML parser, default <hr> render tag, and Markdown-style input rules. The registry kit adds the Plate UI divider component, static renderer, and insert toolbar item.
BasicBlocksKit installs HorizontalRulePlugin with the registry HrElement and both default input rules.
'use client';
import { BlockquoteRules, HeadingRules, HorizontalRuleRules } from 'platejs';
import {
BlockquotePlugin,
HeadingPlugin,
HorizontalRulePlugin,
ParagraphPlugin,
} from 'platejs/react';
import { BlockquoteElement } from '@/components/editor/blockquote';
import { HeadingElement } from '@/components/editor/heading';
import { HrElement } from '@/components/editor/horizontal-rule';
import { ParagraphElement } from '@/components/editor/paragraph';
export const BasicBlocksKit = [
ParagraphPlugin.configure({ component: ParagraphElement }),
HeadingPlugin.configure({
component: HeadingElement,
inputRules: [HeadingRules.markdown()],
rules: {
break: { empty: 'reset' },
},
shortcuts: {
toggleHeading1: {
handler: ({ editor }) => {
editor.plugin(HeadingPlugin).update.toggle({ level: 1 });
},
keys: 'mod+alt+1',
},
toggleHeading2: {
handler: ({ editor }) => {
editor.plugin(HeadingPlugin).update.toggle({ level: 2 });
},
keys: 'mod+alt+2',
},
toggleHeading3: {
handler: ({ editor }) => {
editor.plugin(HeadingPlugin).update.toggle({ level: 3 });
},
keys: 'mod+alt+3',
},
toggleHeading4: {
handler: ({ editor }) => {
editor.plugin(HeadingPlugin).update.toggle({ level: 4 });
},
keys: 'mod+alt+4',
},
toggleHeading5: {
handler: ({ editor }) => {
editor.plugin(HeadingPlugin).update.toggle({ level: 5 });
},
keys: 'mod+alt+5',
},
toggleHeading6: {
handler: ({ editor }) => {
editor.plugin(HeadingPlugin).update.toggle({ level: 6 });
},
keys: 'mod+alt+6',
},
},
}),
BlockquotePlugin.configure({
component: BlockquoteElement,
inputRules: [BlockquoteRules.markdown()],
shortcuts: { toggle: { keys: 'mod+shift+period' } },
}),
HorizontalRulePlugin.configure({
component: HrElement,
inputRules: [
HorizontalRuleRules.markdown({ variant: '-' }),
HorizontalRuleRules.markdown({ variant: '_' }),
],
}),
] as const;'use client';
import { BlockquoteRules, HeadingRules, HorizontalRuleRules } from 'platejs';
import {
BlockquotePlugin,
HeadingPlugin,
HorizontalRulePlugin,
ParagraphPlugin,
} from 'platejs/react';
import { BlockquoteElement } from '@/components/editor/blockquote';
import { HeadingElement } from '@/components/editor/heading';
import { HrElement } from '@/components/editor/horizontal-rule';
import { ParagraphElement } from '@/components/editor/paragraph';
export const BasicBlocksKit = [
ParagraphPlugin.configure({ component: ParagraphElement }),
HeadingPlugin.
import { createEditor } from 'platejs/react';
import { BasicBlocksKit } from '@/components/editor/basic-blocks';
export const editor = createEditor({
plugins: BasicBlocksKit,
});import { createEditor } from 'platejs/react';
import { BasicBlocksKit } from '@/components/editor/basic-blocks';
export const editor = createEditor({
plugins: BasicBlocksKit,
horizontal-rule renders the editable divider with selected/focused ring styles and a static companion component.
'use client';
import {
type HorizontalRulePlugin,
type EditorElementProps,
EditorElement,
useEditorFocused,
useEditorReadOnly,
useElementSelected,
} from 'platejs/react';
import * as React from 'react';
import { cn } from '@/lib/utils';
export function HrElement(
props: EditorElementProps<typeof HorizontalRulePlugin>
) {
const readOnly = useEditorReadOnly();
const selected =
Use BaseBasicBlocksKit when rendering read-only content with platejs/static.
import {
BaseBlockquotePlugin,
BaseHeadingPlugin,
BaseHorizontalRulePlugin,
BaseParagraphPlugin,
} from 'platejs';
import { BlockquoteElementStatic } from '@/components/editor/blockquote-static';
import { HeadingElementStatic } from '@/components/editor/heading-static';
import { HrElementStatic } from '@/components/editor/horizontal-rule-static';
import { ParagraphElementStatic } from '@/components/editor/paragraph-static';
export const BaseBasicBlocksKit = [
BaseParagraphPlugin.configure({
component: ParagraphElementStatic,
}),
BaseHeadingPlugin.configure({ component: HeadingElementStatic }),
| Layer | Owner | What It Does |
|---|---|---|
platejs | Package | Exports BaseHorizontalRulePlugin and HorizontalRuleRules. |
platejs/react | Package | Exports HorizontalRulePlugin. |
basic-blocks | Registry | Adds HorizontalRulePlugin with dash and underscore input rules plus HrElement. |
basic-blocks-static | Registry | Adds BaseHorizontalRulePlugin.configure({ component: HrElementStatic }). |
horizontal-rule | Registry UI | Renders editable and static divider components. |
| Insert toolbar | Registry UI | Uses PLUGINS.horizontalRule to select the horizontal-rule capability, then inserts its resolved schema type through the generic insertBlock path. |
There is no package-specific insertHorizontalRule helper. App UI usually passes PLUGINS.horizontalRule to the same descriptor-aware block insertion helper used by other registry blocks.
Use the React plugin when the editor renders the divider UI.
import { HorizontalRuleRules } from 'platejs';
import { HorizontalRulePlugin } from 'platejs/react';
import { createEditor } from 'platejs/react';
import { HrElement } from '@/components/editor/horizontal-rule';
export const editor = createEditor({
plugins: [
HorizontalRulePlugin.configure({
component: HrElement,
inputRules: [
HorizontalRuleRules.markdown({ variant: '-' }),
HorizontalRuleRules.markdown({ variant: '_' }),
],
}),
],
});Use the base plugin with the static component in static rendering paths.
import { BaseHorizontalRulePlugin } from 'platejs';
import { createStaticEditor } from 'platejs/static';
import { HrElementStatic } from '@/components/editor/horizontal-rule-static';
export const staticEditor = createStaticEditor({
plugins: [
BaseHorizontalRulePlugin.configure({
component: HrElementStatic,
}),
],
});import
Horizontal rules are void block elements. Keep the empty text child so the node remains a valid Plate element.
const value = [
{
children: [{ text: 'Before the divider' }],
type: 'paragraph',
},
{
children: [{ text: '' }],
type: 'horizontalRule',
},
{
children: [{ text: 'After the divider' }],
type: 'paragraph',
},
];const value = [
{
children: [{ text: 'Before the divider' }],
type: 'paragraph',
},
{
children: [{ text: '' }],
type: 'horizontalRule',
},
{
children: [{ text: 'After the divider' }],
type: 'paragraph',
},
];| Field | Type | Notes |
|---|---|---|
type | 'horizontalRule' | Default persisted element type. Read the resolved type from editor.plugin(BaseHorizontalRulePlugin).schema.type in reusable code. |
children | [{ text: '' }] | Required child for the void element. |
HorizontalRuleRules.markdown() converts a paragraph into a horizontalRule block and inserts an empty paragraph after it.
| Rule | Trigger | Behavior |
|---|---|---|
HorizontalRuleRules.markdown({ variant: '-' }) | type the third - after -- | Converts --- to horizontalRule, then inserts a paragraph. |
HorizontalRuleRules.markdown({ variant: '_' }) | type a space after ___ | Converts ___ to horizontalRule, then inserts a paragraph. |
The rule resolves BaseHorizontalRulePlugin through the editor and passes its schema.type to tx.nodes.set, so configured element types keep working.
| Surface | Behavior |
|---|---|
| Editable element | Renders a non-editable padded wrapper with an <hr> inside it. |
| Selected + focused | Adds a focus ring around the divider. |
| Read-only | Removes the pointer cursor from the editable component. |
| Static element | Renders the same divider inside EditorElement. |
| Insert toolbar | Adds a Divider item with MinusIcon; PLUGINS.horizontalRule selects the capability. |
The divider component renders {props.children} after the non-editable wrapper so Plate can still keep the void child mounted.
| API | Package | Use |
|---|---|---|
BaseHorizontalRulePlugin | platejs | Headless void horizontalRule block with HTML deserialization and default render tag. |
HorizontalRulePlugin | platejs/react | React horizontal rule plugin. |
HorizontalRuleRules.markdown({ variant: '-' }) | platejs | Creates the dash input rule for ---. |
HorizontalRuleRules.markdown({ variant: '_' }) | platejs | Creates the underscore input rule for ___ . |
'use client';
import {
type HorizontalRulePlugin,
type EditorElementProps,
EditorElement,
useEditorFocused,
useEditorReadOnly,
useElementSelected,
} from 'platejs/react';
import * as React from 'react';
import { cn } from '@/lib/utils';
export function HrElement(
props: EditorElementProps<typeof HorizontalRulePlugin>
) {
const readOnly = useEditorReadOnly();
const selected = useElementSelected();
const focused = useEditorFocused();
return (
<EditorElement {...props}>
<div className="py-6" contentEditable={false}>
<hr
className={cn(
'h-0.5 rounded-sm border-none bg-muted bg-clip-content',
selected && focused && 'ring-2 ring-ring ring-offset-2',
!readOnly && 'cursor-pointer'
)}
/>
</div>
{props.children}
</EditorElement>
);
}import {
BaseBlockquotePlugin,
BaseHeadingPlugin,
BaseHorizontalRulePlugin,
BaseParagraphPlugin,
} from 'platejs';
import { BlockquoteElementStatic } from '@/components/editor/blockquote-static';
import { HeadingElementStatic } from '@/components/editor/heading-static';
import { HrElementStatic } from '@/components/editor/horizontal-rule-static';
import { ParagraphElementStatic } from '@/components/editor/paragraph-static';
export const BaseBasicBlocksKit = [
BaseParagraphPlugin.configure({
component: ParagraphElementStatic,
}),
BaseHeadingPlugin.configure({ component: HeadingElementStatic }),
BaseBlockquotePlugin.configure({
component: BlockquoteElementStatic,
}),
BaseHorizontalRulePlugin.configure({
component: HrElementStatic,
}),
];import { HorizontalRuleRules } from 'platejs';
import { HorizontalRulePlugin } from 'platejs/react';
import { createEditor } from 'platejs/react';
import { HrElement } from '@/components/editor/horizontal-rule';
export const editor = createEditor({
plugins: [
HorizontalRulePlugin.configure({
component: HrElement,
inputRules: [
HorizontalRuleRules.markdown({ variant: '-' }),
HorizontalRuleRules.markdown({ variant: '_' }),
],
}),
],
});