# rehype-custom-toc

> rehype plugin to generate a customizable table of contents

Latest version **1.1.10** (published 2026-11-06) · MIT license · 0 weekly downloads

## Install

```sh
npm install rehype-custom-toc
pnpm add rehype-custom-toc
yarn add rehype-custom-toc
bun add rehype-custom-toc
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.1.10 |
| Published | 2026-11-06 |
| First published | 2026-01-11 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 8 |
| Unpacked size | 14.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 0 |
| Author | Robot-Inventor |
| Maintainers | robot-inventor |
| Keywords | hast, html, markdown, rehype, toc, unified |

## Links

- npm: https://www.npmjs.com/package/rehype-custom-toc
- Repository: https://github.com/Robot-Inventor/rehype-custom-toc
- Homepage: https://github.com/Robot-Inventor/rehype-custom-toc#readme
- Issues: https://github.com/Robot-Inventor/rehype-custom-toc/issues
- npm.io page: https://npm.io/package/rehype-custom-toc

## Dependencies (8)

- [unified](https://npm.io/package/unified.md) ^11.0.5
- [hastscript](https://npm.io/package/hastscript.md) ^9.0.3
- [github-slugger](https://npm.io/package/github-slugger.md) ^2.0.0
- [unist-util-visit](https://npm.io/package/unist-util-visit.md) ^5.2.0
- [hast-util-to-html](https://npm.io/package/hast-util-to-html.md) ^9.0.6
- [hast-util-to-text](https://npm.io/package/hast-util-to-text.md) ^4.0.3
- [hast-util-from-html](https://npm.io/package/hast-util-from-html.md) ^2.1.0
- [unist-util-visit-parents](https://npm.io/package/unist-util-visit-parents.md) ^6.1.0

## Alternatives

- [@tsparticles/shape-image](https://npm.io/package/@tsparticles/shape-image.md) — 303.7K weekly downloads
- [@tsparticles/shape-line](https://npm.io/package/@tsparticles/shape-line.md) — 233.7K weekly downloads
- [stringify-attributes](https://npm.io/package/stringify-attributes.md) — 58.6K weekly downloads
- [mobile-drag-drop](https://npm.io/package/mobile-drag-drop.md) — 46.3K weekly downloads
- [@comunica/actor-rdf-parse-html](https://npm.io/package/@comunica/actor-rdf-parse-html.md) — 29.2K weekly downloads

## Recent versions

- 1.1.10 (latest) — 2026-11-06
- 1.1.9 — 2026-11-03
- 1.1.8 — 2026-10-30
- 1.1.7 — 2026-10-28
- 1.1.6 — 2026-10-25
- 1.1.5 — 2026-10-21
- 1.1.4 — 2026-09-22
- 1.1.3 — 2026-08-12
- 1.1.2 — 2026-08-11
- 1.1.1 — 2026-07-30
- 1.1.0 — 2026-07-09
- 1.0.7 — 2026-07-07
- 1.0.6 — 2026-06-19
- 1.0.5 — 2026-03-26
- 1.0.4 — 2026-03-26
- … 5 more at https://npm.io/package/rehype-custom-toc/versions

## README

# rehype-custom-toc

rehype plugin to generate a customizable table of contents. If you are using Astro, you can use the convenient wrapper [astro-custom-toc](https://github.com/Robot-Inventor/astro-custom-toc).

## Installation

```bash
npm install rehype-custom-toc
```

## Example usage

The table of contents will be inserted at the location of the `<!-- toc -->` comment or at the beginning of the file if no comment is found.

When parsing HTML with rehype, the `<!-- toc -->` comment is preserved as-is, but when converting from Markdown you need to convert Markdown comments to HTML comments (as shown below with `remark-comment`) so rehype-custom-toc can detect the TOC marker.

```typescript
import remarkComment from "remark-comment";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeCustomToc from "rehype-custom-toc";
import rehypeStringify from "rehype-stringify";
import { unified } from "unified";

/**
 * A comment node generated by the `remark-comment` plugin.
 */
interface Comment extends Node {
    type: "comment";
    value: "";
    commentValue: string;
}

const processor = unified()
    .use(remarkParse)
    .use(remarkComment, {
        ast: true
    })
    .use(remarkRehype, {
        handlers: {
            /**
             * Convert a mdast comment node to a hast comment node.
             * @param _ State
             * @param node mdast comment node
             * @returns hast comment node
             */
            comment: (_, node: Comment) => ({
                type: "comment",
                value: node.commentValue
            })
        }
    })
    .use(rehypeCustomToc)
    .use(rehypeStringify);

const markdown = `
# Title

This is a sample markdown paragraph.

<!-- toc -->

## Section 1

### Subsection 1.1
`;

processor.process(markdown).then((result) => {
    console.log(result.toString());
});
```

The above code will output the following (formatted for readability):

```html
<h1 id="title">Title</h1>
<p>This is a sample markdown paragraph.</p>
<aside class="toc">
    <h2>Contents</h2>
    <nav>
        <ul>
            <li><a href="#title">Title</a></li>
            <ul>
                <li><a href="#section-1">Section 1</a></li>
                <ul>
                    <li><a href="#subsection-11">Subsection 1.1</a></li>
                </ul>
            </ul>
        </ul>
    </nav>
</aside>
<h2 id="section-1">Section 1</h2>
<h3 id="subsection-11">Subsection 1.1</h3>
```

### Add IDs to headings without a TOC

You can import `rehypeSlugger` from `rehype-custom-toc` and use it as a standalone plugin to add slugs to headings without generating a table of contents. This is useful if you want to add IDs to headings but don't want a TOC.

```typescript
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import { rehypeSlugger } from "rehype-custom-toc";
import rehypeStringify from "rehype-stringify";
import { unified } from "unified";

const processor = unified().use(remarkParse).use(remarkRehype).use(rehypeSlugger).use(rehypeStringify);

const markdown = `
# Title

This is a sample markdown paragraph.

## Section 1

### Subsection 1.1
`;

processor.process(markdown).then((result) => {
    console.log(result.toString());
});
```

The above code will output the following (formatted for readability):

```html
<h1 id="title">Title</h1>
<p>This is a sample markdown paragraph.</p>
<h2 id="section-1">Section 1</h2>
<h3 id="subsection-11">Subsection 1.1</h3>
```

## Options

### `template`

A function that takes the generated HTML and returns the final HTML. This can be used to wrap the generated HTML in a custom template.

Default:

```javascript
const defaultTemplate = (html) => {
    return `
<aside class="toc">
    <h2>Contents</h2>
    <nav>
        ${html}
    </nav>
</aside>`.trim();
};
```

### `maxDepth`

The maximum depth of headings to include in the table of contents.

Default: `3`

### `ordered`

Whether to use an ordered list (`<ol>`) or an unordered list (`<ul>`).

Default: `false`

---
_Source: https://npm.io/package/rehype-custom-toc · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
