Markdown Table of Contents GeneratorGitHub-Style Links

Paste Markdown to get a nested table of contents with GitHub-style anchor links. Choose heading levels, switch between bullets and numbers, and update an existing TOC in place.

Your input is never sent anywhere.

Details
Markdown input
0 headings

Heading levels to include

List style

Indent size

Paste Markdown above or load the sample to see your table of contents.

Headings found

0

TOC entries

0

Heading structure check

Skipped heading levels

No skipped levels found

Duplicate headings

No duplicate headings found

TIP

Most READMEs read best with H2 and H3 only. After you edit your headings, press the insert button again and the block between the TOC markers is replaced with a fresh list.

Why a Hand-Written Markdown TOC Falls Out of Date

A table of contents in a README or doc is a list of links, and each link points to an anchor built from a heading. Rename or add a heading and the matching link breaks until you fix it by hand. The anchors are easy to get wrong too: GitHub lowercases the text, drops punctuation, and turns each space into a hyphen, so ## What's New in v2.0? becomes #whats-new-in-v20.

This tool reads your headings, builds the anchors with those rules, and outputs a nested list as Markdown or HTML. You can limit the list to the heading levels you want, switch to numbers, and add the result to the top of your document between <!-- TOC:START --> and <!-- TOC:END -->. Run it again after editing and the block is replaced.

README, Wiki, and Blog Post Use Cases

Adding a TOC to a GitHub README

A README with install, usage, API, and contributing sections gets hard to scan. Paste it in, keep H2 and H3, and copy a linked list to the top.

Navigation for long docs and wiki pages

Setup guides, runbooks, and specs with many sections need a way to jump around. Insert the list once and refresh it whenever the outline changes.

Blog posts and Markdown files on your own site

Copy the HTML version for a post or page template. Some static site generators build heading IDs differently, so check a link or two after publishing.

Reviewing a document's outline

The structure check flags skipped heading levels and repeated titles, which helps when you edit a draft or review someone else's doc.

Paste, Pick Levels, Copy: 4 Steps

  1. Paste your Markdown into the input box, or press Load sample to try it first.
  2. Under Heading levels to include, turn on the levels you want. H2 to H4 are selected by default.
  3. Choose bullets or numbers, set the indent size, and optionally add a "Table of Contents" heading or remove numbers like 1. from your headings.
  4. Copy the Markdown or HTML, download a .md file, or press the insert button to put the list at the top of your document.

Note: Everything runs in your browser. The tool doesn't send your Markdown to a server.

Limits: Setext Headings, Slug Rules, Indented Code

  • Only # style headings are detected, with up to three spaces of indentation and a space after the #. Headings underlined with === or --- (Setext style) are not read.
  • Anchors follow GitHub's basic rules. GitLab, static site generators, and editor previews can generate different IDs, so click a few links after publishing.
  • Lines inside fenced code blocks (``` or ~~~) are ignored, and so are lines indented by four or more spaces. A fence that is never closed hides every heading after it.
  • Headings between the TOC:START and TOC:END markers are ignored, so a previous TOC is not counted again. The markers only count when both are present in order.
  • Remove numbers from headings strips a leading number such as 1., 2.1, or 3), and also a plain leading number such as 2024. Check the result if a heading starts with a year or count.
  • Numbered lists use at least three spaces per nesting level, even if you pick 2, so nested items stay inside their parent when rendered.
  • A heading made only of symbols or emoji gets the anchor #section, which may not match what GitHub generates.

How Heading Text Becomes an Anchor (Slug Examples)

The anchor this tool generates for common heading patterns.

Heading (Markdown)Link textGenerated anchor
## Getting StartedGetting Started#getting-started
### 1. Install on Mac / Windows1. Install on Mac / Windows#1-install-on-mac--windows
## Using `npm install`Using npm install#using-npm-install
## What's New in v2.0?What's New in v2.0?#whats-new-in-v20
## Set API_KEY and PortSet API_KEY and Port#set-api_key-and-port
## 🚀 Quick Start🚀 Quick Start#-quick-start
A slash with a space on each side leaves two spaces behind, which is why Mac / Windows becomes mac--windows with a double hyphen.

Heading Structure Tips for Easier Navigation

A few habits that keep a generated list short, accurate, and easy to follow.

Outline first, then generate the list

Settle your section titles and their order before you build the list. Then regenerate it whenever the outline changes rather than editing individual links, so the list and the page can't drift apart.

Skipped levels make nesting hard to follow

Going from H2 straight to H4 leaves a gap in the outline. This tool nests a skipped level only one step under its parent and flags it in the structure check, but fixing the heading level gives readers a clearer picture.

Keep the list to H2 and H3

Including every level can make the list as long as a short article. H2 and H3 cover the main sections of most READMEs and guides, and readers can still scan the deeper levels in the page itself.

Plain headings give predictable anchors

Emoji, slashes, and punctuation change or disappear in the anchor, and repeated titles get numeric suffixes. Short, distinct headings make links easier to predict. On GitHub you can also hover a heading and click the link icon to see its real anchor.

Broken Anchors and Other Common Problems

A heading changed but the TOC link still points to the old anchor

You rename "Setup" to "Installation" and forget the list, so the link jumps nowhere.

💡 See the solution▼
Insert the list with the TOC markers, then paste your updated Markdown and press the button again. The block between the markers is replaced with links that match the current headings.

A guessed anchor doesn't match the real one

Typing #what's-new for a heading like "What's New?" fails because the apostrophe and question mark are removed from the anchor.

💡 See the solution▼
Copy the generated anchor instead of writing it yourself. For this heading the tool outputs #whats-new.

Two "Overview" headings, and the link opens the wrong one

Only the first "Overview" gets #overview. The second is #overview-1, so a link written as #overview always lands on the first.

💡 See the solution▼
Check the duplicate headings warning, then rename repeated titles (for example "API Overview" and "CLI Overview") so each link is clear.

Headings after a code block are missing from the TOC

A code fence that is opened but never closed makes everything after it look like code, so the headings below it are skipped.

💡 See the solution▼
Check that every ``` or ~~~ has a matching closing line. The heading count next to the input box should go up once the fence is closed.

Anchor, Duplicate Heading, and Privacy Questions

Q.Will the TOC links work on GitHub?

Q.

A. The anchors follow GitHub's basic rules: text is lowercased, each space becomes a hyphen, punctuation is dropped, underscores and hyphens stay, and a repeated heading gets a -1, -2 suffix. That is what GitHub READMEs and other pages rendered by GitHub use. Other renderers can differ, so click a few links after you publish.

Q.Can I build a TOC from only some heading levels?

Q.

A. Yes. The H1 to H6 buttons turn each level on or off, and the number under each button shows how many headings of that level were found. H2, H3, and H4 are selected by default. Nesting is relative to what you include, so if you skip H1, your H2 headings become the top level of the list.

Q.What happens when two headings have the same title?

Q.

A. The first one keeps the plain anchor, such as #overview. The second becomes #overview-1, the third #overview-2, and so on, in document order. The checker lists every repeated title so you can rename it if you want cleaner links.

Q.Are lines starting with # inside code blocks treated as headings?

Q.

A. No. Lines inside a fenced code block (``` or ~~~) are skipped, so shell comments like # install dependencies don't show up in your TOC. A line indented by four or more spaces is not read as a heading either.

Q.How do I update the TOC after I edit my headings?

Q.

A. Press the insert button once. It adds the list at the top of your Markdown between <!-- TOC:START --> and <!-- TOC:END -->. After you edit headings, press the same button again and only the text between the markers is replaced. Headings between the markers are ignored when the tool scans your document, so the optional "Table of Contents" heading never lists itself.

Q.Why does my emoji heading produce an anchor that starts with a hyphen?

Q.

A. Emoji are removed from the anchor, but the space after them is kept and turned into a hyphen. A heading like "🚀 Quick Start" becomes #-quick-start. If you would rather have #quick-start, drop the emoji from the heading.

Q.Is my Markdown uploaded anywhere?

Q.

A. No. Parsing, anchor generation, and the list are all built by JavaScript in your browser, and the tool doesn't send your text to a server. That makes it fine for drafts and private docs.

User Feedback & Request

Your feedback
makes this tool better.

Tell us which features you'd like, what should be fixed, or which new tool you'd like to see. An engineer reads every submission and uses it to guide development.

Send feedback