Markdown Horizontal Lines and Dividers
How to add a markdown horizontal line with ---, ***, or ___, and how to avoid the setext heading gotcha that silently turns your divider into an H2 heading.
Short answer: A markdown horizontal line is three or more hyphens, asterisks, or underscores alone on a line — ---, ***, or ___. All three render the same <hr> element. Leave a blank line above the divider, or markdown reads it as a setext heading and turns the paragraph above it into an H2.
Horizontal rules are the one piece of markdown syntax people get wrong without noticing. The syntax is trivial, but there are two collisions built into it: --- is also the setext underline for a level-2 heading, and it's also the delimiter for YAML frontmatter. Both cause silent, confusing failures where your divider vanishes and something else appears in its place.
Here's the full syntax, the rules parsers actually enforce, and the two traps.
The Three Syntaxes
| Syntax | Renders as | Notes |
|---|---|---|
--- | <hr> | Most common. Collides with setext H2 and YAML frontmatter. |
*** | <hr> | Safest. No collision with anything. |
___ | <hr> | Works everywhere, but visually hard to distinguish from --- in monospace. |
All three produce identical output. There is no "thick line" versus "thin line" — a markdown parser emits a single <hr> element and your CSS decides what it looks like.
Section one.
---
Section two.
***
Section three.
___
Section four.
All three dividers above render exactly the same.
The Rules Parsers Enforce
You need at least three characters. Two hyphens is just two hyphens. -- renders as literal text.
-- <- literal text, not a rule
--- <- horizontal rule
---------- <- also a horizontal rule, identical output
Adding more characters doesn't make the line longer or thicker. --- and ------------------ produce the same <hr>.
Spaces between the characters are allowed. These are all valid horizontal rules:
- - -
* * *
_ _ _
* * *
Some writers prefer * * * because it's visually obvious in the source that it's a divider and not a stray line of dashes.
All characters must be the same. -*- is not a rule. Mixing is a syntax error that falls back to literal text.
Nothing else can be on the line. --- end of section is a paragraph, not a rule.
Up to three leading spaces are allowed. Four spaces of indentation turns the line into an indented code block instead, which is one of the more baffling ways to lose a divider. See tabs and indentation in markdown for why four spaces changes the meaning of almost any line.
The Setext Heading Trap
This is the one that bites people. In markdown, a line of hyphens directly beneath a paragraph is not a divider — it's a setext heading underline, inherited from the original 2004 markdown spec and kept in CommonMark and GFM.
Chapter Two
---
You expected a paragraph followed by a divider. You got:
<h2>Chapter Two</h2>
The equivalent with equals signs produces an H1:
Document Title
===
Renders as <h1>Document Title</h1>.
The CommonMark spec is explicit about the precedence: when a line could be read as either a setext heading underline or a thematic break, the setext heading wins. So the fix is a blank line.
Chapter Two
---
Now you get a paragraph and a horizontal rule, which is what you wanted.
The bulletproof workaround: use *** instead of ---. Only hyphens and equals signs are setext underlines, so asterisks can never be misread. If you write a lot of prose with section breaks, switching to *** eliminates the problem permanently.
The scene ends here.
***
The next scene begins.
That renders correctly even with no blank line above it.
The Frontmatter Collision
YAML frontmatter — the metadata block at the top of a file used by Jekyll, Hugo, Astro, Next.js, Obsidian, and most static site generators — is delimited by ---:
---
title: My Post
date: 2026-09-04
---
The post body starts here.
Two things go wrong.
Starting a document with a divider. If the very first line of your file is --- and you meant it as a horizontal rule, most frontmatter parsers will treat it as an opening delimiter and swallow everything up to the next --- as YAML. Sometimes that throws a parse error; sometimes it silently deletes a chunk of your document. Never open a file with a horizontal rule. If you need one, put a blank line or a heading above it, or use ***.
A divider immediately after the closing delimiter. This is the same setext problem in a different costume. If the closing --- is followed by body text and then another --- with no blank line between them, you get an H2 where you wanted a rule.
Our guide to YAML frontmatter in markdown covers the full delimiter rules and which tools expect which format.
Styling the Rule
Markdown gives you no control over how a horizontal rule looks. It emits <hr> and stops. If you want a dotted line, a thicker rule, or a colored divider, you need raw HTML:
<hr style="border: none; border-top: 2px dashed #666;">
Where this works depends entirely on the renderer:
| Platform | Raw <hr> | Inline style attribute |
|---|---|---|
| GitHub / GitLab | Works | Stripped by the sanitizer |
| Static site generators (Hugo, Jekyll, Astro) | Works | Works |
| Obsidian | Works | Works |
| Reddit / Discord / Slack | Not allowed | Not allowed |
| Most desktop markdown editors | Works | Usually works |
GitHub allows a limited subset of HTML and removes style attributes, so you cannot restyle a rule in a README. What you can do on GitHub is a centered ornamental break, since align survives the sanitizer:
<p align="center">• • •</p>
On your own site, style hr in CSS rather than inline in every document. That keeps the markdown portable.
Common Mistakes
- Two dashes instead of three.
--is literal text. Some editors auto-convert--to an en dash, which makes it worse. - No blank line above. Turns the previous paragraph into an H2.
- Trailing content on the line.
--- Section 2 ---is a paragraph. - Four-space indentation. The rule becomes a code block.
- Using a rule as a heading substitute. A divider carries no semantic meaning for screen readers or table-of-contents generators. If a section needs a title, use a heading.
<hr>is a visual break, nothing more.
When to Use Dividers
Horizontal rules earn their place in a few specific situations: separating major sections of a long README, marking scene or time breaks in prose and fiction, closing off a document before a footnotes block, and splitting distinct answers in AI-generated output.
They stop earning their place when every heading has one above it. If a document has an <hr> between every section, the dividers add noise instead of structure — headings already signal the break.
OpenMark renders dividers as proper rules in Document view and colors the raw ---, ***, and ___ markers in Markdown view, so you can see at a glance whether a line of hyphens was parsed as a rule or quietly became a heading. For the rest of the syntax, the markdown cheat sheet covers everything in one page, and markdown bullet points and nested lists explains the other place where hyphens change meaning depending on context.
Download OpenMark → — $9.99, one-time, native macOS. See exactly how your dividers, headings, and frontmatter parse, side by side with the source.