13. left-sidebar.tsxのカテゴリリンクを押すと、カテゴリに属する記事一覧が表示される仕組みをつくる

Cursor

ステップ1:Frontmatterの追加

まず、mdx記事ファイルfrontmatter(MDXファイルの最上部に記述する、タイトルや公開日などのメタデータを載せる部分)にcategoryを加える

---
category: "Taxes"
---

ステップ2:app/categories/[category]/page.tsxをつくる

page.tsx内ではlib/posts.tsの関数getPostsByCategoryとformatDateを使って、特定カテゴリに属する記事一覧を取得し表示する

import Link from "next/link";
import { getPostsByCategory, formatDate } from "@/lib/posts";

type CategoryPageProps = {
  params: Promise<{
    category: string;
  }>;
};

export default async function CategoryPage({
  params,
}: CategoryPageProps) {
  const { category } = await params;
  const posts = getPostsByCategory(category);

  const categoryName =
    category.charAt(0).toUpperCase() + category.slice(1);

  return (
    <div>
      <h1 className="mb-8 text-3xl font-bold">
        {categoryName}
      </h1>

      <div className="space-y-8">
        {posts.map((post) => (
          <article key={post.slug}>
            <Link href={`/posts/${post.slug}`}>
              <h2 className="text-xl font-semibold hover:underline">
                {post.title}
              </h2>
            </Link>

            {post.date && (
              <p className="mt-2 text-sm text-muted-foreground">
                {formatDate(post.date)}
              </p>
            )}

            {post.excerpt && (
              <p className="mt-2 text-sm leading-6 text-muted-foreground">
                {post.excerpt}
              </p>
            )}
          </article>
        ))}
      </div>
    </div>
  );
}

ステップ3:lib/posts.tsの変更

そして、lib/posts.ts(content/posts内のMDXファイルを読み込み、MDXを解釈して、app/layout.tsxに渡す仲介ファイル)を次のように変更する

import fs from "fs";
import path from "path";
import matter from "gray-matter";

export type Post = {
  slug: string;
  title: string;
  date: string;
  excerpt: string;
  category: string;
  content: string;
};

const postsDirectory = path.join(process.cwd(), "content/posts");

export function getPostSlugs(): string[] {
  if (!fs.existsSync(postsDirectory)) {
    return [];
  }

  return fs
    .readdirSync(postsDirectory)
    .filter((file) => file.endsWith(".mdx"))
    .map((file) => file.replace(/\.mdx$/, ""));
}

export function getPostBySlug(slug: string): Post {
  const fullPath = path.join(postsDirectory, `${slug}.mdx`);
  const fileContents = fs.readFileSync(fullPath, "utf8");
  const { data, content } = matter(fileContents);

  return {
    slug,
    title: String(data.title ?? slug),
    date: String(data.date ?? ""),
    excerpt: String(data.excerpt ?? ""),
    category: String(data.category ?? ""),
    content,
  };
}

export function getAllPosts(): Post[] {
  return getPostSlugs()
    .map((slug) => getPostBySlug(slug))
    .sort((a, b) => (a.date < b.date ? 1 : -1));
}

export function getPostsByCategory(category: string): Post[] {
  return getAllPosts().filter(
    (post) => post.category.toLowerCase() === category.toLowerCase()
  );
}

export function formatDate(date: string): string {
  return new Intl.DateTimeFormat("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
  }).format(new Date(date));
}

ステップ4:ローカルで確認

上手く動作するか npm run devで確認する

開発サーバーを停止させるにはControl+C」であり、Command+Cではないので注意

BACK