Astro Content Collections でブログを構築する
Astro ブログ フロントエンド
Markdownを書くだけでブログができる。frontmatterのtypoや必須項目の漏れはビルド時に検出されるので、本番に載せてから気づく失敗を防げる。必要なのは3つのファイルだけ。
1. スキーマを定義する
src/content/config.ts を作成する。
import { defineCollection, z } from "astro:content";
const blog = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string()).default([]),
}),
});
export const collections = { blog };
z.coerce.date() で「2025-02-15」形式を Date 型に変換する。toLocaleDateString() や getTime() が使える。
2. 記事を書く
src/content/blog/hello.mdx を作成する。
---
title: はじめての記事
description: テスト用の記事です。
pubDate: 2025-02-15
tags: ["Astro"]
---
本文。
3. 一覧ページと詳細ページ
一覧(src/pages/blog/index.astro)
---
import { getCollection } from "astro:content";
const entries = await getCollection("blog");
const sorted = entries.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
---
<ul>
{sorted.map((entry) => (
<li>
<a href={`/blog/${entry.slug}/`}>{entry.data.title}</a>
<time>{entry.data.pubDate.toLocaleDateString("ja-JP")}</time>
</li>
))}
</ul>
詳細(src/pages/blog/[...slug].astro)
---
import { getCollection } from "astro:content";
export async function getStaticPaths() {
const entries = await getCollection("blog");
return entries.map((entry) => ({
params: { slug: entry.slug },
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<article>
<h1>{entry.data.title}</h1>
<time datetime={entry.data.pubDate.toISOString()}>
{entry.data.pubDate.toLocaleDateString("ja-JP", { year: "numeric", month: "long", day: "numeric" })}
</time>
<Content />
</article>
entry.slug はファイル名から自動生成される(hello.mdx → hello)。MDX 内で Callout や Table を使う場合は、<Content components={mdxComponents} /> でコンポーネントを渡す。
型安全のメリット
titleやdescriptionを忘れるとビルドエラー- 日付の形式が混在するとパースエラー
tagsを文字列で書くと型エラー(配列必須)
よくある落とし穴
- slug を変えたい — frontmatter に
slugを定義し、スキーマでslug: z.string().optional()にする - 複数コレクション —
blogとblog-enを分ける場合、collectionsに両方登録 - MDX のコンポーネント —
<Content components={mdxComponents} />で Callout などを渡す