Next.jsでブログ記事に目次を実装する（Tocbot使用）

# はじめに
ブログサイトを作る過程で学んだことを、備忘録目的で投稿しています。
自身は駆け出しエンジニアであり、React自体がほぼ初学者のため、誤った認識・理解をしている可能性があります。
万が一参考にする場合は、上記の点を考慮した上でご一読ください。
また、スタイリングについては割愛しています。

この記事は、過去に投稿した[Next.js × microCMSでブログサイト作成した際に学んだことを書き留める](https://qiita.com/miyaayim_eng/items/62f4267280a9f3cfaa4d)のプロジェクトを元に説明しています。

# 目的
記事詳細ページにて、Qiitaのような目次を実装する。

# 作業環境
Windows10 Pro x64
Node.js: 20.14.0
npm: 10.3.0
Next.js: 14.2.3（App Router使用）

# 手順
実装にあたり`Tocbot`というライブラリを使用します。

https://tscanlin.github.io/tocbot/

まずはインストールします。
```console
npm install --save tocbot
```

`Tocbot`を呼び出すファイルと、それを使用する`TocBox`コンポーネントを作成します。
`useEffect`を使用するためサーバーレンダリングではなく、クライアントレンダリングにする必要があります。（`"use client"`）
下記だと、`.toc`の子要素に`<nav className="toc"></nav>`が追加されます。
```/src/libs/tocbot.js
"use client";

import { useEffect } from "react";
import tocbot from "tocbot";

export const Toc = () => {
  useEffect(() => {
    tocbot.init({
      tocSelector: ".toc", // 目次を追加する class 名
      contentSelector: ".target-toc", // 目次を取得するコンテンツの class 名
      headingSelector: "h2, h3, h4", // 目次として取得する見出しタグ
      headingsOffset: 100, // 見出しのオフセット
      scrollSmoothOffset: -40, //スムーススクロールのオフセット
    });

    // 不要となったtocbotインスタンスを削除
    return () => tocbot.destroy();
  }, []);

  return <nav className="toc" />;
};

```
```/src/components/TocBox/index.jsx
import styles from "./index.module.scss";
import { Toc } from "@/libs/tocbot";

export const TocBox = () => {
  return (
    <>
      <p className={styles.title}>目次</p>
      <Toc />
    </>
  );
};

```


作成した`TocBox`コンポーネントを詳細ページで呼び出します。
```/src/app/articles/[slug]/page.jsx
import Image from "next/image";
import parse from "html-react-parser";
import styles from "./page.module.scss";
import { getArticlesDetail } from "@/libs/microcms";
import { TocBox } from "@/components/TocBox"; // 追加

export default async function Page({ params }) {
  const article = await getArticlesDetail(params.slug);
  return (
    <main className={styles.main}>
      <div className={styles.container}>
        <div className={styles.article}>
          <p className={styles.thumbnail}>
            <Image
              src={article.thumbnail.url}
              alt={article.title}
              height={article.thumbnail.height}
              width={article.thumbnail.width}
              priority
            />
          </p>
          <h1 className={styles.title}>{article.title}</h1>
          <p className={styles.category}>{article.category.name}</p>
          <div className={`${styles.body} target-toc`}>
            {parse(article.body)}
          </div>
        </div>

        {/* 追加 */}
        <div className={styles.toc}>
          <TocBox />
        </div>

      </div>
    </main>
  );
}

```

記事を投稿し、確認してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310435/1e577adc-f811-4442-3131-0d588fb78719.png)

目次が設置できました。
要素を検証すると、見出しレベル毎にクラス名が割り振られていたり（例：`node-name--H2`）、アクティブの見出しにもクラス名が付与されていたりなど（例：is-active-link）、スタイル調整がしやすくなっています。
また、目次内のリンクをクリックすると、該当箇所までスムーススクロールしてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310435/8a650d56-7ef4-3264-e04b-4e8943a7e0d2.png)

# さいごに
理想は目次もSSGに対応させたかったのですが、今の自分の力量では実現できず断念しました。
`Tocbot`はオプションも充実しているのでとても使いやすかったです、ぜひ使用してみてください、


# 参考
https://tscanlin.github.io/tocbot/

https://zenn.dev/yyykms123/articles/2023-12-03-adding-toc-to-nextjs-blog-using-tocbot

https://gizanbeak.com/post/tocbot

https://inari-tech.net/posts/zenn-toc-tocbot

https://www.itti.jp/web-design/advantages-and-disadvantages-of-having-a-table-of-contents-in-your-blog/#chapter-5