Type-Safe Content with Astro Content Collections — AstroDemo
One of Astro’s most practical features for content-heavy sites is Content Collections — a built-in system for organising, validating, and querying Markdown and MDX files.
The problem Content Collections solve
Without a schema, Markdown frontmatter is just untyped YAML. A missed field or a date in the wrong format only surfaces as a runtime error — or a missing piece of UI that nobody notices until after launch.
Content Collections fix this by attaching a Zod schema to a folder of files. Astro validates every file at build time, so a broken frontmatter field becomes a build error rather than a silent bug.
Defining a collection
Collections are defined in src/content/config.ts:
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string().max(160),
pubDate: z.coerce.date(),
draft: z.boolean().default(false),
tags: z.array(z.string()).default([]),
}),
});
export const collections = { blog };
Querying posts in a page
In any .astro file you can query the collection with getCollection:
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => !data.draft);
posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
The returned entries are fully typed — your editor will autocomplete data.title, data.pubDate, and all other schema fields.
Dynamic routes
A single src/pages/blog/[slug].astro file handles every post:
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
Astro calls getStaticPaths at build time and generates one HTML file per post — no server required.
Summary
Content Collections turn your Markdown folder into a type-safe content layer. You get schema validation, autocompletion, and static generation all in one built-in feature.