Markdown Table of Contents Generator

Instantly generate a clickable table of contents from headings in your Markdown. Control depth, style, and anchor links.

Reads headings to instantly create a table of contents ยท Files are not sent to the server
๐Ÿ“ Drag & drop .md file or click to select
Files are read in browser only ยท Max 8MB
Table of Contents
~

Heading Structure

LevelLineTitleAnchor 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

  1. Paste your content into the "Markdown Source" box or drag and drop a .md file.
  2. Select the "Depth" range (e.g., "From H2" to "To H3") to include.
  3. Choose a list style ("Bullet (-)" or "Number (1.)") and toggle "Anchor links" as needed.
  4. Select "TOC only" to get the table of contents or "TOC + Body" for the full document.
  5. 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.

In the Heading Structure table, if you see levels being skipped (e.g., an H4 directly following an H2), fix that first. This is the most common reason for odd-looking indentation in the generated 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.

PlatformAnchor LinksPractical Solution
GitHub READMEs & IssuesWorks perfectlyEnable Anchor links, copy the TOC, and paste. That's it.
Velog, GitHub Pages, etc. (Markdown-based)Mostly worksAfter publishing, click a link or two to verify they work.
TistoryNo automatic IDsEither switch to HTML mode and add IDs to headings manually, or disable Anchor links and use only the TOC text.
Naver BlogNo Markdown or ID supportDisable Anchor links and use the tool only for drafting the TOC or checking structure.
BrunchOnly provides paragraph structureUse this tool to check your subheading structure rather than to insert a clickable TOC.
WordPressDifferent slug rulesUse a TOC plugin, or check the actual IDs on the published page and edit the links manually.
Duplicate headings are handled the same way as on GitHub: a `-1` is appended to the slug of the second identical title, `-2` to the third, and so on. If the 'Duplicate titles' stat is not 0, this means some slugs have been modified to ensure every link is unique.

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.
In heading text, formatting like bold, italics, links, and inline code is stripped out before being added to the TOC. "## **Important** Thing" will appear in the TOC as "Important Thing".

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.

  1. Paste your original text and check the Heading Structure table to see if the grayed-out (excluded) items are intentional.
  2. 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.
  3. If your publishing platform doesn't support anchor links, uncheck the 'Anchor links' option.
  4. 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).