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
Choose the anchor rules used by your target: GitHub / Hugo, GitLab, Jekyll (GFM), kramdown or Bitbucket Cloud. Check a generated link in that renderer.
Show the full Markdown with the TOC replaced between <!-- toc --> and <!-- /toc -->. Without markers, insert before the first # heading; an unclosed marker leaves the document unchanged.
Formatting and heading levels
Choose hyphens, asterisks or numbered Markdown items. Each numbered item starts with 1.; your Markdown renderer displays the list numbers.
Indent each heading level by 2 spaces, 4 spaces or a tab. The shallowest included heading starts without indentation.
Include headings from this level, between 1 and 6. If the minimum is higher than the maximum, the two values are read in ascending order.
Include headings up to this level, between 1 and 6. Excluded headings still count when duplicate anchor IDs are assigned.
Turn off Include H1 when your template already displays the document title. Remaining headings keep their anchor IDs.
Copy the complete generated Markdown TOC. This button is unavailable when the selected range contains no headings.
Copy the complete Markdown produced by Marker mode. An empty TOC or an unclosed opening marker disables this copy.
Paste or type Markdown; headings and settings update the TOC immediately. ATX (#) and setext headings are read; fenced code and existing TOC marker blocks are skipped.

Your generated TOC will appear here.

Read the full guide Markdown TOC Generator: One File, Four Anchor Dialects
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 uses autoHeadingIDType: github by default.
  • GitLab: the same rules, as listed in GitLab’s heading anchor docs (GitLab 17.0 and later). Repeated hyphens are kept: a --- b becomes a-----b.
  • Jekyll (kramdown GFM): Jekyll’s default input: GFM uses 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 uses section when 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.