Home About

How to generate Toc in Next.js with contentplayer

How to generate Toc in Next.js with contentplayer

in this article, I will show you how to generate a table of contents in Next.js with contentplayer.

What is contentplayer?

contentplayer is a static site generator for Next.js. It is a great tool to build a blog or documentation site. It is easy to use and has a lot of features.

extract toc from markdown file

when using contentplayer, we must create a contentlayer.config.ts file in the root directory. In this file, we can define the content type and the fields of the content type. For example, we can define a Post content type with the following code:

const Post = defineDocumentType(() => ({
  name: "Post",
  filePathPattern: `**/*.mdx`,
  contentType: "mdx",
  fields: {
    title: {
      type: "string",
      description: "The title of the post",
      required: true
    },
    date: {
      type: "date",
      description: "The date of the post",
      required: true
    }
  },
  computedFields: {
    url: {
      type: "string",
      resolve: (doc) => `/posts/${doc._raw.flattenedPath}`
    }
  }
}));

In the above code, we define a Post content type with two fields: title and date. We also define a computed field url to generate the url of the post. for Toc, we can define a computed field toc to extract the toc from the markdown file.

const Post = defineDocumentType(() => ({
  ... other fields
  computedFields: {
    url: {
      type: "string",
      resolve: (doc) => `/posts/${doc._raw.flattenedPath}`
    },
    toc: {
      type: "list",
      resolve: (doc) => {
        return doc.body.raw.match(/\n## .*\n\n/g)?.map((heading) => {
          const title = heading.replace("## ", "").replace(/\n+/, "");
          const id = title.toLowerCase().replace(/ +|\./g, "-");
          return { title, id };
        });
      }
    }
  }
}));

In the above code, we define a computed field toc to extract the toc from the markdown file. We use the match method to extract the headings from the markdown file(contents like ## h2 heading ). Then we use the map method to convert the headings to an array of objects with title and id properties.

render toc in the post page

in the post page, we can use the toc field to render the toc. For example, we can use the toc field to render the toc in the post page.

<ul className="my-4 flex flex-col text-primary">
  {post.toc &&
    post.toc.map(({ title, id }: { title: string; id: string }) => (
      <li key={id}>
        <Link href={`#${id}`}>#{title}</Link>
      </li>
    ))}
</ul>

add anchor to headings

in order to make the toc work, we need to add an anchor to each heading. to achieve this, we need modify mdx-component.tsx file.

const h2 = ({ children }: any): ReactElement => {
  const idText = children.replace(/\s+|\./g, "-").toLowerCase();
  return <h2 id={idText}>{children}</h2>;
};

const components = {
  h2,
  Image
};

export function Mdx({ code }: MdxProps) {
  const Component = useMDXComponent(code);

  return <Component components={components} />;
}

In the above code, we define a h2 component to add an anchor to each heading. We use the replace method to replace the spaces and dots with -. Then we use the toLowerCase method to convert the heading to lowercase. Finally, we use the id attribute to add an anchor to the heading.

Conclusion

in this article, I show you how to generate a table of contents in Next.js with contentplayer. I hope this article is helpful to you.