[Astro] Content Collectionsとマークダウンマイグレーション

2026-03-26 hit count image

AstroのContent Collectionsを利用してブログコンテンツを管理する方法と、Jekyllからマークダウンファイルをマイグレーションする過程を共有します。

astro

概要

前回の記事Astroのインストールとプロジェクト構成でプロジェクトの基本構成を完了しました。今回の記事ではAstroのコア機能の1つであるContent Collectionsを利用してブログコンテンツを管理する方法と、Jekyllからマークダウンファイルをマイグレーションする過程を説明します。

Content Collectionsとは

Content CollectionsAstroでマークダウン、MDXなどのコンテンツファイルを構造化して管理する機能です。主な特徴は以下の通りです。

  • 型安全性Zodスキーマでfrontmatterを検証してビルド時にエラーを検出
  • 自動型生成:TypeScriptの型が自動生成されIDEでオートコンプリートをサポート
  • 柔軟なローダー:globパターンでファイルを柔軟にロード
  • ビルトインAPIgetCollection()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 };

スキーマフィールドの説明

スキーマの各フィールドは以下のような役割を持ちます。

フィールドタイプ必須説明
titlestringOポストタイトル
descriptionstringOSEO用説明(160文字以内推奨)
langenumO言語コード(jakoen
categorystringOカテゴリslug(例:astroreact
permalinkstringOURLパス(例:/astro/installation/
imagestring-代表画像パス
commentsboolean-コメント表示有無(デフォルト:true
datedateO作成日
publishedboolean-公開有無(デフォルト: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を比較すると以下のような違いがあります。

項目JekyllAstro
レイアウトlayout: post不要(ページでレイアウトを指定)
カテゴリcategories: jekyllcategory: 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でアクセスできます。

完了

今回の記事ではAstroContent Collectionsを利用してブログコンテンツを管理する方法を見てきました。

  • src/content.config.tsZodスキーマによりfrontmatterを型安全に管理
  • YYYY-MM-DD-slug/index-{lang}.mdネーミングで多言語ポストを構造化
  • JekyllのfrontmatterからAstroのfrontmatterへの変換ポイント
  • categories.tsでカテゴリを一元管理
  • getCollection() APIでコンテンツクエリおよびフィルタリング

次の記事多言語(i18n)実装では、日本語/韓国語/英語3言語をサポートする多言語システムの実装方法を扱います。

シリーズ案内

このポストはJekyllからAstroへのマイグレーションシリーズの一部です。

  1. JekyllからAstroへマイグレーションした理由
  2. Astroのインストールとプロジェクト構成
  3. Content Collectionsとマークダウンマイグレーション
  4. 多言語(i18n)実装
  5. SEO実装
  6. 画像最適化 — カスタムrehypeプラグイン
  7. コメントシステム(Utterances)
  8. 広告連携(Google AdSense)
  9. Pagefindを利用した検索実装
  10. レイアウトとコンポーネントアーキテクチャ
  11. GitHub Pagesデプロイ
  12. ソーシャル共有自動化スクリプト
  13. トラブルシューティングとヒント

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

アプリ広報

今見てるブログを作成たDekuが開発したアプリを使ってみてください。
Dekuが開発したアプリはFlutterで開発されています。

興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。



SHARE
Twitter Facebook RSS