remark plugin to generate a table of contents (TOC)
npx @tessl/cli install tessl/npm-remark-toc@9.0.00
# remark-toc
1
2
remark-toc is a [remark](https://github.com/remarkjs/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.
3
4
## Package Information
5
6
- **Package Name**: remark-toc
7
- **Package Type**: npm
8
- **Language**: JavaScript (ESM only)
9
- **Installation**: `npm install remark-toc`
10
11
## Core Imports
12
13
```javascript
14
import remarkToc from 'remark-toc'
15
```
16
17
For Deno:
18
19
```javascript
20
import remarkToc from 'https://esm.sh/remark-toc@9'
21
```
22
23
## Basic Usage
24
25
```javascript
26
import {remark} from 'remark'
27
import remarkToc from 'remark-toc'
28
import {read} from 'to-vfile'
29
30
const file = await remark()
31
.use(remarkToc)
32
.process(await read('example.md'))
33
34
console.log(String(file))
35
```
36
37
**Input markdown:**
38
39
```markdown
40
# My Document
41
42
Intro content here.
43
44
## Contents
45
46
## Chapter 1
47
48
Content...
49
50
### Subsection
51
52
More content...
53
54
## Chapter 2
55
56
Final content...
57
```
58
59
**Output markdown:**
60
61
```markdown
62
# My Document
63
64
Intro content here.
65
66
## Contents
67
68
* [Chapter 1](#chapter-1)
69
* [Subsection](#subsection)
70
* [Chapter 2](#chapter-2)
71
72
## Chapter 1
73
74
Content...
75
76
### Subsection
77
78
More content...
79
80
## Chapter 2
81
82
Final content...
83
```
84
85
## Capabilities
86
87
### Table of Contents Generation
88
89
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.
90
91
```javascript { .api }
92
/**
93
* Generate a table of contents (TOC).
94
*
95
* Looks for the first heading matching options.heading (case insensitive),
96
* removes everything between it and an equal or higher next heading, and
97
* replaces that with a list representing the rest of the document structure,
98
* linking to all further headings.
99
*/
100
function remarkToc(options?: Readonly<Options> | null | undefined): Transform;
101
```
102
103
**Usage with options:**
104
105
```javascript
106
import {remark} from 'remark'
107
import remarkToc from 'remark-toc'
108
109
// Custom heading pattern
110
const processor = remark().use(remarkToc, {
111
heading: 'table of contents|overview',
112
maxDepth: 3,
113
tight: false,
114
ordered: true
115
})
116
```
117
118
**Advanced configuration:**
119
120
```javascript
121
import {remark} from 'remark'
122
import remarkToc from 'remark-toc'
123
124
const processor = remark().use(remarkToc, {
125
heading: 'structure',
126
minDepth: 2,
127
maxDepth: 3,
128
skip: 'example|notes?',
129
prefix: 'user-content-',
130
parents: ['listItem', 'root'],
131
tight: false,
132
ordered: true
133
})
134
```
135
136
## Configuration Options
137
138
```javascript { .api }
139
interface Options {
140
/**
141
* Heading to look for, wrapped in new RegExp('^(' + value + ')$', 'i')
142
* @default '(table[ -]of[ -])?contents?|toc'
143
*/
144
heading?: string;
145
146
/**
147
* Max heading depth to include in the table of contents
148
* This is inclusive: when set to 3, level three headings are included (###)
149
* @default 6
150
*/
151
maxDepth?: number;
152
153
/**
154
* Min heading depth to include in the table of contents
155
* This is inclusive: when set to 2, only level two headings and deeper are included (##, ###, etc.)
156
* @default 1
157
*/
158
minDepth?: number;
159
160
/**
161
* Headings to skip, wrapped in new RegExp('^(' + value + ')$', 'i')
162
* Any heading matching this expression will not be present in the table of contents
163
*/
164
skip?: string;
165
166
/**
167
* Allow headings to be children of certain node types
168
* @default tree (root node)
169
*/
170
parents?: Test;
171
172
/**
173
* Whether to compile list items tightly, otherwise space is added around items
174
* @default true
175
*/
176
tight?: boolean;
177
178
/**
179
* Whether to compile list items as an ordered list, otherwise they are unordered
180
* @default false
181
*/
182
ordered?: boolean;
183
184
/**
185
* Add a prefix to links to headings in the table of contents
186
* Useful when later going from markdown to HTML and sanitizing with rehype-sanitize
187
* @example 'user-content-'
188
*/
189
prefix?: string;
190
}
191
192
type Test = string | Function | Object | Array<string | Function | Object>;
193
194
type Transform = (tree: Root) => undefined;
195
196
interface Root {
197
type: 'root';
198
children: Array<any>;
199
}
200
```
201
202
## Examples
203
204
### Custom Heading Pattern
205
206
```javascript
207
import {remark} from 'remark'
208
import remarkToc from 'remark-toc'
209
210
const processor = remark().use(remarkToc, {
211
heading: 'structure'
212
})
213
```
214
215
Searches for headings containing "structure" (case-insensitive).
216
217
### Ordered, Loose List
218
219
```javascript
220
import {remark} from 'remark'
221
import remarkToc from 'remark-toc'
222
223
const processor = remark().use(remarkToc, {
224
ordered: true,
225
tight: false
226
})
227
```
228
229
Generates:
230
231
```markdown
232
1. [History](#history)
233
234
1. [Discovery](#discovery)
235
2. [Name and symbol](#name-and-symbol)
236
3. [Planet X disproved](#planet-x-disproved)
237
238
2. [Orbit](#orbit)
239
```
240
241
### Including and Excluding Headings
242
243
```javascript
244
import {remark} from 'remark'
245
import remarkToc from 'remark-toc'
246
247
const processor = remark().use(remarkToc, {
248
maxDepth: 3,
249
parents: ['listItem', 'root'],
250
skip: 'delta'
251
})
252
```
253
254
- Only includes level 1, 2, and 3 headings
255
- Allows headings directly in list items
256
- Excludes headings with text "delta" (case-insensitive, full match)
257
258
### Setting Heading Depth Range
259
260
```javascript
261
import {remark} from 'remark'
262
import remarkToc from 'remark-toc'
263
264
const processor = remark().use(remarkToc, {
265
minDepth: 2,
266
maxDepth: 4
267
})
268
```
269
270
- Only includes level 2, 3, and 4 headings (##, ###, ####)
271
- Excludes top-level headings (#) from the table of contents
272
- Useful when the main document title should not appear in the TOC
273
274
### Adding Link Prefix
275
276
```javascript
277
import {remark} from 'remark'
278
import remarkToc from 'remark-toc'
279
280
const processor = remark().use(remarkToc, {
281
prefix: 'user-content-'
282
})
283
```
284
285
Generates:
286
287
```markdown
288
* [History](#user-content-history)
289
* [Discovery](#user-content-discovery)
290
* [Name and symbol](#user-content-name-and-symbol)
291
* [Orbit](#user-content-orbit)
292
```
293
294
## Compatibility
295
296
- **Node.js**: Version 16+
297
- **unified**: Version 3+
298
- **remark**: Version 4+
299
- **Package Type**: ESM only
300
301
## Security Considerations
302
303
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:
304
305
```markdown
306
# Contents
307
308
## Bravo<script>alert(1)</script>
309
```
310
311
Becomes:
312
313
```markdown
314
# Contents
315
316
- [Bravo<script>alert(1)</script>](#bravoscriptalert1script)
317
318
## Bravo<script>alert(1)</script>
319
```
320
321
Use [rehype-sanitize](https://github.com/rehypejs/rehype-sanitize) when converting to HTML.