Migrating my blog to Astro - Content collections
Content collections are a key feature of Astro. This post describes how I defined a content collection for all the blog posts.
This is part of a series of posts on how I migrated my blog to Astro
The convention I adopted is that all blog posts will live under src/posts
. There’s a subdirectory for each year, and then each individual post’s filename includes the date (which means they end up being sorted in date order).
One of the core concepts of Astro is the idea of content collections. Rather than creating individual custom pages for every post, you can create logical sets of content, and in turn those collections can be queried as part of generating web pages.
Content collections are defined in src/content.config.ts
In my case, I need a content collection which includes all the blog posts. Astro has a glob
loader that can match files which is just the ticket.
import { glob } from "astro/loaders";
import { defineCollection, z } from "astro:content";
import { DateTime } from "luxon";
const blog = defineCollection({
type: "content_layer",
loader: glob({
pattern: "**/*.{md,mdx}",
base: "./src/posts",
generateId: ({ entry, data }) => {
const date = DateTime.fromISO(data.date as string, { setZone: true });
const id = entry.substring(16);
const slug = `${date.toFormat("yyyy")}/${date.toFormat("MM")}/${id}`;
return slug.replace(/\.mdx?$/, '');
},
}),
schema: ({ image }) =>
z.object({
date: z.string().datetime({ offset: true }),
title: z.string(),
draft: z.boolean().optional().default(false),
tags: z.array(z.string()).default(["others"]),
image: image().optional(),
imageAlt: z.string().optional(),
description: z.string().optional(),
}),
});
export const collections = { blog };
I also need to take into account that while the source path of a post might be src/posts/2025/2025-05-13-bbs.md
, the final URL relative URL for that page should be /2025/05/bbs
. That’s the reason for the custom generateId
function.
The schema allows me to define some additional frontmatter properties, that I’ll take advantage of if present when rendering the pages.