Why I Migrated to Astro v5 Collections
I’ll admit it: I put off upgrading my Astro content collections for a while. The old API worked, and I had a lot of blog posts already humming along in src/content/blog/
. But with Astro v5, the new Content Layer API isn’t just a nice-to-have—it’s the future. Type safety, custom loaders, and a more flexible config? Count me in.
What Changed in v5
Astro v5 introduces a new way to define content collections. Instead of the legacy src/content/config.ts
and type: 'content'
, you now use a root-level src/content.config.ts
and a loader-based approach. This unlocks more power and future-proofs your content.
Key differences:
- Collections are defined in
src/content.config.ts
(not inside a folder). - Use the
loader
property withglob
fromastro/loaders
. - No more
type: 'content'
orentries
.
Step-by-Step Migration
Here’s how I migrated my blog collection:
1. Move Markdown Files (Optional)
I moved my blog posts from the legacy src/content/blog/
to a flatter src/blog/
directory for clarity. You can keep your structure, but I found this cleaner.
mkdir src/blog
mv src/content/blog/*.md src/blog/
2. Create the New Config
I replaced my old config with a new src/content.config.ts
:
import { defineCollection, z } from 'astro:content'
import { glob } from 'astro/loaders'
const blog = defineCollection({
loader: glob({ pattern: '*.md', base: './src/blog' }),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.date(),
updatedDate: z.date().optional(),
image: z
.object({
url: z.string(),
alt: z.string(),
})
.optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
})
export const collections = {
blog,
}
3. Update Your Imports
If you were using the old collections
export, switch to the new getCollection('blog')
API from astro:content
. Luckily, my pages already used this, so no changes were needed.
4. Test Everything
I ran a build and checked my blog index, tag, and post pages. Everything loaded, and Astro’s type safety caught a missing field in one post—exactly what I wanted!
Lessons Learned
- The new loader API is more explicit and flexible.
- Type errors are surfaced early, making content safer for future edits.
- The migration was smoother than expected—most of my code didn’t need to change.
Resources
If you’re still on the legacy API, I highly recommend making the switch. It’s a small investment for a much better developer experience. Let me know if you run into any snags—I’m happy to help!