← Back to blog

4 Ways to Add a Markdown Table of Contents, Chosen by File Count

September 5, 2026
4 Ways to Add a Markdown Table of Contents, Chosen by File Count

For a single README, open GitHub's Outline menu to browse without building anything. For a static doc you plan to share as one file, paste a generator's output straight into the file. For a repo with many Markdown files or multiple maintainers, automate the job with a CLI tool like markdown-toc or a render-time plugin like remark-toc. Pick based on how many files you touch and who else edits them.


TL;DR:

  • Use the correct slug style matching your hosting platform before automating TOC generation to prevent broken links caused by renderer mismatches.
  • For a single file or static document, manual or online generators are sufficient, but larger projects benefit from CLI tools or render-time plugins.
  • Automate TOC updates with CLI tools like markdown-toc or md-toc when managing multiple files or teams to avoid stale links.
  • Static site generators automatically insert live TOCs during builds, ensuring anchor consistency across heading changes.
  • Always test anchor links after any change by clicking or inspecting the HTML to catch issues from duplicate headings, punctuation, or skipped levels.

Table of Contents

Quick Options for a Markdown Table of Contents

Four approaches cover almost every situation, and picking the right one comes down to file count and who maintains the document.

  • Manual list: You write bullet links by hand, pointing at heading anchors. Best for a single, rarely-changing README.
  • Online generator: Paste your Markdown in, copy a formatted TOC out. Best for one-off files or quick static docs with no build pipeline.
  • CLI tool: Something like markdown-toc or md-toc scans your file and injects or updates the list automatically. Best for repos with several docs or frequent heading changes.
  • Render-time plugin: Tools like remark-toc or a markdown-it plugin generate the TOC when the site builds. Best for documentation sites where headings change often and consistency with the renderer's anchors matters most.

Once you know which bucket you're in, the next step is concrete: copy the generated block, run the install command, or drop the plugin into your build config.

Pro Tip: If you're not sure which camp you fall into, count your Markdown files. One file, no build step: go manual or generator. Five or more files, or a build pipeline already in place: go CLI or plugin.

How Do You Write a Manual Table of Contents in Markdown?

A manual TOC still earns its place in single-file READMEs, offline PDFs, and anywhere the document travels outside its original hosting platform, since a pasted TOC works even when a viewer has no outline feature at all. Here's how to build one that actually matches your rendered anchors.

  1. Place it after your badges and title, before your first real section. That's the convention most READMEs follow, and it's what readers expect.
  2. List each heading as a bullet, indented by heading level. H2s at the top level, H3s nested one level in.
  3. Convert each heading to a slug: lowercase it, replace spaces with hyphens, and strip punctuation. "How It Works?" becomes how-it-works.
  4. Link each bullet to #your-slug. Test by clicking it after rendering.
  5. Wrap the whole block in <!-- toc --> and <!-- tocstop --> comments if you might automate updates later, a convention the markdown-toc README recommends specifically so in-place tools know where to insert or replace content.

A simple example:

- [Installation](#installation)
- [Usage](#usage)
  - [Basic Commands](#basic-commands)
  - [Advanced Flags](#advanced-flags)
- [Contributing](#contributing)

Manual TOCs break the moment someone renames a heading and forgets to update the link, which is the main argument for automating this once your file count grows.

When Should You Use an Online Table of Contents Generator?

An online generator earns its keep when you have one file, no build pipeline, and want a finished result in under a minute. You paste your Markdown, the tool parses every heading, and it spits out a nested bullet list with anchors already built.

Watch three settings before you copy the output:

  • Slug style. GitHub, GitLab, and plain CommonMark each slugify headings slightly differently, so confirm the generator matches wherever you're publishing.
  • Depth. Most generators let you cap how many heading levels show up. Two or three levels deep is usually the readable limit; deeper than that and the TOC turns into visual noise.
  • Bullet formatting. Some tools output dashes, others asterisks. Match your existing document style.

Run a quick health check before publishing: scan for duplicate headings, skipped levels (an H2 followed directly by an H4), and empty headings, since generators can silently misfire on all three. Good generators flag these issues; tools that skip fenced code blocks and de-duplicate headings avoid the most common source of broken anchors. Reach for a generator over full automation when the file won't change again soon, and reach past it the moment you're maintaining more than one or two documents.

Automate a Markdown TOC With CLI Tools and CI

Once you're maintaining more than a couple of Markdown files, or more than one person edits your docs, hand-editing a table of contents in Markdown stops scaling. Two CLI tools cover most workflows.

  1. markdown-toc (Node.js). Install with npm install markdown-toc, then run markdown-toc -i README.md to inject the TOC in place. Common flags include --maxdepth 3 to cap heading levels and --no-firsth1 to skip the document title, and the tool can also run as a build plugin, per the markdown-toc project.
  2. md-toc (Python). Install with pip install md-toc, then run md_toc github README.md to write the TOC using GitHub's slug rules. Swap github for gitlab or cmark to match your target renderer, and the tool handles header-level selection and anchor de-duplication natively, according to the md-toc documentation.

Both tools respect the <!-- toc --> / <!-- tocstop --> comment markers, so re-running them updates the list without touching the rest of your file.

For repos with multiple contributors, wire the command into a pre-commit hook or a CI step that runs on merge, so nobody ships a stale TOC. Test your setup against two edge cases before trusting it: headings inside fenced code blocks (a good tool skips these) and duplicate heading text (which most tools resolve with numbered suffixes like -1 and -2).

Pro Tip: Match your CLI tool's slug style to your actual hosting platform before you automate anything. Running the GitHub slugifier while publishing on GitLab produces links that look right in the terminal and break in the browser.

Render-Time TOCs for Static Sites and Docs Platforms

Documentation sites built with static-site generators handle tables of contents differently. Instead of a fixed block of text sitting in the file, a plugin scans the rendered heading tree and drops in a live TOC every time the site builds.

The mechanics are simple: you place a placeholder like [TOC] or [[toc]] in your Markdown, and the plugin swaps it out for a nested list at build time.

  • Python-Markdown's TOC extension populates a [TOC] marker and generates anchors that match the same renderer producing your page, according to the Python-Markdown documentation.
  • remark-toc does the equivalent for the remark/MDX ecosystem, scanning your heading tree during the build step.
  • markdown-it-table-of-contents looks for a [[toc]] marker and, paired with the markdown-it-anchor plugin, ensures every heading actually has an anchor ID to link to, per its project documentation.

Most of these plugins expose includeLevel or maxDepth options to control heading depth, plus a skip or prefix setting for excluding specific sections. The advantage over a static, pasted TOC is consistency: the anchors always match whatever the renderer just produced, so you never end up with a TOC pointing at slugs that no longer exist.

Broken anchors almost always trace back to one of four causes, and each has a specific fix.

  • Renderer mismatch. GitHub, GitLab, and plain CommonMark slugify headings differently, so a TOC built for one can misfire on another. Fix: pick a generator or CLI flag that matches your actual hosting platform, since testing anchors against your target renderer prevents most mismatches before they happen.
  • Duplicate headings. Two sections both titled "Overview" get anchors like #overview and #overview-1. Fix: rename one heading, or confirm your tool's de-duplication numbering before linking to it.
  • Punctuation and spacing. Question marks, colons, and extra spaces get stripped differently across renderers. Fix: keep headings simple, or generate the slug and copy it directly rather than guessing.
  • Skipped heading levels. An H2 followed by an H4 confuses some generators' depth logic. Fix: keep your heading hierarchy sequential.

Test any anchor by clicking the rendered link, checking GitHub's Outline view for the actual slug it assigned, or inspecting the page's HTML for the id attribute directly. For deeper syntax quirks around anchors and rendering, Markbin's guide to Markdown table syntax covers related formatting edge cases worth knowing before you publish.

Publishing TOC-Friendly Docs With Markbin

This platform renders full GitHub Flavored Markdown, which covers everything a solid table of contents in Markdown depends on: working heading anchors, tables, task lists, and math formulas, all in one shareable link. You paste your Markdown, including a manual TOC or generator output, and get a rendered, navigable document without setting up a repo or build pipeline.

That makes it a fit for quick internal docs, password-protected drafts, or self-destructing shares for a one-time review. For teams scaling table and heading structures across many files, Markbin's post on Markdown table use cases for docs and teams walks through related patterns worth reusing. If your workflow already includes CI automation for anchor consistency, Arkian's notes on localizing YAML language files cover a similar automation pattern worth borrowing for doc pipelines. Start at Markbin if you just need to get a TOC-friendly document in front of someone today.

The Real Lesson on Markdown Tables of Contents

Most advice on this topic treats the table of contents as one problem with one best tool, and that's backwards. The right method depends entirely on how many files you're touching and who else has to maintain them after you.

The Real Lesson on Markdown Tables of Contents — overview diagram

Where conventional guides fall short is recommending automation to someone with a single README, or recommending manual editing to someone maintaining a twelve-file docs site. Both choices waste time in the wrong direction. A solo maintainer with one file doesn't need a pre-commit hook. A five-person team shipping weekly doc updates absolutely does, because a stale manual TOC is worse than no TOC at all, since it actively points readers to the wrong place.

If you take one thing from this, prioritize matching your slug style to your actual renderer before you automate anything. That single mismatch, GitHub slugs on a GitLab-hosted doc, or CommonMark rules feeding a GitHub Pages site, causes more broken anchors than any other mistake covered here. Get that right first, then decide between manual, generated, or automated based on file count. The tooling matters less than most guides imply. The renderer match matters more.

— Zack

Sources