Astro で Table of Contents(目次)を実装する

はじめに

Astro で構築している当サイトに TOC を実装したくネットである程度調べたが remark や rehype プラグインを使う方法が多かった。しかし、Markdown や MDX で記事を書いている場合、それらのライブラリを使わずによりシンプルに実装できるので紹介がてら書いてみた。

解説

astro:content のユーティリティに含まれる CollectionEntry<TCollectionName> 型が持っている render() メソッドを介して headings プロパティを利用することで TOC(目次) を実現できる。

実装例

以下に簡単な実装例を示す。

./src/pages/blog/index.astro
---
// `/blog/aaa`, `/blog/bbb` などのページを生成する関数
export const getStaticPaths = async () => {
  const blogs = await getCollection("blog");

  return blogs.map((blog) => {
    params: { slug: blog.slug },
    props: { blog }  // 各ページに渡す props を指定する( blog は `CollectionEntry<"blog">` 型)
  });
}

const { blog } = Astro.props;
const { Content, headings } = await blog.render();  // ここで取得できる
---

<div>
    <!-- markup here -->
</div>

このような形で簡単に目次に使いたい要素を Markdown や MDX から取得することが可能だ。

headings プロパティについて

ちなみに render() メソッドから取得できる headings プロパティの型は MarkdownHeading[] で一例を示すとこのような形で取得できる。

## はじめに

### ほげ

#### ふが
[
  { "depth": 2, "slug": "はじめに", "text": "はじめに" },
  { "depth": 3, "slug": "ほげ", "text": "ほげ" },
  { "depth": 4, "slug": "ふが", "text": "ふが" }
]

まとめ

前述した headings プロパティで目次を描画したい情報が取得できればあとは、コンポーネントを作るなり自由に UI を作ればいいだろう。

今回は、remark, rehype プラグインを使わずに TOC(目次)を実装する方法について解説した。詳しくは当サイトのソースコードも置いておくのでもしよければこちらも参照いただければと思う。

参考

API Reference
API Reference favicon docs.astro.build
API Reference