mdtoc: Markdown Table of Contents ☰ with numbering and stable anchor links
generate,stripandcheckwithout turning your Markdown into a moving target.
Table of Contents (click to expand)
-
easy to use:
mdtoc MY.md, single binary, also as VS Code extension- same binary in scripts, CI and as VS Code extension keeps workflow aligned
-
configurable: CLI or edit generated config:
<!-- numbering=true min=2 max=4 slug=github anchor=true link=true toc=true bullets=auto -->
on|offfor numbering, anchor, link, toc- targets ATX headings (min
#to max######) - slug profiles:
github,gitlab,crossnote - auto or explicit (
*,-,+) ToC bullets style
-
move the generated ToC with its container to any place - it will be re-generated there
-
protects non-generated content inside ToC area
- generated content stays clearly separated from authored content
-
works with files and Unix pipes
-
repeated headings support
-
Intentionally ignored:
- Setext:
IGNORED Titleunderlined with===or--- - fenced code blocks:
```txt ## IGNORED Title ``` - HTML comments:
<!-- ## IGNORED Title --> - exclusion regions:
<!-- mdtoc off -->## IGNORED Title<!-- mdtoc on --> - starting space:
␠## IGNORED Title
- Setext:
-
deterministic and idempotent: updates existing ToC
CLI install: code --install-extension rokath.mdtoc or open VS Code, click Extensions, enter mdtoc:
Download a prebuilt binary from GitHub Releases.
Homebrew tap install:
brew install rokath/tap/mdtocgo build ./cmd/mdtocmdtoc --help # show compact CLI usage and commands
mdtoc --verbose # show extended root help with command details
mdtoc <command> -v # show the long help for one commandmdtoc EXAMPLE.md # root mode: update an existing managed container or create a new one
mdtoc generate EXAMPLE.md -a false # new ToC, use CLI or defaults, rewrite the config block
mdtoc check EXAMPLE.md # fail in CI when ToC differs from the reconstructed ToC
cat EXAMPLE.md | mdtoc # dry-run
cat EXAMPLE.md | mdtoc strip > s.md # clear via Unix pipe and write to a different file- Exactly one input source is allowed: piped
stdinor file i/o (with or without--file/-f). - Small CLI note: the Go-style one-dash long form such as
-toc offis currently tolerated, but--toc offremains the documented form.
mdtoc uses an explicit container so generated content is easy to spot and safe to regenerate.
(click to expand)
<!-- mdtoc -->
* [About](#about)
<!-- numbering=true min=2 max=4 slug=github anchor=true link=true toc=true bullets=auto -->
<!-- /mdtoc -->The heading title stays the source of truth. Numbers, inline anchors, and ToC entries are derived from it. With anchor=false, the ToC target follows the rendered heading text because no managed inline anchor exists. Use slug=github|gitlab|crossnote to control the renderer/profile-dependent link type generation.
The optional config block records the settings used for regeneration. generate uses current CLI flags or defaults and then rewrites that block when settings need to be persisted.
This means:
- the stored config is persisted generator input
checkvalidates against regenerated output- changing generation can go through generate, or manual config edits
- repeated-heading links depend on occurrence order (#8)
- Workaround: example
- The
checkcommand does not detect duplicate link anchors. See #97. - By default,
anchor=truegenerates ToC links that reliably work in any environment, but this reduces the readability of the raw Markdown document. A setting likeanchor=false numbering=false slug=crossnotecan work well in some renderers. Switchingnumbering=onthere breaks the link stability promise, though. There is no universally best setting; you have to choose. See also #94. - not a site generator
- not a Markdown formatter
+ READY TO USE +
