16. ブログ記事(中央カラム)の冒頭にindexをつける
・ パソコンではすべて表示し、スマホでは折りたたんで表示する
・ パソコンでは右カラムに表示し、スクロールに追従する方式をとっているサイトもあるが、個人的には気が散るので嫌い。あと、ブログ記事に近い方がSEO的には良い
まず、TOCのComponentsをつくる
Next.js&Typescriptではコンポーネントはキャメルケースでファイル名を書き、それ以外はケバブケースでファイル名を書くのが一般的(Nextjs公式もそうなっている)
components/table-of-contents.tsx
import Link from "next/link";
type Heading = {
id: string;
text: string;
};
type TableOfContentsProps = {
headings: Heading[];
};
export function TableOfContents({
headings,
}: TableOfContentsProps) {
if (headings.length === 0) {
return null;
}
return (
<nav className="rounded-xl border border-stone-200 bg-stone-50 p-5 dark:border-stone-700 dark:bg-stone-900">
<h2 className="mb-4 text-lg font-semibold">
Table of Contents
</h2>
<ul className="space-y-2">
{headings.map((heading) => (
<li key={heading.id}>
<Link
href={`#${heading.id}`}
className="text-sm leading-6 text-stone-600 hover:text-stone-900 dark:text-stone-400 dark:hover:text-stone-100"
>
{heading.text}
</Link>
</li>
))}
</ul>
</nav>
);
}lib/posts.ts
MDXファイルを読み込み、MDXを解釈するファイル
ここで##というMarkdownに反応してH2を抽出する処理を行う
まずはPostの下あたりに次のコードを追加
export type Heading = {
id: string;
text: string;
};そして、getPostsByCategory)0の下あたりに次のコードを追加
export function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, "")
.replace(/\s+/g, "-");
}
export function getHeadings(content: string): Heading[] {
const headings: Heading[] = [];
const regex = /^## (.+)$/gm;
let match;
while ((match = regex.exec(content)) !== null) {
const text = match[1].trim();
const id = text
.toLowerCase()
.replace(/[^\w\s-]/g, "")
.replace(/\s+/g, "-");
headings.push({
id,
text,
});
}
return headings;
}これでたとえばMDXに
## What is Japanese Pension System?
本文。。。
## Who needs to join?
本文。。。とあった場合、
What is Japanese Pension System?
Who needs to join?H2だけが抽出される
app/posts/[slug]/page.tsx
変更前
import { formatDate, getAllPosts, getPostBySlug } from "@/lib/posts";
変更後
import {
formatDate,
getAllPosts,
getHeadings,
getPostBySlug,
} from "@/lib/posts";
import { TableOfContents } from "@/components/table-of-contents";
変更前
const post = getPostBySlug(slug);
変更後
const headings = getHeadings(post.content);PostContentの下に追加
<TableOfContents headings={headings} />残りをどうやったか忘れたけど、なんかできた。
触ったのは上記3つのファイルだけ。
見出しが日本語だとエラーが出るので、英語にしたらエラーがなおった(どうやら、libs/postのreplace(正規表現)の部分が英語しか考慮していない書き方だったらしく、日本語をすべて消去してしまうかららしい)
BACK