Headings with #

An ATX heading is a line that starts with one to six # characters followed by a space and the heading text. The number of # characters is the level, so # becomes an <h1> and ###### an <h6>: the same heading elements you would write by hand in HTML. (The names come from two earlier plain-text formats that Markdown borrowed these styles from: atx, created by Aaron Swartz in 2002, and the older Setext.)

# Restart the portal
## Before you start
### Access you need
#### Tools
##### Notes
###### Smallest level
####### Seven is too many
<h1>Restart the portal</h1>
<h2>Before you start</h2>
<h3>Access you need</h3>
<h4>Tools</h4>
<h5>Notes</h5>
<h6>Smallest level</h6>
<p>####### Seven is too many</p>

Six levels is the maximum, as in HTML; a line with seven # characters is just a paragraph. In practice documents rarely need more than three or four.

The details that matter

  • The space is required. Without it, the line is ordinary text. This is the most common heading mistake, and many older renderers accepted it, so you will still see #Heading in old files.
  • Closing # characters are optional and are removed: ## Steps ## is the same as ## Steps. A # inside the text, as in "C#" or "#42", stays.
  • Up to three spaces of indentation are allowed before the #. With four or more spaces the line becomes an indented code block instead (see Code: Inline Code and Fenced Code Blocks).
  • A heading needs no blank line around it in CommonMark: it can follow a paragraph directly. Leave blank lines anyway; they make the source easier to read, and some older renderers and linters expect them.
## Steps ##
### Step 3: check ticket #42 ###
#Rollback
#5 is the next step
<h2>Steps</h2>
<h3>Step 3: check ticket #42</h3>
<p>#Rollback
#5 is the next step</p>

The last two lines show both sides of the space rule: neither is a heading, and the renderer joins them into one paragraph. To start a line with a literal # followed by a space, put a backslash in front of it (\# 5 is next); escaping is covered on Blockquotes, Horizontal Rules and Escaping.

Underlined headings

Markdown has a second, older style. A setext heading is a line of text underlined with = characters (level 1) or - characters (level 2) on the next line. Any number of underline characters works.

Kestrel Energy runbooks
=======================

Network
-------
<h1>Kestrel Energy runbooks</h1>
<h2>Network</h2>

Setext headings look good in plain text, but they only have two levels and they are harder to scan in a long file. Most style guides prefer the # style, and so does this topic; a linter can enforce whichever style a team picks. Do not mix the two styles in one document.

Change window
Tuesday 22:00
---

Change window
Tuesday 22:00

---
<h2>Change window
Tuesday 22:00</h2>
<p>Change window
Tuesday 22:00</p>
<hr>

Headings as structure

Headings are not a way to make text big. They form the outline of the document, and several tools rely on that outline:

  • GitHub builds an outline (a table of contents) for every rendered Markdown file from its headings, and gives each heading an anchor so you can link to it, as explained on Markdown on GitHub and GitLab.
  • Documentation sites build their navigation and page titles from headings.
  • Screen reader users jump from heading to heading to find their way, exactly as on any web page (see Accessibility Basics).

So the same rules apply as for HTML headings: one level-1 heading per document, which names it; no skipped levels on the way down (no #### directly under a ##); and the level chosen for the structure, not for the size. Linters can check all three, see Linting and Consistent Style.

Paragraphs and blank lines

A paragraph in Markdown is a run of consecutive lines of text that are not some other block, such as a heading or a list. A blank line (an empty line, or one with only spaces) ends it. Each paragraph becomes a <p> element.

The portal will be unavailable on Tuesday
from 22:00 to 23:30.

Customers can still report outages
   by phone.



Meter readings submitted before 22:00 are saved.
<p>The portal will be unavailable on Tuesday
from 22:00 to 23:30.</p>
<p>Customers can still report outages
by phone.</p>
<p>Meter readings submitted before 22:00 are saved.</p>

Three things to notice:

  • The line break inside the first paragraph has disappeared from the result: the two lines are one paragraph. More on that below.
  • Several blank lines count as one. You cannot add extra vertical space with empty lines; spacing is a matter for the site's CSS.
  • Leading spaces at the start of a continuation line are removed. At the start of a new paragraph, however, four or more spaces turn the text into a code block, so do not indent paragraphs the way you might in a letter.

Why a newline is not a line break

A soft line break is an ordinary newline inside a paragraph. The renderer keeps it in the HTML as a newline, and the browser shows a newline in running text as a space. The effect is that the lines are joined and the browser wraps the text to fit the window.

Tomasz Nowak
NOC network engineer
on call this week
<p>Tomasz Nowak
NOC network engineer
on call this week</p>

This surprises almost everyone, but it is deliberate. It means you can wrap long lines in your editor wherever you like (for example at 80 characters, or after every sentence) without changing the result. It is the same rule HTML follows for whitespace in a paragraph.

Forcing a line break

Sometimes the break is part of the content: an address, a signature, the lines of a poem. A hard line break is a line break that the renderer keeps, as a <br> element. CommonMark offers two ways to write one at the end of a line:

  1. A backslash (\) as the last character of the line. The specification calls it "a more visible alternative", and it is the one to prefer.
  2. Two or more spaces at the end of the line. This is the original way from 2004, and you will meet it often.
Mei Chen\
Service desk lead\
Kestrel Energy
<p>Mei Chen<br>
Service desk lead<br>
Kestrel Energy</p>

The two-space form gives exactly the same HTML, but you cannot see it. In the block below each · stands for a space:

Mei Chen··
Service desk lead··
Kestrel Energy

Trailing spaces cause trouble for that very reason:

  • Many editors and formatters remove trailing spaces when you save, and the line breaks silently disappear.
  • A reviewer cannot see them in a diff, so nobody notices.
  • Editors, .editorconfig settings and git diff --check treat trailing spaces as stray whitespace, so they get flagged or removed. (markdownlint's rule MD009 makes an exception for exactly two spaces.)

Neither form works at the end of a paragraph or heading: there is nothing to break to, so the backslash simply stays as a character. If the renderer allows raw HTML, an explicit <br> also works, and GitHub's documentation lists it as a third option. With raw HTML switched off, as in this site's default, the tag is shown as text.

Where every newline is a break

Not every tool follows the CommonMark rule. In places meant for short messages, people expect Enter to start a new line, so the renderer treats every newline as a hard break:

  • GitHub renders line breaks automatically in issues, pull requests and comments, but not in .md files in a repository. The same text can therefore look different in a comment and in a README.
  • Chat apps such as Slack, Microsoft Teams and Discord always keep your line breaks.
  • Note apps differ, and several have a setting for it; in Obsidian it is called "Strict line breaks".

Renderers usually have an option for this behavior. In markdown-it it is called breaks; the example below turns it on (data-md-breaks), and the same Markdown as in the soft line break example above now keeps its lines:

Tomasz Nowak
NOC network engineer
on call this week
<p>Tomasz Nowak<br>
NOC network engineer<br>
on call this week</p>

Putting it together

A short runbook page by Emeka Obi uses everything on this page: headings for structure, paragraphs separated by blank lines, wrapped source lines, and hard breaks only where the break belongs to the content.

# Replace a failed access switch

This page describes how the NOC replaces an access switch
that no longer responds.

## Before you start

Make sure an approved change exists
and that the site contact knows you are coming.

## Escalation

If the replacement fails, call the on-call network engineer:

Tomasz Nowak\
NOC network engineer\
Internal line 4142
<h1>Replace a failed access switch</h1>
<p>This page describes how the NOC replaces an access switch
that no longer responds.</p>
<h2>Before you start</h2>
<p>Make sure an approved change exists
and that the site contact knows you are coming.</p>
<h2>Escalation</h2>
<p>If the replacement fails, call the on-call network engineer:</p>
<p>Tomasz Nowak<br>
NOC network engineer<br>
Internal line 4142</p>