Skip to content

Horizontal Rule

The HorizontalRule node provides a block-level thematic break (<hr>). It supports markdown-style input rules (---, ***, ___ + space) and a toolbar button for insertion.

Use HorizontalRule when you need:

  • An <hr> separator with --- or *** markdown shortcuts
  • Visual section breaks without changing heading hierarchy

Notes:

  • HorizontalRule is an atom node. Pair with Gapcursor so users can place the caret immediately above or below it
  • For decorative dividers with custom styling, override HTMLAttributes to add a class

Type ---, ***, or ___ followed by space at the start of a line to insert a horizontal rule. You can also use the toolbar button.

Click to try it out

HorizontalRule is included in StarterKit. If you are building a custom setup without StarterKit, add it manually:

import { Document, Text, Paragraph, HorizontalRule } from '@domternal/core';
import { DomternalEditor } from '@domternal/vanilla';
const dm = new DomternalEditor(document.getElementById('editor')!, {
extensions: [Document, Text, Paragraph, HorizontalRule],
content: '<p>Above the rule</p><hr><p>Below the rule</p>',
});
PropertyValue
ProseMirror namehorizontalRule
TypeNode
Groupblock
ContentNone (leaf node)
HTML tag<hr>

HorizontalRule is a leaf node with no content. It cannot contain text or other nodes.

OptionTypeDefaultDescription
HTMLAttributesRecord<string, unknown>{}HTML attributes added to the <hr> element
import { HorizontalRule } from '@domternal/core';
const CustomHR = HorizontalRule.configure({
HTMLAttributes: { class: 'my-divider' },
});
CommandDescription
setHorizontalRule()Insert a horizontal rule at the current cursor position
// Insert a horizontal rule
editor.commands.setHorizontalRule();
// With chaining
editor.chain().focus().setHorizontalRule().run();

setHorizontalRule takes one of three paths depending on context:

  1. Cursor in a list or task item: Inserts the rule outside the list item instead of nesting it inside, so the bullet, number or checkbox above it keeps its content. In the last item of a list the rule lands directly after the list; in a first or middle item the parent list is split around that item, so the items above and below the rule stay intact. If the item’s label is empty, that empty item is consumed by the rule instead of being left behind as a dangling bullet. This path applies only to a collapsed cursor sitting in the item’s first block (its label).
  2. Cursor in an empty paragraph: Replaces the empty paragraph with the horizontal rule and creates a new paragraph below it.
  3. Cursor in a non-empty block: Inserts the horizontal rule after the current block and creates a new paragraph below it.

In every case, the cursor moves into the new paragraph after the rule. The command only works when the cursor is inside a textblock - it will not insert inside non-text contexts like table cell selections.

InputResult
--- + spaceHorizontal rule
*** + spaceHorizontal rule
___ + spaceHorizontal rule

Type the pattern at the start of a new line, then press space. The entire paragraph is replaced with a horizontal rule, and the cursor moves to the next block below.

The regex also matches —- + space (em dash + hyphen + space), which handles the case where the Typography extension converts -- to before you type the third -.

The input rules do not take the list-aware path that setHorizontalRule() uses: they replace the block the cursor sits in.

HorizontalRule registers a button in the toolbar with the name horizontalRule in group blocks at priority 130.

ItemCommandIcon
Horizontal RulesetHorizontalRuleminus

HorizontalRule also registers one item in the Basic group of the slash command and floating menu with the name horizontal-rule at priority 150, matched by the keywords divider, hr, line, separator, horizontal rule.

ItemCommandIconShortcut hint
DividersetHorizontalRuleminus---

The label here is Divider, not the toolbar’s “Horizontal Rule”, so both terms find the item. The description shown next to the label is “Insert a horizontal rule”.

{
"type": "horizontalRule"
}

A document with a horizontal rule between two paragraphs:

{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Above" }
]
},
{ "type": "horizontalRule" },
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Below" }
]
}
]
}

@domternal/core - HorizontalRule.ts

  • Hard Break - soft line break (<br>) with Shift+Enter
  • Paragraph - default block-level text container
  • Dropcursor - visual indicator for drag-and-drop targets