How to auto-generate a markdown table of contents
- Step 1Load your Markdown — Paste the doc into the text box or drop a .md, .mdx, .markdown, or .txt file. One file at a time.
- Step 2Choose where the TOC goes — Set position to after-h1 (default — keeps your title first) or top (TOC above the whole body).
- Step 3Scope the heading depth — Leave minDepth 1 / maxDepth 6 for everything, or narrow to, say, minDepth 2 / maxDepth 3 for a tidy two-level TOC.
- Step 4Run the generator — The tool scans headings, builds anchor slugs, and assembles the nested bullet list under a
## Table of Contentsheading. - Step 5Review the inserted TOC — Check the preview: indentation reflects depth minus minDepth, and each entry links to its heading's anchor.
- Step 6Download the updated Markdown — Save the file (it keeps its original name) and commit or publish it. Re-run after heading edits to refresh.
Every option this tool exposes
These three controls are the entire option surface — there are no presets, no custom TOC title field, and no drag-to-reorder.
| Option | Type | Default | Range / values | Effect |
|---|---|---|---|---|
| position | enum | after-h1 | after-h1, top | after-h1 inserts the TOC just below the first H1; top inserts it above the body (still below frontmatter). |
| minDepth | number | 1 | 1-6 | Lowest heading level (smallest number = biggest heading) included. Also the level that sits flush-left in the TOC. |
| maxDepth | number | 6 | 1-6 | Highest heading level (deepest) included. Headings deeper than this are dropped from the TOC. |
How a heading becomes an anchor slug
The slug function: lowercase, strip characters outside [A-Za-z0-9_ and hyphen], spaces to hyphens, collapse and trim hyphens.
| Heading text | Generated anchor | Why |
|---|---|---|
| ## Getting Started | #getting-started | Lowercased; space becomes a hyphen. |
| ## API Reference & Notes | #api-reference-notes | & is stripped; the surrounding spaces collapse to single hyphens. |
| ## snake_case Config | #snake_case-config | Underscore is a word character and is kept; the space is hyphenated. |
| ## 100% Done | #100-done | Digits kept; % stripped, leaving a clean hyphen. |
| ## Configuração | #configurao | Non-ASCII letters (ç, ã) are stripped — this is NOT a byte-for-byte GitHub match. |
Tier limits for Markdown tools
Size is the raw file bytes; charLimit is a separate cap on character count. This tool processes one file per run.
| Tier | Max file size | Max characters | Files per batch |
|---|---|---|---|
| Free | 1 MB | 500,000 | 1 |
| Pro | 10 MB | 5,000,000 | 10 |
| Pro-media | 50 MB | 20,000,000 | 50 |
| Developer | 500 MB | Unlimited | Unlimited |
Cookbook
Recipes below show the exact before/after for common documentation TOCs. Output is plain Markdown, so what you paste in the box is what lands in your file.
Full TOC after the first H1 (defaults)
With position=after-h1, minDepth=1, maxDepth=6, the title stays on top and the TOC drops in right below it.
INPUT
# Onboarding Guide
## Prerequisites
### Accounts
## First Run
OUTPUT
# Onboarding Guide
## Table of Contents
- [Onboarding Guide](#onboarding-guide)
- [Prerequisites](#prerequisites)
- [Accounts](#accounts)
- [First Run](#first-run)
## Prerequisites
### Accounts
## First RunTwo-level TOC with minDepth=2, maxDepth=3
Skip the H1 title and any H4+ detail; show only the H2/H3 spine of the doc.
INPUT # Reference ## Endpoints ### GET /users #### Query params ## Errors OUTPUT (TOC excerpt) ## Table of Contents - [Endpoints](#endpoints) - [GET /users](#get-users) - [Errors](#errors) # (H4 'Query params' is excluded by maxDepth=3)
TOC at the very top with position=top
Put navigation above the title — useful when the doc is rendered on a page where the H1 is shown separately.
INPUT # Release Notes ## v2.1 ## v2.0 OUTPUT ## Table of Contents - [Release Notes](#release-notes) - [v2.1](#v21) - [v2.0](#v20) # Release Notes ## v2.1 ## v2.0
Frontmatter is preserved
A YAML block at the top is detected and kept; the TOC is inserted below it, never inside it.
INPUT --- title: Setup layout: doc --- # Setup ## Install OUTPUT --- title: Setup layout: doc --- # Setup ## Table of Contents - [Setup](#setup) - [Install](#install) ## Install
Code-block headings are ignored
A # line inside a fenced block is sample text, not a heading, so it never appears in the TOC.
INPUT # Shell Tips ## Comments ```bash # this is a comment, not a heading echo hi ``` OUTPUT (TOC excerpt) ## Table of Contents - [Shell Tips](#shell-tips) - [Comments](#comments) # the '# this is a comment' line is skipped
Edge cases and what actually happens
Document has no headings at all
UnchangedIf no line matches an ATX heading in the chosen depth range, the tool returns your input exactly as-is. No empty ## Table of Contents is added.
position=after-h1 but there is no H1
By designWhen the doc starts at H2 (no # line), the after-h1 mode falls back to inserting the TOC at the top of the body, the same as position=top.
Two headings with identical text
Duplicate anchorBoth headings produce the same slug, so both TOC links point to the first one. The tool does NOT append -1/-2 like GitHub does — rename one heading if you need distinct links.
Heading in a non-Latin script (e.g. CJK or accented)
StrippedThe slug keeps only ASCII word characters, so ## 安装指南 becomes an empty anchor and ## Café becomes #caf. These anchors will not resolve on GitHub, which preserves Unicode.
minDepth set higher than maxDepth
Empty rangeIf minDepth exceeds maxDepth, the inclusive filter matches no level, so no headings qualify and the document is returned unchanged.
Inline formatting in a heading (`**bold**`, `` `code` ``)
CleanedEmphasis markers **, __, ~~, and backticks are stripped from the link text before slugging, so ## **API** shows as - [API](#api) — readable and correctly linked.
An existing `## Table of Contents` already in the doc
DuplicatedThe tool does not detect or replace a prior TOC — it inserts a fresh one. Delete the old block first, or it will show up as a TOC entry and a stray section.
Setext headings (underlined with === or ---)
Not detectedOnly ATX headings (lines starting with #) are scanned. Convert Setext-style headings to #/## form first, or run them through the prettifier to normalize.
Closed ATX headings (`## Title ##`)
Trailing hashes keptThe trailing ## becomes part of the heading text and slug, producing #title- artifacts. Remove the closing hashes for clean anchors.
File over the free tier 1 MB / 500K-character cap
RejectedOversized files are rejected before processing on the free tier. Trim the doc, or upgrade to Pro (10 MB / 5,000,000 characters) for large handbooks.
Frequently asked questions
What exactly does the tool insert?
A single ## Table of Contents heading followed by a blank line and one nested bullet per qualifying heading, each linking to that heading's anchor.
Can I change the TOC heading text?
No. The inserted heading is hard-coded as ## Table of Contents. If you want a different label or level, edit the output after generating.
Where is the TOC placed?
By default (position=after-h1) it goes immediately after the first H1. Set position to top to place it above the body. Frontmatter always stays on top either way.
How do I limit which heading levels appear?
Use minDepth and maxDepth (1-6). For a clean H2-H3 TOC, set minDepth=2 and maxDepth=3. You do not need to hand-edit the output.
Are the anchors exactly GitHub-compatible?
For plain ASCII headings, yes — lowercase, spaces to hyphens, punctuation stripped. For non-ASCII (accents, CJK, emoji) it diverges: those characters are dropped, while GitHub keeps Unicode.
Why do two identical headings link to the same place?
The tool generates the same slug for the same text and does not add -1/-2 suffixes. Give duplicate headings distinct wording for unique anchors.
Will headings inside code blocks show up?
No. Lines inside fenced `` blocks are treated as code, not headings, so a # comment` in a sample never enters the TOC.
Does it touch my frontmatter?
No. YAML (---) and TOML (+++) blocks are detected and left intact; the TOC is inserted after them.
What file types can I feed it?
Paste text directly, or upload .md, .mdx, .markdown, or .txt. It processes one file per run.
Is my document uploaded anywhere?
No. Processing happens entirely in your browser. Nothing is sent to a server.
How big a doc can it handle?
Free tier: up to 1 MB or 500,000 characters. Pro raises that to 10 MB / 5,000,000 characters, with higher tiers beyond.
What if I also need to shift heading levels first?
Use the Heading Shifter to bump levels, then generate the TOC. For splitting a giant doc into per-file TOCs, see the Splitter.
Privacy first
All Markdown processing runs locally in your browser using JavaScript. No file is ever uploaded to JAD Apps servers — only metadata counters are saved for signed-in dashboard stats.