0

Table of Contents Generator

Paste Markdown or HTML and get a nested, clickable table of contents with real GitHub-style collision-safe anchor slugs, plus the original content with heading IDs injected.

Auto-detect looks for real

-

tags first, then Markdown # headings.

Adds a {#slug} attribute after each heading - supported by kramdown, Pandoc, MkDocs and GitHub Pages (Jekyll).

Buy Me a Coffee at ko-fi.com
Processing... 0%
Detecting content format
Parsing headings
Assigning collision-safe slugs
Building table of contents

Result

A table of contents is only as useful as the anchor links underneath it, and anchor links are only reliable if every heading gets a unique, predictable slug. This generator parses the headings out of whatever you paste - Markdown or HTML - and assigns each one a real GitHub-style anchor: lowercase text, spaces and punctuation replaced or stripped, and, critically, a genuine collision-safe suffix whenever two headings would otherwise produce the same slug. The second "Introduction" in a document becomes introduction-1, the third becomes introduction-2, tracked with a running occurrence map exactly the way GitHub's own slugger works - not a shortcut that silently overwrites earlier links when headings repeat, which is the single most common way hand-rolled table-of-contents scripts break.

Markdown parsing is line-based on purpose, because the classic failure mode for a naive implementation is treating a "#" written inside a fenced code block as a heading. This tool tracks whether a ``` or ~~~ fence is currently open as it walks the document line by line, and only recognizes ATX headings (# through ######) outside of open fences - so a code sample showing shell comments or a Python "# comment" never pollutes your table of contents. HTML input takes a different, equally real path: it's parsed with the browser's own DOMParser, headings are selected by tag name, and each one's id attribute is set directly on the actual element before the document is serialized back to a string.

Two things come out the other end. First, the table of contents itself, exported as both a nested Markdown list (- [Title](#slug), indented by heading level) and a nested HTML list (<ul><li><a href="#slug">), each with its own copy button. Second, your original content with the anchors actually wired in: for Markdown input you can choose the widely supported {#slug} heading-attribute syntax used by kramdown, Pandoc, MkDocs, and GitHub Pages' Jekyll, or fall back to plain HTML heading tags with id="slug" already set if your target renderer doesn't understand attribute syntax. For HTML input, the id is injected straight into the parsed heading element - there's no separate "mode" to pick because the tags already exist.

Everything runs locally in your browser: the parsing, the slug math, and the document reassembly all happen in JavaScript on the page you're looking at right now, with nothing uploaded anywhere. That matters for draft documentation, internal wikis, or an unpublished blog post you're not ready to paste into a third-party service just to get a working table of contents.