or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-remark-toc

remark plugin to generate a table of contents (TOC)

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/remark-toc@9.0.x

To install, run

npx @tessl/cli install tessl/npm-remark-toc@9.0.0

index.mddocs/

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.