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.
Jump to [the install steps](#installing-the-cli). ## Installing the CLI
Jump to the install steps.
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:
- Lowercase everything.
- Remove anything that isn't a letter, number, space or hyphen. Punctuation is deleted, not replaced.
- Replace each space with a hyphen.
| Heading | Anchor |
|---|---|
# 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 |
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.
## Notes ... ## Notes
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.
See [the config guide](docs/setup.md#environment-variables).
See the config guide.
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:
<a id="pricing"></a> Our pricing works like this... [jump to pricing](#pricing)
Our pricing works like this...
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
| Renderer | Heading anchors | Custom ids |
|---|---|---|
| GitHub / GitLab | Auto, slug rules above | Only via <a id> |
| Pandoc | Auto | {#custom-id} after the heading |
| kramdown / Jekyll | Auto | {: #custom-id} |
| Obsidian | Uses the heading text directly: [[#Heading]] | Block refs with ^ |
| Strict CommonMark | None. 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.