目次
概要
前回の記事Astroのインストールとプロジェクト構成でプロジェクトの基本構成を完了しました。今回の記事ではAstroのコア機能の1つであるContent Collectionsを利用してブログコンテンツを管理する方法と、Jekyllからマークダウンファイルをマイグレーションする過程を説明します。
Content Collectionsとは
Content CollectionsはAstroでマークダウン、MDXなどのコンテンツファイルを構造化して管理する機能です。主な特徴は以下の通りです。
- 型安全性:
Zodスキーマでfrontmatterを検証してビルド時にエラーを検出 - 自動型生成:TypeScriptの型が自動生成されIDEでオートコンプリートをサポート
- 柔軟なローダー:globパターンでファイルを柔軟にロード
- ビルトインAPI:
getCollection()、getEntry()などのAPIでコンテンツを簡単にクエリ
スキーマ定義
Content Collectionsのスキーマはsrc/content.config.tsファイルで定義します。このブログで使用しているスキーマは以下の通りです。
// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content' }),
schema: z.object({
title: z.string(),
description: z.string(),
lang: z.enum(['ja', 'ko', 'en']),
category: z.string(),
permalink: z.string(),
image: z.string().optional(),
comments: z.boolean().default(true),
date: z.coerce.date(),
published: z.boolean().default(true),
}),
});
export const collections = { blog };
スキーマフィールドの説明
スキーマの各フィールドは以下のような役割を持ちます。
| フィールド | タイプ | 必須 | 説明 |
|---|---|---|---|
title | string | O | ポストタイトル |
description | string | O | SEO用説明(160文字以内推奨) |
lang | enum | O | 言語コード(ja、ko、en) |
category | string | O | カテゴリslug(例:astro、react) |
permalink | string | O | URLパス(例:/astro/installation/) |
image | string | - | 代表画像パス |
comments | boolean | - | コメント表示有無(デフォルト:true) |
date | date | O | 作成日 |
published | boolean | - | 公開有無(デフォルト:true) |
主要ポイント
globローダー
loader: glob({ pattern: '**/*.md', base: './src/content' }),
src/content/配下のすべての.mdファイルを再帰的にロードします。カテゴリ別のサブディレクトリ構造を自由に構成できます。
z.coerce.date()
date: z.coerce.date(),
z.date()の代わりにz.coerce.date()を使用します。こうすることでfrontmatterに文字列で記述された日付('2026-04-15')を自動的にDateオブジェクトに変換してくれます。Jekyllからマイグレーションする時に日付形式を変更しなくて済むので便利です。
z.enumで言語を制限
lang: z.enum(['ja', 'ko', 'en']),
langフィールドをenumで制限して、サポートしていない言語コードが入力されるとビルド時にエラーが発生します。
ディレクトリ構造とネーミング規則
ディレクトリ構造
ポストファイルはsrc/content/配下にカテゴリ別ディレクトリで分けます。
src/content/
├── astro/
│ ├── 2026-04-01-migration-reason/
│ │ ├── index-ja.md
│ │ ├── index-ko.md
│ │ └── index-en.md
│ └── 2026-04-08-installation/
│ ├── index-ja.md
│ ├── index-ko.md
│ └── index-en.md
├── react/
│ └── 2026-03-03-react-19-migration/
│ ├── index-ja.md
│ ├── index-ko.md
│ └── index-en.md
├── jekyll/
│ └── ...
└── ...
ネーミング規則
ディレクトリとファイルのネーミング規則は以下の通りです。
- ディレクトリ名:
YYYY-MM-DD-slug/形式で日付とスラッグを含む - ファイル名:
index-{lang}.md形式で言語別ファイルを区分index-ja.md— 日本語index-ko.md— 韓国語index-en.md— 英語
この構造の利点は、1つのポストに対する多言語ファイルが同じディレクトリにまとまっていて管理が楽だということです。
JekyllからのFrontmatterマイグレーション
JekyllのFrontmatter
Jekyllで使用していたfrontmatter形式です。
---
layout: post
title: jekyll インストール
description: jekyllブログを始めるために、Mac/Windowsにjekyllをインストールして基本プロジェクトを作成しよう。
image: /assets/images/category/jekyll/install.jpg
categories: jekyll
date: 2018-09-08
---
AstroのFrontmatter
Astroにマイグレーションしたfrontmatter形式です。
---
title: jekyll インストール
description: jekyllブログを始めるために、Mac/Windowsにjekyllをインストールして基本プロジェクトを作成しよう。
lang: ja
category: jekyll
permalink: /jekyll/installation/
date: '2018-09-08'
published: false
comments: true
image: /assets/images/category/jekyll/install.jpg
---
主な変更点
JekyllのfrontmatterとAstroのfrontmatterを比較すると以下のような違いがあります。
| 項目 | Jekyll | Astro |
|---|---|---|
| レイアウト | layout: post | 不要(ページでレイアウトを指定) |
| カテゴリ | categories: jekyll | category: jekyll(単数形) |
| 言語 | プラグインで管理 | lang: ja(明示的) |
| URL | 自動生成 | permalink: /jekyll/installation/(明示的) |
| 日付 | 2018-09-08 | '2018-09-08'(文字列推奨) |
Jekyllではlayoutフィールドで使用するレイアウトを指定していましたが、Astroではページコンポーネント([...path].astro)でレイアウトをimportして使用するため、frontmatterにlayoutフィールドは不要です。
カテゴリシステム
ブログのカテゴリはsrc/data/categories.tsで一元管理します。
// src/data/categories.ts
export type CategoryGroup =
| 'frontend'
| 'backend'
| 'development'
| 'service'
| 'etc';
export interface Category {
slug: string;
group: CategoryGroup;
image: string;
isMainCategory: boolean;
permalink: string;
singlePage: boolean;
searchJson: string;
translations: Record<string, CategoryTranslation>;
}
各カテゴリは以下のような情報を含みます。
- slug:URLとコンテンツディレクトリで使用される識別子
- group:フロントエンド、バックエンド、開発、サービスなどの分類
- translations:日本語/韓国語/英語3言語のタイトル、説明
{
slug: 'astro',
group: 'frontend',
image: '/assets/images/category/astro/background.jpg',
isMainCategory: true,
permalink: '/astro/',
singlePage: false,
searchJson: 'astro',
translations: {
ja: {
title: 'Astro',
description: 'JekyllからAstroへのマイグレーション記録です...',
seeMore: 'もっと見る',
noSearchResult: '検索結果がありません。',
},
ko: {
title: 'Astro',
description: 'JekyllからAstroへのマイグレーション記録です...',
seeMore: '자세히 보기',
noSearchResult: '검색 결과가 없습니다.',
},
en: {
title: 'Astro',
description: 'A record of migrating from Jekyll to Astro...',
seeMore: 'see more',
noSearchResult: 'There is no search result.',
},
},
},
コンテンツクエリ
Content Collectionsに保存されたコンテンツはgetCollection() APIでクエリできます。
基本的な使い方
getCollection() APIは以下のように使用できます。
import { getCollection } from 'astro:content';
// すべてのブログポストを取得
const allPosts = await getCollection('blog');
// 日本語 + 公開済みポストのみフィルタリング
const jaPosts = await getCollection('blog', ({ data }) => {
return data.lang === 'ja' && data.published !== false;
});
カテゴリ別フィルタリング
getCollection()を使用して以下のように特定カテゴリのポストだけを取得することもできます。
// 特定カテゴリのポストを取得
const astroPosts = await getCollection('blog', ({ data }) => {
return (
data.lang === 'ja' && data.category === 'astro' && data.published !== false
);
});
// 日付の降順ソート
const sortedPosts = astroPosts.sort(
(a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
);
ページでの使用
getCollection()は以下のように動的ルーティングページ([...path].astro)でgetStaticPathsと一緒に使用します。
// src/pages/[...path].astro
export async function getStaticPaths() {
const posts = await getCollection('blog', ({ data }) => {
return data.lang === 'ja' && data.published !== false;
});
return posts.map((post) => ({
params: { path: post.data.permalink.slice(1, -1) },
props: { post },
}));
}
permalinkフィールドの前後の/を除去してparams.pathとして使用するのがポイントです。こうすることで/astro/installation/のようなURLでアクセスできます。
完了
今回の記事ではAstroのContent Collectionsを利用してブログコンテンツを管理する方法を見てきました。
src/content.config.tsでZodスキーマによりfrontmatterを型安全に管理YYYY-MM-DD-slug/index-{lang}.mdネーミングで多言語ポストを構造化JekyllのfrontmatterからAstroのfrontmatterへの変換ポイントcategories.tsでカテゴリを一元管理getCollection()APIでコンテンツクエリおよびフィルタリング
次の記事多言語(i18n)実装では、日本語/韓国語/英語3言語をサポートする多言語システムの実装方法を扱います。
シリーズ案内
このポストはJekyllからAstroへのマイグレーションシリーズの一部です。
- JekyllからAstroへマイグレーションした理由
- Astroのインストールとプロジェクト構成
- Content Collectionsとマークダウンマイグレーション
- 多言語(i18n)実装
- SEO実装
- 画像最適化 — カスタムrehypeプラグイン
- コメントシステム(Utterances)
- 広告連携(Google AdSense)
- Pagefindを利用した検索実装
- レイアウトとコンポーネントアーキテクチャ
- GitHub Pagesデプロイ
- ソーシャル共有自動化スクリプト
- トラブルシューティングとヒント
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。