Next.js × MDX ブログに tocbot でいい感じの Table of Contents を作る
はじめに
tocbot で
tocbot は
本記事では、
環境
- Next.js: v10.0.5
- MDX: v1.6.22
tocbot を追加する
まず
1yarn add tocbot
2// or
3// npm install tocbot
TOC コンポーネントを作る
1import { useEffect } from 'react'
2import tocbot from 'tocbot'
3
4const Toc: React.VFC = () => {
5 useEffect(() => {
6 tocbot.init({
7 tocSelector: '.toc',
8 contentSelector: '.post',
9 headingSelector: 'h1, h2, h3',
10 })
11 }, [])
12
13 return () => tocbot.destroy()
14}
15
16export default Toc
useEffect() で初期化する
useEffect 内でreturn () => tocbot.destroy()
はtocbot.init()
する[]
を
tocbot.init() の基本的なオプション
tocSelector
目次自身を.toc
です。return <div className="toc" />
と
1<div class="toc">
2 <ol class="toc-list">
3 ...
4 </ol>
5</div>
実際の<div class="toc">...</div>
で
contentSelector
目次を.post
です。
1<Layout>
2 <article>
3 <h1 className={styles.headingXl}>{meta.title}</h1>
4 <LightText className={styles.lightText}>
5 {meta.tags.map((t) => (
6 <Tag key={t} tag={t} />
7 ))}
8 <br />
9 <Date date={meta.date} />
10 </LightText>
11 <div className="post">
12 <MDXProvider components={MDXComponents}>{children}</MDXProvider>
13 </div>
14 </article>
15 <ImageOverlay />
16</Layout>
このブログの{children}
で<div className="post">
を
<MDXProvider>
は
headingSelector
どの'h2, h3, h4'
と
heading タグの id 属性に見出し文字列を設定する
目次に
1const MDXComponents: Components = {
2 return (
3 h1: (props) => {
4 return (
5 <h1 id={props.children} className={styles.h1}>
6 {props.children}
7 </h1>
8 )
9 },
10 h2: (props) => {
11 return (
12 <h2 id={props.children} className={styles.h2}>
13 {props.children}
14 </h2>
15 )
16 },
17 h3: (props) => {
18 return (
19 <h3 id={props.children} className={styles.h3}>
20 {props.children}
21 </h3>
22 )
23 },
24 )
25}
1<div className="post">
2 <MDXProvider components={MDXComponents}>{children}</MDXProvider>
3</div>
props.children
に
例えば
1## 見出し
2
3本文です。
4
1<h1 id="見出し" class="mdxComponents_h1__2IZ3_">見出し</h1>
2<p>本文です</p>
これを
1<a href="#見出し" class="toc-link node-name--H1 ">見出し</a>
と
例
手前味噌ながら、
1import { useEffect } from 'react'
2import tocbot from 'tocbot'
3
4export const Toc: React.VFC = () => {
5 useEffect(() => {
6 tocbot.init({
7 tocSelector: '.toc',
8 contentSelector: '.post',
9 headingSelector: 'h1, h2, h3',
10 })
11
12 return () => tocbot.destroy()
13 }, [])
14
15 return (
16 <>
17 <div className="toc" />
18 <style jsx global>{`
19 .toc {
20 background-color: var(--content-bg-primary);
21 border: 1px solid var(--content-border);
22 border-radius: 0.25rem;
23 padding: 1rem;
24 font-size: 0.875rem;
25 }
26
27 .toc-list .toc-list {
28 padding-left: 1rem;
29 padding-top: 0.5rem;
30 }
31
32 .toc-list-item {
33 padding-bottom: 0.5rem;
34 }
35
36 .toc-list-item:last-child {
37 padding-bottom: 0;
38 }
39
40 .toc-link {
41 color: var(--text-secondary);
42 }
43
44 .is-active-link {
45 color: var(--text-primary);
46 font-weight: 700;
47 }
48 `}</style>
49 </>
50 )
51}
色に
まとめ
tocbot.init()
時に目次の DOM と コンテンツの DOM の CSS クラスを 指定する - heading タグに
ちゃんと id 属性を 設定する