MDConverter
Universal In-Browser Converter
100% Client-Side Private

Markdown Headings: Syntax, Levels and Linking to a Heading

By the MDConverter team · Updated · 3 min read

Quick answer

A Markdown heading is a line that starts with one to six # characters followed by a space: # is the largest (level 1), ###### the smallest (level 6). Most renderers also give every heading an automatic anchor, so you can link to it with [text](#heading-text). Use one level-1 heading as the title and nest the others in order — that outline powers tables of contents, Word's navigation pane and screen readers.

Heading syntax: # to ######

Put the # characters at the start of the line, then a space, then the text. The number of # characters is the level.

Markdown
# Level 1 — page title
## Level 2 — section
### Level 3 — sub-section
#### Level 4
##### Level 5
###### Level 6
Result

Level 1 — page title

Level 2 — section

Level 3 — sub-section

Level 4

Level 5

Level 6

Trailing # characters are optional and ignored (## Section ## is the same as ## Section), and up to three spaces of indentation before the # are allowed.

The alternative syntax: underlined headings

Level 1 and 2 headings can also be written by underlining the text with = or -. This "setext" style comes from the original Markdown and is still valid, but most people use # because it works for all six levels.

Markdown
Project title
=============

Section
-------
Result

Project title

Section

Watch out: a line of --- under ordinary text turns that text into a heading. Leave a blank line above --- when you mean a horizontal rule.

GitHub, GitLab, VS Code, Obsidian, MkDocs and most site generators give every heading an ID you can link to. GitHub's rules, which most tools copy:

  1. Convert the heading text to lowercase.
  2. Replace spaces with hyphens.
  3. Remove punctuation (except hyphens and underscores) and any formatting.
  4. If two headings produce the same ID, add -1, -2… to the later ones.

So ## Getting Started (v2) gets the anchor #getting-started-v2.

Markdown
See [Getting started](#getting-started-v2) below.

Jump to [the FAQ](#faq) or open [setup in another file](docs/setup.md#install).
Result

See Getting started below.

Jump to the FAQ or open setup in another file.

The last link points to a heading in another file. In Obsidian, use [[setup#Install]] wiki-link syntax instead.

Need a table of contents built from these anchors? The Markdown table of contents generator creates one from your headings in a click.

Custom heading IDs

Automatic anchors change when you edit the heading text, which breaks links. Two ways to keep a stable ID:

  • Extended syntax ## Install {#install} — supported by Pandoc, MkDocs (attr_list), Hugo, Jekyll (kramdown) and Docusaurus, but not by GitHub.
  • An HTML anchor placed above the heading — <a id="install"></a> — which works on GitHub and anywhere HTML is allowed.

Heading best practices

  • One level-1 heading per document, used as the title. Many site generators already add the page title as an H1, in which case start at ##.
  • Do not skip levels (## straight to ####). Screen readers and tables of contents rely on the order.
  • Keep headings short and descriptive — they become anchor IDs, tab titles in some tools, and sheet names when you convert tables to Excel.
  • Do not use headings for bold text. If you only want emphasis, use **bold**.
  • Leave a blank line before and after a heading. Not always required, but some renderers need it and it keeps the source readable.

Headings when you convert Markdown

Headings carry real structure into other formats: Markdown to Word maps #–###### to Word's Heading 1–6 styles (so the navigation pane and automatic table of contents work), Markdown to PowerPoint starts a new slide at each top-level heading, and Markdown to HTML produces <h1>–<h6>. Going the other way, Word to Markdown turns Word heading styles back into # headings.

Heading support by app

AppSupportedNotes
GitHub / GitLabYesAll six levels, automatic anchors (hover a heading to see the link icon).
ObsidianYesAll six levels; link with [[Note#Heading]].
NotionPartlyTyping #, ##, ### + space creates Heading 1–3; there are no levels 4–6.
DiscordPartly#, ## and ### only; -# makes small subtext.
RedditYesMarkdown mode supports # headings.
SlackNoNo headings in messages; use bold text.
Google DocsPartlyWith Markdown enabled, # + space at the start of a line becomes a heading.

Common mistakes and how to fix them

ProblemFix
#Heading shows the # symbolAdd a space after the # characters: # Heading. CommonMark requires it.
Text above --- became a headingAdd a blank line between the text and the --- horizontal rule.
Anchor link does nothingCheck the ID rules: lowercase, hyphens for spaces, punctuation removed. Duplicate headings get -1, -2.
Heading inside a list item is not renderedIndent it to the list item’s content, or move it out of the list.

Tools for this

Frequently Asked Questions

Frequently Asked Questions About Headings

Comprehensive answers to common technical, formatting, security, and compatibility questions.

Six, from # (level 1) to ###### (level 6), matching HTML’s <h1> to <h6>. Some apps support fewer — Notion and Discord stop at level 3.

Sources

← All Markdown lessons · Markdown cheat sheet