Generate a Markdown table of contents and download it as a MARKDOWNTOC file, processed locally in your browser.
## Table of Contents
- [Installation](#installation)
- [Requirements](#requirements)
- [Quick start](#quick-start)
- [Manual install](#manual-install)
- [Usage](#usage)
- [Basic example](#basic-example)
- [Advanced configuration](#advanced-configuration)
- [Environment variables](#environment-variables)
- [Logging](#logging)
- [API Reference](#api-reference)
- [Endpoints](#endpoints)
- [Authentication](#authentication)
- [Contributing](#contributing)
- [License](#license)# through ###### heading line by line and ignores anything inside fenced code blocks so triple-backtick snippets containing # comments don't pollute the TOC. A sample is loaded by default so you can see the output before pasting your own.2 / max 4, which is the conventional README range — H1 is the document title (skipped) and H5/H6 are usually too granular to navigate. Tighten both to 2 for a short outline that lists only top-level sections, or widen 1–6 to mirror every heading in a long spec.Table of Contents and prints as ## Table of Contents above the bullet list. Replace it with Contents, Overview, On this page, or any other label your style guide prefers; leave it blank to emit only the bullets, which is what you want if you're embedding the TOC under an existing heading..md file — typically right after the H1 title and before the first H2 section, which is the GitHub README convention. Everything runs in your browser; the markdown never leaves your device.A TOC turns a long markdown document into a navigable page. Readers can jump straight to "Installation," "API," or "Troubleshooting" without scrolling, and search engines surface those anchor links as sitelinks. GitHub itself added a built-in outline sidebar on April 13, 2021, but the rendered sidebar only appears on github.com — it's not embedded in the file, so it doesn't carry over when the README is mirrored on npm, PyPI, crates.io, GitLab, BitBucket, or any docs site that consumes the raw markdown. An inline TOC fixes that gap.
## Table of Contents block is visible immediately on the file view and on npm/PyPI/crates.io package pages.[[_TOC_]] tag explicitly. A pre-generated bullet list is portable across both rendering engines and avoids vendor-specific syntax.{{< toc >}}, [TOC], :::toc), and each produces slightly different anchor slugs. Shipping a hand-rendered TOC removes the per-engine plugin dependency for short pages.2 / max 3 TOC pairs nicely with a 2,000-word design doc.[Section](#section) links as in-document jumps.Anchor links work because each rendered heading gets an id attribute, and [Link text](#id) jumps to it. The rules for deriving that id from the heading text differ per platform. This generator uses an ASCII slugify (lowercase, strip non-alphanumeric, spaces → hyphens) that matches GitHub for plain-English headings; if your headings contain Unicode or emoji, see the notes below.
| Platform | Default rule | Unicode | Emoji | Duplicate handling |
|---|---|---|---|---|
GitHub (github-slugger) |
Lowercase, strip punctuation, spaces → - |
Preserved and lowercased (你好 → 你好) |
Stripped (😄 emoji → -emoji) |
-1, -2 suffix appended |
| GitLab | Lowercase, strip punctuation, spaces → - |
Preserved | Stripped | -1, -2 suffix |
| Hugo (Goldmark, default since v0.60) | autoHeadingIDType: github by default |
Preserved unless set to github-ascii |
Stripped | -1, -2 suffix |
MkDocs (Python-Markdown toc) |
ASCII-only slugify by default — strips accents |
Use slugify_unicode to keep them |
Stripped | _1, _2 suffix (underscore, not hyphen) |
| Docusaurus | GitHub-compatible via github-slugger |
Preserved | Stripped | -1, -2 suffix |
| BitBucket | Lowercase, spaces → -, alphanumerics + hyphens |
Stripped | Stripped | _1, _2 suffix |
| Jekyll (kramdown) | Lowercase, spaces → -, strip most punctuation |
Preserved (kramdown is UTF-8 aware) | Stripped | -1, -2 suffix |
| Document type | Min | Max | Reasoning |
|---|---|---|---|
| Short README (< 500 words) | 2 | 2 | One bullet per top-level section; avoids visual clutter |
| Standard library README | 2 | 3 | Captures methods/options grouped under section H2s |
| Long docs page / tutorial | 2 | 4 | Default; balances detail and scannability |
| RFC, design doc, spec | 1 | 3 | Includes the title as the TOC's first line |
| API reference (one heading per endpoint) | 2 | 5 | Surfaces parameters and response schemas as sub-bullets |
| Book / multi-chapter | 1 | 6 | Captures every heading for printable navigation |
Each platform implements its own slugify function. GitHub uses github-slugger, which lowercases, strips punctuation, replaces spaces with hyphens, and keeps Unicode (Cyrillic, CJK, accented Latin) untouched. MkDocs's default Python-Markdown toc extension uses an ASCII-only slugify that strips accents — Café becomes caf unless you opt into slugify_unicode. Hugo's Goldmark renderer defaults to autoHeadingIDType: github since v0.60 (so it matches GitHub), but pre-v0.60 Blackfriday used a different algorithm that stripped hyphens. The result is that one heading can produce three different slugs across three platforms — which is why an "anchor link broken on Read the Docs but fine on GitHub" bug is so common.
1.) or unordered (-) bullets?Unordered, using the hyphen bullet (-). The hyphen is the most portable bullet character — GitHub, GitLab, MkDocs, Docusaurus, and Jekyll all render it identically and most linters (markdownlint, remark-lint) ship with - as the default. Asterisk (*) and plus (+) are CommonMark-equivalent but trigger style warnings under default linter rules (MD004). If you want an ordered list, paste the output into your editor and do a column-replace from - to 1. . Most popular renderers (CommonMark-based engines, GitHub, GitLab) auto-renumber sequential 1. lines, so writing 1. on every line keeps diffs small when items are reordered — a convention popularized by the Prettier markdown formatter.
Most slugifiers — including GitHub's, this generator's, and Docusaurus's — strip emoji entirely. A heading like ## 🚀 Quick Start becomes the anchor quick-start on GitHub (with a leading hyphen on some implementations: -quick-start). Docusaurus shipped a bug-fix PR in 2023 specifically to align with GitHub's emoji-stripping behavior. If you want emoji to appear in the TOC bullet text but not the anchor, you're fine — this generator preserves the original heading text verbatim in the link label and only sanitizes the URL fragment.
The dominant README convention is after the H1 title (and optional badges/hero image), before the first H2 section. GitHub's auto-generated outline sidebar follows the same logical position. For long docs pages, some style guides put the TOC inside a <details> block so it's collapsible:
<details>
<summary>Table of Contents</summary>
- [Installation](#installation)
- [Usage](#usage)
</details>
Paste the generated bullets between <summary> and </details> to get that collapsible behavior in GitHub, GitLab, and most static site generators.
Most slugifiers strip non-alphanumeric characters except hyphens. So ## 2.1 Configuration becomes 21-configuration on GitHub (the period is dropped, digits stay), while ## Q&A becomes qa (ampersand stripped). If you've copy-pasted a TOC and the link doesn't jump, hover the heading on the rendered GitHub page — there's a paragraph icon that exposes the actual generated id. The xconvert generator follows the same lowercase-and-strip rules, so the slugs it emits will match GitHub for plain English text. For headings with accents, Cyrillic, or CJK characters, GitHub's actual behavior preserves them — this generator strips them, so paste the TOC into the GitHub editor preview to verify before committing.
The auto-rendered outline (shipped April 13, 2021, triggered when a file has 2+ headings) only appears on github.com — collapsed behind a hamburger icon at the top of the file view. It does not appear: on the package pages of npm, PyPI, RubyGems, or crates.io that mirror the README; in GitHub's mobile app; in PR/issue descriptions; in raw markdown views; or when the file is consumed by other tools. An inline TOC is still useful whenever your README is read outside github.com's rendered file view — which is most of the time for any published package.
DocToc and bitdowntoc insert HTML comment markers (<!-- START doctoc -->, <!-- TOC -->) into your file so they can find and regenerate the block on every run. That's powerful for files that change frequently (you can wire it into a pre-commit hook), but it also adds tool-specific markup that anyone reading the raw markdown sees. The xconvert generator is the opposite trade-off: you get a clean copy-paste output with no embedded markers — better for one-shot generation, README templates, and PR descriptions, but you'll regenerate manually when your headings change.
Yes — set Min depth to 1. By default the generator skips H1 because most READMEs use exactly one H1 (the project name) at the top, and listing it as the first TOC bullet just repeats the title you already see. For specs, RFCs, and design docs where the H1 is the first thing you'd want to link to, drop min depth to 1. The HTML spec permits multiple H1s — particularly inside sectioning elements like <article> or <section> — so if your markdown does use several top-level headings, each will appear as a top-level TOC entry.
The generator is markdown-only. MDX (.mdx) heading syntax is identical to markdown so it works fine for the heading lines, but JSX components inside an MDX file are ignored — the regex only matches lines starting with #. AsciiDoc uses =/==/=== for headings and reST uses underlined headings with ===/---/~~~, so neither will parse. For those formats use AsciiDoc's built-in :toc: directive or reST's .. contents:: directive. Need to convert between formats first? See convert markdown to HTML or convert HTML to markdown.