markdown-toc

Generates and injects a markdown table of contents into README files, using Remarkable to parse headings.

Library
npm
v1.2.0
1,755stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
43/100Fair
Development Activity0
Maintenance0
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
62/100Good
Architecture65
Code Quality60
Innovation78
Learning Curve45

markdown-toc is a Node.js library and CLI for generating a table of contents from markdown headings. It parses a markdown file with Remarkable, extracts every heading, and renders a nested bullet list of links, handling repeated heading text, YAML front matter, and headings that appear inside fenced code blocks without mistaking them for real sections.

Beyond the one-line toc() API, it exposes a Remarkable plugin (toc.plugin), a JSON heading tree (toc.json), and an insert() helper that finds <!-- toc --> / <!-- tocstop --> markers in an existing file and rewrites just that block, making it a common build-script dependency for keeping README tables of contents in sync across thousands of open source projects.

What You Get

  • Programmatic API - require('markdown-toc') returns a function that parses a markdown string and returns {content, json, highest, tokens}.
  • In-place TOC injection - toc.insert() finds <!-- toc -->/<!-- tocstop --> markers in a file and rewrites just that block, safe to re-run repeatedly.
  • CLI binary - the bundled markdown-toc command supports -i (edit file in place), --json, --append, --bullets, --maxdepth, and --indent flags for build-script use.
  • Remarkable plugin mode - toc.plugin(options) registers as a Remarkable renderer plugin so TOC generation can be part of a larger markdown rendering pipeline.
  • Customizable slugify/filter/titleize hooks - override how heading text becomes a link, filter out unwanted headings, or reformat heading text for the TOC entry.

Common Use Cases

  • Keeping README TOCs current - a pretest/prepublish npm script runs markdown-toc -i README.md so the table of contents never drifts from the actual headings.
  • Generating docs site navigation - static site generators call the programmatic API to build a heading-based nav sidebar from each markdown source file.
  • Filtering noisy headings - projects whose markdown includes fenced-code examples containing # comments rely on markdown-toc’s code-block-aware parsing to avoid false TOC entries.
  • Custom TOC rendering - tools consume toc.json’s heading token array directly to build a non-bullet-list TOC, such as a dropdown or a JSON sitemap.

Under The Hood

Architecture The package is a thin single-purpose module built around index.js, which overrides a Remarkable renderer’s render method to walk the token stream, collect heading_open tokens, and assemble a JSON heading tree before rendering it back out as a bullet list; lib/insert.js is a separate, front-matter-aware module handling the <!-- toc -->/<!-- tocstop --> marker-splitting logic for in-place file edits, and cli.js is a thin binary wrapper composing index.js and lib/insert.js with minimist-parsed flags. There is no dependency injection or layering beyond this three-module split; data flows one way from a markdown string through the Remarkable override into a token array and finally a bullet string, and the small, isolated surface (toc(), toc.plugin, toc.insert, toc.json) means the renderer-override internals could change with limited blast radius on consumers.

Tech Stack Plain CommonJS Node.js with no build step, no TypeScript, and an engines field pinned to >=0.10.0, reflecting the package’s age. It depends on an old fork of the CommonMark parser (remarkable@^1.7.1) plus a set of small single-purpose utility packages (gray-matter for front matter, list-item for bullet rendering, minimist for CLI parsing, mixin-deep, object.pick, lazy-cache, diacritics-map, markdown-link, repeat-string, strip-color, concat-stream). Dev tooling is limited to mocha for tests and gulp-format-md/verb for generating the README from a template; CI is configured only via a legacy AppVeyor Windows build, with no modern GitHub Actions workflow.

Code Quality The test suite (test/test.js) is fixture-driven, comparing generated TOC output against a test/expected/ directory across many edge cases (front matter, repeated headings, code blocks, custom bullets), which gives reasonable regression coverage for a package this size. There is no type system (plain pre-ES6-style JS using var), and error handling is minimal: the only explicit throw guards against multiple TOC markers in one file, with no other input validation or try/catch around parsing. An .eslintrc.json exists but there’s no CI job enforcing it.

API Design The public API is small and consistent: toc(str, options) for a one-line call, toc.plugin/toc.json/toc.insert for the same operation in different contexts, and utility exports (toc.slugify, toc.linkify, toc.bullets) for consumers who want to build a fully custom renderer. Options (filter, slugify, bullets, maxdepth, firsth1, stripHeadingTags, append) are documented with examples and mirrored 1:1 by CLI flags, so getting started requires close to zero boilerplate — require('markdown-toc')('# H1').content is the entire integration for the common case.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search