How to Generate a Table of Contents in React and Next.js

How to Generate a Table of Contents in React and Next.js
Mridul Panda

Mridul Panda

8 min read

February 26

How to Generate a Table of Contents in React and Next.js

If you're writing long-form blog posts, you might want to include a table of contents to help your readers navigate through your content. In this tutorial, we'll show you how to generate a table of contents automatically in React and Next.js based on the heading tags in your blog. We'll use the remark-toc package, which is a plugin for the unified markdown processor.

What is remark-toc and How Does it Work in React and Next.js?

The remark-toc plugin generates a table of contents from your markdown document based on the heading tags (h1, h2, h3, etc.) in your document. It creates an HTML ul element that contains links to the headings in your document. By default, the plugin generates a table of contents for all headings in the document, but you can customize the plugin to generate a table of contents for specific headings or limit the depth of the table of contents.

Creating a React Component for the Table of Contents in Your Blog Post

To use remark-toc in React and Next.js, we'll create a React component called TableOfContents. The component takes a markdown prop, which is the Markdown content of your blog post. We'll use the useEffect hook to generate a table of contents using remark-toc and update the state of the component with the table of contents items. We'll then render the table of contents as an unordered list.

Optimizing Your Table of Contents Component for SEO in React and Next.js

To optimize the TableOfContents component for SEO, we'll use optimized h2 tags and a meta description. We'll set the heading option of remark-toc to "Table of Contents", which will create an h2 tag with the text "Table of Contents" at the top of the table of contents. We'll also set the maxDepth option of remark-toc to 2, which will limit the depth of the table of contents to h1 and h2 headings only. Finally, we'll include a meta description for the table of contents in the HTML head section of the page.

Code Sample: How to Create a Table of Contents Component in React and Next.js

import React, { useEffect, useState } from "react"; import unified from "unified"; import markdown from "remark-parse"; import toc from "remark-toc"; import { sanitize } from "hast-util-sanitize"; import toHAST from "mdast-util-to-hast"; import hastToString from "hast-util-to-string"; function TableOfContents({ markdown }) { const [toc, setToc] = useState([]); useEffect(() => { // Create a unified processor that can parse markdown const processor = unified().use(markdown); // Add the toc plugin to the processor to generate a table of contents processor.use(toc, { heading: "Table of Contents", tight: true, maxDepth: 2, }); // Parse the markdown and generate a table of contents const tree = processor.parse(markdown); const tocNode = tree.children.find((node) => node.type === "toc"); const tocItems = => { const title = hastToString(toHAST(item)); const slug = item.slug; return { title, slug }; }); setToc(tocItems); }, [markdown]); return ( <nav> <ul> { => ( <li key={item.slug}> <a href={`#${item.slug}`}>{item.title}</a> </li> ))} </ul> </nav> ); } export default TableOfContents;
import TableOfContents from "./TableOfContents"; function MyBlogPost() { const markdown = ` # My Blog Post ## Introduction Lorem ipsum dolor sit amet, consectetur adipiscing elit. ... ## Conclusion Sed ut perspiciatis unde omnis iste natus error sit voluptatem ... `; return ( <> <TableOfContents markdown={markdown} /> <article dangerouslySetInnerHTML={{ __html: markdown }} /> </> ); }

Note for Security

In this example, we create a MyBlogPost component that defines the Markdown content of the blog post and renders the TableOfContents component and the blog post content using dangerouslySetInnerHTML. The dangerouslySetInnerHTML prop is used to render the Markdown content as HTML, which is not recommended for security reasons but is used in this example for brevity.

View Previous Blog


View Next Blog