Table of Contents in a Next.js blog


I recently settled on Next.js as my static site generator of choice. I’ve only been doing frontend development for around a year, so when you think of someone who learned by doing React applications, that’s me.

I’ll probably write more about this another time but with React and these frontend frameworks where repetitiveness is removed via JavaScript (ie. .map(d => <div />)) I’ve never really gotten adept at using CSS classes and figuring out the right thing to abstract. Technologies like Tailwind CSS make a lot more sense to me.

Anywho as part of settling on Next.js I had to setup a blog for this site and Portabella. I was previously using Eleventy but for the reasons above (liking JS too much) I moved to React. I couldn’t find any good ways to generate a table of contents for my posts, so here’s how I do it.

It looks a little bit (with custom styling) like this:


I pull in a bunch of dependencies here and I could probably trim them, but for now it just works which is okay with me.

yarn add gray-matter marked markdown-it markdown-it-anchor markdown-it-toc-done-right

Post page

My posts live under /posts/<slug> so I have a pages/posts/[slug].tsx with the following:

export default function ({ content }) {
  return (
      <div className="post">
        <div dangerouslySetInnerHTML={{ __html: content }} />
  return <Post {...props} />;

export async function getStaticProps(context) {
  return {
    props: await getPostBySlug(context.params.slug),

Render the TOC

The actual meat of this article is here, we’re just leveraging the above libraries to render the table of contents. Our getPostBySlug function looks like this:

export async function getPostBySlug(slug) {
  const fileContent = await import(`../posts/${slug}.md`);
  const meta = matter(fileContent.default);

  const result = md.render(meta.content);
  return {
    content: result,


Now to actually get a table of contents in your posts all you need to do is insert [[toc]] where you want it to go.