Markdown TOC Generator
Generate a clickable table of contents from Markdown headings. GitHub, GitLab, Jekyll, and Bitbucket anchor styles. Update in place via <!-- toc --> markers. Runs in your browser.
- Runs in your browser
- Your data never leaves your browser
- Free · No Sign-Up
Scan with WeChat to share this tool
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.
Anchor Styles
The tool ships with five anchor styles. Pick the one that matches your renderer:
- GitHub / Hugo: lowercase; keep letters, marks, digits,
_,-and spaces; turn each space into-. Runs of spaces become runs of hyphens and leading or trailing hyphens stay. This is github-slugger 2.0.0, which the tool is tested against. Hugo’s Goldmark renderer usesautoHeadingIDType: githubby default. - GitLab: the same rules, as listed in GitLab’s heading anchor docs (GitLab 17.0 and later). Repeated hyphens are kept:
a --- bbecomesa-----b. - Jekyll (kramdown GFM): Jekyll’s default
input: GFMuses kramdown-parser-gfm, which keeps Unicode like GitHub, also turns tabs into hyphens, and de-duplicates by counting each base name. - kramdown (input: kramdown): kramdown’s own parser (basic_generate_id). It drops leading characters until the first ASCII letter, keeps only
a-z,0-9, spaces and hyphens, and usessectionwhen nothing is left. - Bitbucket Cloud: every anchor starts with
markdown-header-(Atlassian issue BCLOUD-8276) and duplicates use_N. Atlassian does not document the rest of the rules, so the tool applies the GitHub rules after the prefix. Bitbucket Data Center does not add heading ids.
Marker Mode
Marker mode keeps your TOC fresh without touching the rest of the document. Wrap the spot you want the TOC to live with two HTML comments:
<!— toc —>
<!— /toc —>
Run the generator any time and the block between the two comments is replaced with a new TOC. If your document has no markers yet, the tool inserts a fresh block right before the first heading and you can commit the result.
Worked Examples
Simple TOC, GitHub style
Input:
# API Reference
## Authentication
### OAuth Flow
## Endpoints
### GET /users
### POST /users
Output (GitHub anchors, 2-space indent):
- [API Reference](#api-reference)
- [Authentication](#authentication)
- [OAuth Flow](#oauth-flow)
- [Endpoints](#endpoints)
- [GET /users](#get-users)
- [POST /users](#post-users)
Duplicate headings
Two sections named ## Examples become #examples and #examples-1 on GitHub, GitLab, Jekyll and kramdown, or #markdown-header-examples and #markdown-header-examples_1 on Bitbucket Cloud.
Skipping H1
If your static site renders the H1 from frontmatter, turn off Include H1 so the TOC starts at the first H2 and indentation flattens by one level.
One Document, Different Platforms
This document has a repeated ### Examples, punctuation and a heading in Chinese. With min level 2 and max level 3:
# Install Guide
## Requirements
### Examples
## Usage
### Examples
## Q&A: what's new?
## 安装 (Install)
GitHub, GitLab and Jekyll (GFM) give the same TOC:
- [Requirements](#requirements)
- [Examples](#examples)
- [Usage](#usage)
- [Examples](#examples-1)
- [Q&A: what's new?](#qa-whats-new)
- [安装 (Install)](#安装-install)
With the kramdown style, 安装 is dropped and only install is left:
- [Requirements](#requirements)
- [Examples](#examples)
- [Usage](#usage)
- [Examples](#examples-1)
- [Q&A: what's new?](#qa-whats-new)
- [安装 (Install)](#install)
Bitbucket Cloud gives the same ids as GitHub with markdown-header- in front, and the second Examples becomes #markdown-header-examples_1.
Limits
- The tool reads ATX (
#) and setext (===,---) headings. It does not read headings written as HTML (<h2>), and it removes inline Markdown with simple patterns, so unusual nesting such as a link inside emphasis can leave stray characters in the label. - Custom ids (
## Title {#custom-id}in kramdown and Hugo) are not read; the tool computes the automatic id. - Hugo, VS Code and other renderers that are not listed here can use different rules. Check one generated link before you commit a long TOC.
FAQ
Why does the anchor style matter?
Each platform turns a heading into an anchor with its own rules. GitHub and GitLab keep Unicode letters and underscores, drop punctuation and turn each space into a hyphen. Jekyll's default GFM parser does the same; kramdown's own parser keeps only ASCII. Bitbucket Cloud prefixes every anchor with markdown-header-. A TOC built with the wrong rules produces dead links.
Does it work with Chinese, Japanese, and Korean headings?
Yes for GitHub, GitLab, Jekyll (GFM) and Bitbucket Cloud: Chinese, Japanese and Korean letters stay in the anchor. The kramdown style (a Jekyll or kramdown site set to input: kramdown) drops every non-ASCII character, and a heading with nothing left gets the id section, section-1 and so on.
How does the marker mode update an existing TOC?
Turn on Marker mode and the tool searches for <!-- toc --> ... <!-- /toc --> in your Markdown, replaces everything between the markers with the freshly built TOC, and leaves everything else untouched. If no markers exist, it inserts a new TOC block right before the first heading. The original document is regenerated on every change.
What if my document has duplicate headings?
Every heading in the document gets a unique id, also headings outside your min / max level range, because the renderer counts them all. GitHub and GitLab append -1, -2 and skip numbers that are already taken; Jekyll and kramdown append -1, -2 per base name; Bitbucket Cloud uses _1, _2.
Does it skip headings inside code blocks?
Yes. Anything inside fenced code blocks (``` or ~~~) is treated as code — the tool never picks up a leading # as a heading. The same applies to lines inside an existing <!-- toc --> ... <!-- /toc --> block, so you can run the generator repeatedly without nesting old TOCs.