Astro Content Collections と Zod で frontmatter を型安全にする
フロントエンド
Markdown でブログを書いていると、frontmatter の書き間違いが地味に効いてくる。publishDate を pubDate と書いてしまったり、日付のフォーマットがバラついたり。Astro の Content Collections を使えば、これを Zod スキーマで型として縛れる。
スキーマを定義する
Astro 5 以降、コレクションの設定は src/content.config.ts に置く。glob() ローダーで Markdown を読み込み、schema に Zod を書く。
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.md' }),
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
publishDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
heroImage: z.optional(image()),
}),
});
export const collections = { blog };ポイントをいくつか。
z.coerce.date()にしておくと、'2026-06-29'のような文字列を自動でDateに変換してくれる。tagsは.default([])で省略可能にしている。draftを持たせておくと、下書きを一覧や RSS から除外できる。
下書きを隠す
draft: true の記事を出し分けるには、getCollection の第2引数でフィルタするだけでよい。
const posts = await getCollection('blog', ({ data }) => !data.draft);一覧ページ・個別ページ・RSS のすべてで同じフィルタをかけておけば、公開判断は frontmatter の draft フラグを切り替えるだけになる。
型が効く安心感
スキーマを定義しておくと、post.data.publishDate は Date 型として扱えるし、存在しないフィールドを参照すればエディタが警告してくれる。「仕様書を書く感覚でコンテンツ構造を設計できる」のがこの仕組みのいいところだ。


