Markdown Table of Contents Generator
Instantly generate a clickable table of contents from headings in your Markdown. Control depth, style, and anchor links.
Heading Structure
| Level | Line | Title | Anchor slug |
|---|---|---|---|
| Enter markdown to see the list of headings. | |||
The # inside code blocks (sections enclosed by three backticks or three tildes) are not counted as headings. Underline-style headings (equal signs/hyphens) are also recognized as H1/H2 respectively.
Anchor links are GitHub-style slugs. Tistory, WordPress, and Velog may have different anchor rules, so links might not work. In such cases, uncheck "Anchor links" and use only the TOC text.
What is the Markdown Table of Contents Generator?
Long Markdown documents need a table of contents for easy navigation. This tool automates that process. Paste your Markdown or drop a file, and it instantly generates a TOC from your headings. It's smart enough to ignore comments inside code blocks and supports both `#` (ATX) and underline (Setext) heading styles. It creates GitHub-style anchor links that work with any language, automatically resolving duplicates by adding suffixes like `-1`. Because your text is processed entirely in your browser, no data is ever sent to a server.
How to use
- Paste your content into the "Markdown Source" box or drag and drop a .md file.
- Select the "Depth" range (e.g., "From H2" to "To H3") to include.
- Choose a list style ("Bullet (-)" or "Number (1.)") and toggle "Anchor links" as needed.
- Select "TOC only" to get the table of contents or "TOC + Body" for the full document.
- Use "Copy" to copy the result or "Save" to download it as a file.
Markdown Table of Contents Generator guide
How this tool is used in real work, and what to watch out for.
What a Table of Contents Actually Changes
The benefits of a table of contents are often exaggerated. Let's be honest about what it really does.
The clearest benefit is for the reader. In a long article, they can jump directly to the section they want, which means they'll read that part instead of hitting the back button. It's also a way for visitors from search engines to immediately confirm, "Yes, the information I'm looking for is in this document."
The effect on search engines is indirect. A TOC itself is not a ranking factor. However, creating a TOC requires you to use headings properly, and a well-structured article with headings makes it easier for search engines to understand its topic. The sitelinks that Google sometimes shows below a search result can also be generated from pages with anchors, but this is at Google's discretion and not guaranteed just by adding a TOC.
Where Anchor Links Work (and Where They Don't)
This tool's anchor slugs are GitHub-style. It converts the text to lowercase, removes punctuation (keeping only letters, numbers, spaces, hyphens, and underscores), and replaces spaces with hyphens. So, "## Selection Criteria" becomes '#selection-criteria'.
The problem is that each platform has its own rules for this. For the same heading, WordPress might generate a different slug, and platforms like Tistory or Naver Blog don't automatically add IDs to headings at all. If clicking a link does nothing, it's because the rules don't match.
| Platform | Anchor Links | Practical Solution |
|---|---|---|
| GitHub READMEs & Issues | Works perfectly | Enable Anchor links, copy the TOC, and paste. That's it. |
| Velog, GitHub Pages, etc. (Markdown-based) | Mostly works | After publishing, click a link or two to verify they work. |
| Tistory | No automatic IDs | Either switch to HTML mode and add IDs to headings manually, or disable Anchor links and use only the TOC text. |
| Naver Blog | No Markdown or ID support | Disable Anchor links and use the tool only for drafting the TOC or checking structure. |
| Brunch | Only provides paragraph structure | Use this tool to check your subheading structure rather than to insert a clickable TOC. |
| WordPress | Different slug rules | Use a TOC plugin, or check the actual IDs on the published page and edit the links manually. |
What Isn't Counted as a HeadingโAnd Why It's Intentional
If the 'Headings count' is lower than you expect, it's usually because of the rules below. This is all intentional, designed to prevent false positives.
- # inside code blocks: Sections enclosed in triple backticks or tildes are skipped entirely. This prevents shell script comments (like `# install`) from being mistaken for H1 headings.
- YAML front matter: The section at the top of a file enclosed by `---` is ignored. You can paste content from Jekyll or Hugo posts directly.
- Lines with no space after the #: `#title` is not a heading. It could be a hashtag, so according to Markdown rules, a space is required for it to be recognized as a heading.
- Lines in tables and blockquotes (>): To avoid misinterpreting underline-style headings, lines containing a pipe (`|`) and lines that are part of a list or blockquote are excluded as heading candidates.
- What *is* recognized: Underline-style (Setext) headings. A line of equal signs under a title is treated as an H1, and a line of hyphens as an H2.
Depth Settings and 'TOC + Body' Mode
There's a reason the default depth is from H2 to H3. The H1 is usually the article's main title, so including it in the TOC would create a link to itself. Including H4 and below often makes the TOC as long as the article, which defeats the purpose. For most blog posts, two levels (H2-H3) are sufficient.
The 'TOC + Body' mode outputs the full Markdown with the table of contents inserted at the beginning. If the top-level H1 (the article title) is excluded from the TOC's depth range, the TOC is cleverly inserted right *after* that H1. This avoids the awkward result of having a TOC appear above the main title. Entering a value in the 'TOC Title' field adds it as a subheading (##) above the TOC; leaving it blank outputs just the list.
- Paste your original text and check the Heading Structure table to see if the grayed-out (excluded) items are intentional.
- Set the depth to H2-H3 and see if the TOC is within 10-15 lines. If it's longer, try reducing the depth to just H2.
- If your publishing platform doesn't support anchor links, uncheck the 'Anchor links' option.
- Switch to 'TOC + Body' to copy the entire document, or use the Save button to download `with-toc.md`.
Frequently asked questions
Is my content uploaded to a server?
No. All processing happens entirely within your browser. Whether you paste text or drop a file, your data is never sent to our servers, ensuring complete privacy.
Will comments in my code be treated as headings?
No. The generator correctly skips any lines inside fenced code blocks (enclosed by ``` or ~~~), so shell comments and other lines starting with # will not be parsed as headings.
What if I have the same heading title multiple times?
The tool automatically handles duplicate titles just like GitHub does. If a title appears more than once, it appends a suffix like `-1`, `-2`, etc., to the anchor slug to ensure each link is unique.
Which heading styles are supported?
It recognizes both ATX-style headings (using 1-6 `#` characters) and Setext-style headings (text underlined with `=` for H1 or `-` for H2).