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.
When to use
Section titled “When to use”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
Gapcursorso users can place the caret immediately above or below it - For decorative dividers with custom styling, override
HTMLAttributesto add a class
Live Playground
Section titled “Live Playground”Type ---, ***, or ___ followed by space at the start of a line to insert a horizontal rule. You can also use the toolbar button.
The button above the editor is a custom HTML button wired to setHorizontalRule().
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>',});import { Component, signal } from '@angular/core';import { DomternalEditorComponent } from '@domternal/angular';import { Editor, Document, Text, Paragraph, HorizontalRule } from '@domternal/core';
@Component({ selector: 'app-editor', imports: [DomternalEditorComponent], template: ` <domternal-editor [extensions]="extensions" [content]="content" (editorCreated)="editor.set($event)" /> `,})export class EditorComponent { editor = signal<Editor | null>(null); extensions = [Document, Text, Paragraph, HorizontalRule]; content = '<p>Above the rule</p><hr><p>Below the rule</p>';}import { Domternal } from '@domternal/react';import { Document, Text, Paragraph, HorizontalRule } from '@domternal/core';
export default function Editor() { return ( <Domternal extensions={[Document, Text, Paragraph, HorizontalRule]} content="<p>Above the rule</p><hr><p>Below the rule</p>" > <Domternal.Content /> </Domternal> );}<script setup lang="ts">import { Domternal } from '@domternal/vue';import { Document, Text, Paragraph, HorizontalRule } from '@domternal/core';
const extensions = [Document, Text, Paragraph, HorizontalRule];</script>
<template> <Domternal :extensions="extensions" content="<p>Above the rule</p><hr><p>Below the rule</p>"> <Domternal.Content /> </Domternal></template>Schema
Section titled “Schema”| Property | Value |
|---|---|
| ProseMirror name | horizontalRule |
| Type | Node |
| Group | block |
| Content | None (leaf node) |
| HTML tag | <hr> |
HorizontalRule is a leaf node with no content. It cannot contain text or other nodes.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
HTMLAttributes | Record<string, unknown> | {} | HTML attributes added to the <hr> element |
Custom HTML attributes
Section titled “Custom HTML attributes”import { HorizontalRule } from '@domternal/core';
const CustomHR = HorizontalRule.configure({ HTMLAttributes: { class: 'my-divider' },});Commands
Section titled “Commands”| Command | Description |
|---|---|
setHorizontalRule() | Insert a horizontal rule at the current cursor position |
// Insert a horizontal ruleeditor.commands.setHorizontalRule();
// With chainingeditor.chain().focus().setHorizontalRule().run();setHorizontalRule takes one of three paths depending on context:
- 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).
- Cursor in an empty paragraph: Replaces the empty paragraph with the horizontal rule and creates a new paragraph below it.
- 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.
Input rules
Section titled “Input rules”| Input | Result |
|---|---|
--- + space | Horizontal rule |
*** + space | Horizontal rule |
___ + space | Horizontal 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.
Toolbar items
Section titled “Toolbar items”HorizontalRule registers a button in the toolbar with the name horizontalRule in group blocks at priority 130.
| Item | Command | Icon |
|---|---|---|
| Horizontal Rule | setHorizontalRule | minus |
Slash / floating menu
Section titled “Slash / floating menu”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.
| Item | Command | Icon | Shortcut hint |
|---|---|---|---|
| Divider | setHorizontalRule | minus | --- |
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”.
JSON representation
Section titled “JSON representation”{ "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" } ] } ]}Source
Section titled “Source”@domternal/core - HorizontalRule.ts
See also
Section titled “See also”- Hard Break - soft line break (
<br>) withShift+Enter - Paragraph - default block-level text container
- Dropcursor - visual indicator for drag-and-drop targets