AstroでMDファイルに型をつけられるContent Collectionsの導入方法
Astro2.0からMarkdownやMDXファイルに型を設定できるContent Collectionsという機能が追加されました。
contentディレクトリを作成
まず、srcディレクトリの中にcontentディレクトリを作成します。contentディレクトリの中でのみcontent collectionsを使用することが可能です。
複数のcollectionの作成したい場合
contentの中には以下のように複数のcollectionを作成することも可能です。
サブディレクトリにも対応しています。例えば言語対応をするときなどにサブディレクトリで分けてることが出来ます。
Mardownファイルを作成
contentディレクトリ内にMarkdownファイルを作成します。
src
└── content
└── blog
├── test-post-1.md
├── test-post-2.md
└── test-post-3.md
サンプルのMarkdownファイルは以下のようにしておきます。
---
title: "タイトル1"
slug: "my-custom-slug/supports/slashes"
tags: ["tag1", "tag2", "tag3" ]
publishDate: 2023-03-25
image: "/images/sample1.jpg"
---
## 見出しが入ります
本文が入ります。本文が入ります。本文が入ります。本文が入ります。
collectionの型を定義
型を定義するためのcontentディレクトリ直下にconfig.tsというファイルを作成します。
その中で以下のように型を定義していきます。
// 1. ユーティリティをastro:contentからインポートする。
import { z, defineCollection } from 'astro:content';
// 2. 検証したいコレクションごとにスキーマを定義する。
const blogCollection = defineCollection({)
schema: z.object({)
title: z.string()、
slug: z.string()、
tags: z.array(z.string())、
publishDate: z.date()、
image: z.string().optional()、
}),
});
// 3. collectionを登録するために、collectionsオブジェクトをエクスポートする
export const collections = {.
'blog': blogCollection、
};
バリデーションは astro:content
からz
utilityをインポートすることでZodを使用することができます。
記事一覧を表示
getCollectionにコレクション名を指定すると先程のMarkdownファイルのデータが一覧で取得できます。
dataオブジェクトでマークダウン内で指定した値を取得することができます。
src/blogs/index.astro
---
import { getCollection } from 'astro:content';
const blogEntries = await getCollection('blog');
---
<ul>
{blogEntries.map(blogPostEntry => (
<li>
<a href={`/my-blog-url/${blogPostEntry.slug}`}>{blogPostEntry.data.title}</a>
<time datetime={blogPostEntry.data.publishedDate.toISOString()}>
{blogPostEntry.data.publishedDate.toDateString()}
</time>
</li>
))}
</ul>
記事詳細を表示
src/blogsディレクトリ直下に[…slug].astroを作成します。
CollectionEntry<‘blog’>で先程指定してMarkdownの型情報を取得することができます。
また、const { Content } = await entry.render()で本文情報を取得できます。
src/blogs/[…slug].astro
---
import { getCollection,CollectionEntry } from 'astro:content';
export async function getStaticPaths() {
const blogEntries = await getCollection('blog');
return blogEntries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
}
interface Props {
entry: CollectionEntry<'blog'>;
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<h1>{entry.data.title}</h1>
<Content />