Markdown cheatsheet · Anchor links

Anchor links in markdown

Linking to a heading is a normal link pointing at #the-heading-slug. The whole trick is knowing how the renderer turns your heading into that slug, and that's exactly what breaks.

The syntax

A link to a heading on the same page is a normal link whose target is # plus the heading, lowercased, with spaces turned into hyphens.

You type
Jump to [the install steps](#installing-the-cli).

## Installing the CLI
You get

Jump to the install steps.

Installing the CLI

You don't create the anchor. The renderer generates one for every heading automatically, and you link to it. The entire difficulty of this topic is predicting what that generated anchor will be.

The slug rules, which is what you're really here for

To turn a heading into its anchor, GitHub-style renderers do this, in order:

  1. Lowercase everything.
  2. Remove anything that isn't a letter, number, space or hyphen. Punctuation is deleted, not replaced.
  3. Replace each space with a hyphen.
HeadingAnchor
# Getting Started#getting-started
# Step 1: Install#step-1-install
# FAQ & Support#faq--support
# What's new?#whats-new
# café con leche#café-con-leche
The two that catch everyone. Step 1: Install keeps the number but drops the colon, giving step-1-install. And FAQ & Support becomes faq--support with two hyphens, because the space before & and the space after both become hyphens while the & itself is deleted. Miss a hyphen and the link silently goes nowhere.

Duplicate headings

Two headings with the same text can't share an anchor, so the renderer appends -1, -2 and so on to the later ones.

You type
## Notes
...
## Notes
You get

first → #notes, second → #notes-1

Linking to another file's heading

Combine a relative file path with the anchor. The file part is a normal relative link; the anchor part follows the same slug rules.

You type
See [the config guide](docs/setup.md#environment-variables).
You get

When you need an anchor that isn't a heading

To link to a spot that has no heading, drop in an HTML anchor where inline HTML is allowed:

You type
<a id="pricing"></a>
Our pricing works like this...

[jump to pricing](#pricing)
You get

Our pricing works like this...

jump to pricing

You can also give a heading an explicit id in some flavors (Pandoc, kramdown) with ## Pricing {#pricing}, which frees you from the slug rules entirely. GitHub doesn't support that form.

Where this differs by renderer

RendererHeading anchorsCustom ids
GitHub / GitLabAuto, slug rules aboveOnly via <a id>
PandocAuto{#custom-id} after the heading
kramdown / JekyllAuto{: #custom-id}
ObsidianUses the heading text directly: [[#Heading]]Block refs with ^
Strict CommonMarkNone. No heading ids at all.Only raw HTML

Common questions

Why does my anchor link go nowhere?

Almost always a slug mismatch: a colon or ampersand you left in, a hyphen you added that the renderer didn't, or a capital letter. Copy the anchor from the rendered heading rather than guessing; on GitHub, hover the heading and click the link icon that appears.

Do anchors work in the preview but not on GitHub?

Different renderers, sometimes different slug rules. If it works locally and breaks on GitHub, trust GitHub's version and adjust.

How do I link to a heading with emoji in it?

The emoji is stripped like other non-word characters, but the surrounding hyphens can double up. Copy the real anchor rather than deriving it.

Related syntax

· All markdown guides

Less syntax. More writing.

Download maxdown

Free · no account · macOS, Windows & Linux