From zbeyens. The source code is available on GitHub.

Plate
PlatePliteEditorsTemplates
GitHub16kGitHub
DiscordDiscord
    • Stream
    • Copilot
  • Discussion
    • Comments
    • Suggestion
    • Basic Blocks
      • Blockquote
      • Heading
      • Horizontal Rule
    • Callout
    • Code Block
    • Column
    • Date
    • Equation
    • Link
    • List Classic
    • Media
    • MentionElement
    • Table
    • Table of Contents
    • Footnote
    • Toggle
  • Marks
    • Bold
    • Italic
    • Underline
    • Code
    • Highlight
    • Keyboard Input
    • Strikethrough
    • Subscript
    • Superscript
      • Font
      • Line Height
      • Text Align
    • Indent
    • List
      • Exit Break
      • Single Block
      • Trailing Block
    • Autoformat
    • Block Menu
    • Block Placeholder
    • Combobox
      • Emoji
      • MentionElement
      • Slash Command
    • Cursor Overlay
    • Drag & Drop
    • Navigation Feedback
    • Tabbable
    • Toolbar
    • Yjs
    • Multi SelectEditor
    • CSV
    • DOCX
    • HTML
    • Markdown

Horizontal Rule

PreviousNext

Void divider blocks rendered as horizontal rules.

Horizontal Rule ElementBasic BlocksPlus

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.

Loading…
HeadingCallout

On This Page

FeaturesFast PathAdd Basic BlocksRender The DividerAdd Static RenderingOwnershipManual SetupInstall PackageAdd The PluginAdd Static RenderingValue ShapeInput RulesRegistry UIAPI Reference
Build your editor
Production-ready AI template and reusable components.
Get all-access

Features

  • Void block horizontalRule element.
  • HTML deserialization from <hr>.
  • Default render tag of <hr>.
  • --- and ___ input rules through HorizontalRuleRules.markdown().
  • Editable and static registry divider components.
  • Insert toolbar item labeled Divider.
Report an issue

Fast Path

Add Basic Blocks

BasicBlocksKit installs HorizontalRulePlugin with the registry HrElement and both default input rules.

'use client';
 
import {
  BlockquoteRules,
  HeadingRules,
  HorizontalRuleRules,
} from '@platejs/basic-nodes';
import {
  BlockquotePlugin,
  HeadingPlugin,
  HorizontalRulePlugin,
} from '@platejs/basic-nodes/react';
import { 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/basic-nodes';
import {
  BlockquotePlugin,
  HeadingPlugin,
  HorizontalRulePlugin,
} from '@platejs/basic-nodes/react';
import { 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';
 



























































import { createPlateEditor } from 'platejs/react';
 
import { BasicBlocksKit } from '@/components/editor/basic-blocks';
 
export const editor = createPlateEditor({
  plugins: BasicBlocksKit,
});
import { createPlateEditor } from 'platejs/react';
 
import { BasicBlocksKit } from '@/components/editor/basic-blocks';
 
export const editor = createPlateEditor({
  plugins: BasicBlocksKit,

Render The Divider

horizontal-rule renders the editable divider with selected/focused ring styles and a static companion component.

'use client';
 
import type { HorizontalRulePlugin } from '@platejs/basic-nodes/react';
import type { PlateElementProps } from 'platejs/react';
import {
  PlateElement,
  useEditorFocused,
  useEditorReadOnly,
  useElementSelected,
} from 'platejs/react';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
 
export function HrElement(
  props: PlateElementProps<typeof HorizontalRulePlugin>
) {
  const readOnly

















Add Static Rendering

Use BaseBasicBlocksKit when rendering read-only content with platejs/static.

import {
  BaseBlockquotePlugin,
  BaseHeadingPlugin,
  BaseHorizontalRulePlugin,
} from '@platejs/basic-nodes';
import { 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.






Ownership

LayerOwnerWhat It Does
@platejs/basic-nodesPackageExports BaseHorizontalRulePlugin and HorizontalRuleRules.
@platejs/basic-nodes/reactPackageExports HorizontalRulePlugin.
basic-blocksRegistryAdds HorizontalRulePlugin with dash and underscore input rules plus HrElement.
basic-blocks-staticRegistryAdds BaseHorizontalRulePlugin.configure({ component: HrElementStatic }).
horizontal-ruleRegistry UIRenders editable and static divider components.
Insert toolbarRegistry UIUses 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.

Manual Setup

Install Package

pnpm add @platejs/basic-nodes
pnpm add @platejs/basic-nodes

Add The Plugin

Use the React plugin when the editor renders the divider UI.

import { HorizontalRuleRules } from '@platejs/basic-nodes';
import { HorizontalRulePlugin } from '@platejs/basic-nodes/react';
import { createPlateEditor } from 'platejs/react';
 
import { HrElement } from '@/components/editor/horizontal-rule';
 
export const editor = createPlateEditor({
  plugins: [
    HorizontalRulePlugin.configure({
      component: HrElement,
      inputRules: [
        HorizontalRuleRules.markdown({ variant: '-' }),
        HorizontalRuleRules.markdown({ variant: '_' }),
      ],
    }),
  ],
});
import { HorizontalRuleRules } from '@platejs/basic-nodes';
import { HorizontalRulePlugin } from '@platejs/basic-nodes/react';
import { createPlateEditor } from 'platejs/react';
 
import { HrElement } from '@/components/editor/horizontal-rule';
 
export const editor = createPlateEditor({
  plugins: [
    HorizontalRulePlugin.configure({
      component: HrElement,
      inputRules: [
        HorizontalRuleRules.markdown({ variant: '-' }),
        HorizontalRuleRules.markdown({ variant: '_' }),
      ],
    }),
  ],
});

Add Static Rendering

Use the base plugin with the static component in static rendering paths.

import { BaseHorizontalRulePlugin } from '@platejs/basic-nodes';
import { createStaticEditor } from 'platejs/static';
 
import { HrElementStatic } from '@/components/editor/horizontal-rule-static';
 
export const staticEditor = createStaticEditor({
  plugins: [
    BaseHorizontalRulePlugin.configure({
      component: HrElementStatic,
    }),
  ],
});
import { BaseHorizontalRulePlugin } 










Value Shape

Horizontal rules are void block elements. Keep the empty text child so the node remains a valid Plite 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',
  },
];
FieldTypeNotes
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.

Input Rules

HorizontalRuleRules.markdown() converts a paragraph into a horizontalRule block and inserts an empty paragraph after it.

RuleTriggerBehavior
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.

Registry UI

SurfaceBehavior
Editable elementRenders a non-editable padded wrapper with an <hr> inside it.
Selected + focusedAdds a focus ring around the divider.
Read-onlyRemoves the pointer cursor from the editable component.
Static elementRenders the same divider inside PliteElement.
Insert toolbarAdds a Divider item with MinusIcon; PLUGINS.horizontalRule selects the capability.

The divider component renders {props.children} after the non-editable wrapper so Plite can still keep the void child mounted.

API Reference

APIPackageUse
BaseHorizontalRulePlugin@platejs/basic-nodesHeadless void horizontalRule block with HTML deserialization and default render tag.
HorizontalRulePlugin@platejs/basic-nodes/reactReact horizontal rule plugin.
HorizontalRuleRules.markdown({ variant: '-' })@platejs/basic-nodesCreates the dash input rule for ---.
HorizontalRuleRules.markdown({ variant: '_' })@platejs/basic-nodesCreates the underscore input rule for ___ .
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;
});
=
useEditorReadOnly
();
const selected = useElementSelected();
const focused = useEditorFocused();
return (
<PlateElement {...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}
</PlateElement>
);
}
'use client';
 
import type { HorizontalRulePlugin } from '@platejs/basic-nodes/react';
import type { PlateElementProps } from 'platejs/react';
import {
  PlateElement,
  useEditorFocused,
  useEditorReadOnly,
  useElementSelected,
} from 'platejs/react';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
 
export function HrElement(
  props: PlateElementProps<typeof HorizontalRulePlugin>
) {
  const readOnly = useEditorReadOnly();
  const selected = useElementSelected();
  const focused = useEditorFocused();
 
  return (
    <PlateElement {...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}
    </PlateElement>
  );
}
configure
({ component: HeadingElementStatic }),
BaseBlockquotePlugin.configure({
component: BlockquoteElementStatic,
}),
BaseHorizontalRulePlugin.configure({
component: HrElementStatic,
}),
];
import {
  BaseBlockquotePlugin,
  BaseHeadingPlugin,
  BaseHorizontalRulePlugin,
} from '@platejs/basic-nodes';
import { 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,
  }),
];
from
'@platejs/basic-nodes'
;
import { createStaticEditor } from 'platejs/static';
import { HrElementStatic } from '@/components/editor/horizontal-rule-static';
export const staticEditor = createStaticEditor({
plugins: [
BaseHorizontalRulePlugin.configure({
component: HrElementStatic,
}),
],
});