CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-remark-toc

remark plugin to generate a table of contents (TOC)

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

remark-toc

remark-toc is a remark plugin that automatically generates table of contents (TOC) for markdown documents. It searches for headings that match a configurable pattern (default: 'Contents' or 'TOC'), removes existing content under that heading, and replaces it with a nested list of links to all subsequent headings in the document.

Package Information

  • Package Name: remark-toc
  • Package Type: npm
  • Language: JavaScript (ESM only)
  • Installation: npm install remark-toc

Core Imports

import remarkToc from 'remark-toc'

For Deno:

import remarkToc from 'https://esm.sh/remark-toc@9'

Basic Usage

import {remark} from 'remark'
import remarkToc from 'remark-toc'
import {read} from 'to-vfile'

const file = await remark()
  .use(remarkToc)
  .process(await read('example.md'))

console.log(String(file))

Input markdown:

# My Document

Intro content here.

## Contents

## Chapter 1

Content...

### Subsection

More content...

## Chapter 2

Final content...

Output markdown:

# My Document

Intro content here.

## Contents

* [Chapter 1](#chapter-1)
  * [Subsection](#subsection)
* [Chapter 2](#chapter-2)

## Chapter 1

Content...

### Subsection

More content...

## Chapter 2

Final content...

Capabilities

Table of Contents Generation

Generates a table of contents by finding a heading that matches a configurable pattern and replacing its content with a structured list of all subsequent headings.

/**
 * Generate a table of contents (TOC).
 * 
 * Looks for the first heading matching options.heading (case insensitive),
 * removes everything between it and an equal or higher next heading, and
 * replaces that with a list representing the rest of the document structure,
 * linking to all further headings.
 */
function remarkToc(options?: Readonly<Options> | null | undefined): Transform;

Usage with options:

import {remark} from 'remark'
import remarkToc from 'remark-toc'

// Custom heading pattern
const processor = remark().use(remarkToc, {
  heading: 'table of contents|overview',
  maxDepth: 3,
  tight: false,
  ordered: true
})

Advanced configuration:

import {remark} from 'remark'
import remarkToc from 'remark-toc'

const processor = remark().use(remarkToc, {
  heading: 'structure',
  minDepth: 2,
  maxDepth: 3,
  skip: 'example|notes?',
  prefix: 'user-content-',
  parents: ['listItem', 'root'],
  tight: false,
  ordered: true
})

Configuration Options

interface Options {
  /** 
   * Heading to look for, wrapped in new RegExp('^(' + value + ')$', 'i')
   * @default '(table[ -]of[ -])?contents?|toc'
   */
  heading?: string;
  
  /** 
   * Max heading depth to include in the table of contents
   * This is inclusive: when set to 3, level three headings are included (###)
   * @default 6
   */
  maxDepth?: number;
  
  /** 
   * Min heading depth to include in the table of contents
   * This is inclusive: when set to 2, only level two headings and deeper are included (##, ###, etc.)
   * @default 1
   */
  minDepth?: number;
  
  /** 
   * Headings to skip, wrapped in new RegExp('^(' + value + ')$', 'i')
   * Any heading matching this expression will not be present in the table of contents
   */
  skip?: string;
  
  /** 
   * Allow headings to be children of certain node types
   * @default tree (root node)
   */
  parents?: Test;
  
  /** 
   * Whether to compile list items tightly, otherwise space is added around items
   * @default true
   */
  tight?: boolean;
  
  /** 
   * Whether to compile list items as an ordered list, otherwise they are unordered
   * @default false
   */
  ordered?: boolean;
  
  /** 
   * Add a prefix to links to headings in the table of contents
   * Useful when later going from markdown to HTML and sanitizing with rehype-sanitize
   * @example 'user-content-'
   */
  prefix?: string;
}

type Test = string | Function | Object | Array<string | Function | Object>;

type Transform = (tree: Root) => undefined;

interface Root {
  type: 'root';
  children: Array<any>;
}

Examples

Custom Heading Pattern

import {remark} from 'remark'
import remarkToc from 'remark-toc'

const processor = remark().use(remarkToc, {
  heading: 'structure'
})

Searches for headings containing "structure" (case-insensitive).

Ordered, Loose List

import {remark} from 'remark'
import remarkToc from 'remark-toc'

const processor = remark().use(remarkToc, {
  ordered: true,
  tight: false
})

Generates:

1. [History](#history)

   1. [Discovery](#discovery)
   2. [Name and symbol](#name-and-symbol)
   3. [Planet X disproved](#planet-x-disproved)

2. [Orbit](#orbit)

Including and Excluding Headings

import {remark} from 'remark'
import remarkToc from 'remark-toc'

const processor = remark().use(remarkToc, {
  maxDepth: 3,
  parents: ['listItem', 'root'],
  skip: 'delta'
})
  • Only includes level 1, 2, and 3 headings
  • Allows headings directly in list items
  • Excludes headings with text "delta" (case-insensitive, full match)

Setting Heading Depth Range

import {remark} from 'remark'
import remarkToc from 'remark-toc'

const processor = remark().use(remarkToc, {
  minDepth: 2,
  maxDepth: 4
})
  • Only includes level 2, 3, and 4 headings (##, ###, ####)
  • Excludes top-level headings (#) from the table of contents
  • Useful when the main document title should not appear in the TOC

Adding Link Prefix

import {remark} from 'remark'
import remarkToc from 'remark-toc'

const processor = remark().use(remarkToc, {
  prefix: 'user-content-'
})

Generates:

* [History](#user-content-history)
  * [Discovery](#user-content-discovery)
  * [Name and symbol](#user-content-name-and-symbol)
* [Orbit](#user-content-orbit)

Compatibility

  • Node.js: Version 16+
  • unified: Version 3+
  • remark: Version 4+
  • Package Type: ESM only

Security Considerations

remark-toc copies existing nodes into the table of contents, which can include script tags or other HTML content. When transforming to HTML, ensure proper sanitization:

# Contents

## Bravo<script>alert(1)</script>

Becomes:

# Contents

-   [Bravo<script>alert(1)</script>](#bravoscriptalert1script)

## Bravo<script>alert(1)</script>

Use rehype-sanitize when converting to HTML.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/remark-toc@9.0.x
Publish Source
CLI
Badge
tessl/npm-remark-toc badge