# all·markdown

How to add a table of contents in Markdown

Markdown has no contents directive: a table of contents is a list of links to heading anchors, like - [Install](#install). Each anchor is the heading text in lowercase, with spaces turned into hyphens and punctuation removed.

Try it - edit the markdown and watch the preview

Visual

Open the full editor Runs in this tab, nothing is uploaded.

Markdown has no table-of-contents directive. A contents list is a list of ordinary links pointing at heading anchors:

- [Installing](#installing)
- [Configuration](#configuration)
- [Troubleshooting](#troubleshooting)

Every heading gets an anchor automatically. Nothing has to be declared.

How the anchor is generated

Take the heading text, then:

  1. lowercase it
  2. replace each space with a hyphen
  3. remove punctuation
  4. leave everything else alone
HeadingAnchor
## Installing#installing
## Set-up (fast)#set-up-fast
## Two words here#two-words-here
## FAQ#faq

When two headings share the same text, the first keeps the plain anchor, the second gets -1, the third -2, in document order. These are the rules GitHub documents for section links, and this editor follows them, so a contents list that works in the preview works on GitHub.

This is where most broken anchor links come from: the brackets, the colon or the emoji in the heading is gone from the slug, and the guess does not match. Render the page and copy the link from the heading rather than working it out by hand.

Custom anchors

## Configuration {#config}

Works in kramdown (Jekyll), Pandoc, Python-Markdown and Quarto. GitHub renders the braces as literal text.

Where HTML is allowed, this works anywhere, GitHub included:

<a name="config"></a>
## Configuration

Empty anchor immediately before the heading. Ugly, portable, and the form GitHub’s own documentation gives for custom anchors.

What builds the list for you

GitHub shows a contents dropdown at the top-right of any rendered Markdown file. There is nothing to add to the file: it is built from the headings.

Static site generators (Hugo, Jekyll, Docusaurus, MkDocs) all have a built-in contents variable you place in the template rather than the document.

The editor on this page builds one live in the Contents panel while you type, and clicking an entry scrolls the preview to it. That is a reading aid rather than something inserted into the file, so the file stays clean.

If you want the list in the file (a README, where the reader may be looking at raw text) write it by hand and keep it short. A contents list longer than the screen is worse than none.

Linking between files

The same hash works after a path:

[the config section](docs/setup.md#configuration)

On GitHub this resolves relative to the current file. In a static site it resolves against the built URL, which may not be the same path: check one before writing fifty.

Common questions

Does Markdown generate a table of contents automatically?
No. Markdown has no directive for it: a contents list is ordinary links pointing at heading anchors. Some tools add one for you: GitHub shows a contents dropdown at the top of any rendered file, and most static site generators can build one.
How is a heading anchor generated?
Lowercase the heading text, replace each space with a hyphen, drop punctuation, and keep the rest. "Two words here" becomes `#two-words-here`. A repeated heading keeps the plain anchor the first time, then gets `-1`, then `-2`.
How do I link to a section?
`[Configuration](#configuration)` - a normal link whose target is a hash followed by the anchor. Nothing else is needed; the anchor exists as soon as the heading does.
Why is my anchor link broken?
Usually punctuation or an emoji in the heading. `## Set-up (fast)` becomes `#set-up-fast`, not `#set-up-(fast)`. Render the page and copy the link from the heading itself rather than guessing the slug.
Can I set my own anchor?
Yes, two ways. kramdown and Pandoc accept `## Heading {#custom-id}`, which GitHub shows as literal text. Wherever HTML is allowed, an empty `<a name="custom-id"></a>` before the heading works too, and that is the form GitHub documents.

Sources