First Look at Astro 5: What Actually Changed
Astro 5 shipped with a lot of fanfare. “Content Layer,” “Server Islands,” “Vite 6 under the hood.” I spent a week actually using it before writing a word.
Content Layer is Real
The old content collections worked fine for small sites. Content Layer works for large ones. The new loader API lets you pull content from anywhere — a CMS, a database, a remote JSON file — and it all compiles to the same static output. For a site like this, the difference is minimal. For a site with 10,000 posts, it matters.
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.mdx', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
date: z.date(),
category: z.enum(['technical', 'health', 'political', 'food']),
}),
});
Server Islands: Overhyped for Static Sites
Server Islands let you mark a component as server-rendered inside an otherwise static page. Think: a static blog post with a live comment count widget. The concept is sound. For a pure static site like StreetStack, it’s irrelevant. If you’re on static output, skip this whole section.
The Footgun: Date Handling
Frontmatter dates. Astro 5 with Content Layer parses them differently than Astro 4. If you have date: 2026-05-10 in your frontmatter, you now get a Date object, not a string. Any component doing .toLocaleDateString() directly on a string will silently break.
Fix: always convert explicitly.
const formattedDate = new Date(post.data.date).toLocaleDateString('en-US', {
year: 'numeric', month: 'long', day: 'numeric'
});
Verdict
Upgrade if you’re starting fresh. Migration from Astro 4 takes about a day and is mostly mechanical. The DX improvements are real. The performance improvements at scale are real. The marketing hype around Server Islands is aimed at a different use case than static blogs.