Since the stable version of Astro 6.0 has been released, I checked whether this blog needs to be migrated or not.
Contents
This blog is running on Astro 5 series (currently ^5.16.8). The stable version of Astro 6.0 was released on March 10th, so I looked into what has changed and what I need to do if I want to migrate.
Major new features in Astro 6
Development server redesign (Vite Environment API)
astro dev has been completely rebuilt using Vite’s Environment API. This allows code to be executed at the same runtime as production during development.
The biggest beneficiaries are Cloudflare Workers users, who have direct access to bindings like KV, D1, R2, and Durable Objects during development. This blog is deployed to Vercel, so there is little direct benefit, but I welcome the direction in which the problem of “it works locally but breaks when I deploy it” will be reduced.
Fonts API
Added API to automate custom font management.
import { defineConfig, fontProviders } from 'astro/config';
export default defineConfig({
fonts: [{
name: 'Roboto',
cssVariable: '--font-roboto',
provider: fontProviders.fontsource(),
}],
});
On the component side, you can instruct preloading with the <Font> component.
---
import { Font } from 'astro:assets';
---
<Font cssVariable="--font-roboto" preload />
It downloads fonts, caches them, self-hosts them, generates fallbacks, and inserts preload links. It supports multiple providers such as Google Fonts, Fontsource, and Bunny.
Content Security Policy (CSP)
CSP support, which was experimental in Astro 5, has been promoted to stable. It can be used for both static and dynamic pages, regardless of server or serverless environment.
export default defineConfig({
security: {
csp: {
algorithm: 'SHA-512',
directives: ["default-src 'self'"],
},
},
});
The framework automatically hashes all your scripts and styles. Astro is the first to provide embedded CSP in a JavaScript meta framework.
Live Content Collections
Live Content Collections, introduced experimentally in Astro 5.10, is now stable. Content can be retrieved at request time rather than at build time.
import { defineLiveCollection } from 'astro:content';
const updates = defineLiveCollection({
loader: cmsLoader({ apiKey: process.env.MY_API_KEY }),
schema: z.object({ /* ... */ }),
});
Data from CMS and API is reflected immediately without rebuilding. This blog is based on Markdown files, so I won’t use it directly, but it seems useful for sites that collaborate with external CMS.
experimental features
Rust compiler
A Rust compiler was experimentally added as a successor to the Go .astro compiler.
experimental: { rustCompiler: true }
Requires installation of @astrojs/compiler-rs. The purpose is to improve performance, strengthen diagnostics, and improve reliability.
queue rendering
Experimental feature that employs a two-pass method instead of recursive rendering. Initial benchmarks have shown up to twice the rendering speed, and it will become the default in Astro v7.
experimental: {
queuedRendering: { enabled: true },
}
route caching
Platform-independent caching API using web standard caching semantics. Automatically track dependencies between pages and content entries.
import { memoryCache } from 'astro/config';
export default defineConfig({
experimental: {
cache: { provider: memoryCache() },
},
});
You can set the cache policy on the page side using Astro.cache.set(). This feature is used in an SSR environment, so it is not relevant to static sites, but it will be an option if you want to migrate to SSR in the future.
List of breaking changes
Node.js 22 required
Node 18 will be EOL in March 2025, and Node 20 will be EOL in April 2026. Astro 6 now requires Node 22.12.0 or higher.
このブログのプロジェクトはVoltaでnode: "22.22.0"を指定しているので問題なし。 You also need to check the Node settings on the Vercel side.
Vite 7
Astro itself and all @astrojs packages have been migrated to Vite 7. If you have fixed the Vite version by custom, you need to update to v7 or higher. Vitest users require v3.2 or higher.
This blog is vitest: "^4.0.18", so there is no problem.
Zod 4
Zod, which is used for content schema validation, is now v4. Main changes:
| Zod 3 | Zod 4 |
|---|---|
z.string().email() | z.email() |
.min(5, { message: "..." }) | .min(5, { error: "..." }) |
.transform(Number).default("0") | .transform(Number).default(0) — default is the type after conversion |
If you are importing Zod from astro:content, you need to change it to astro/zod.
Shiki 4
Code highlighting Shiki is now v4. It may affect <Code /> components and Markdown code blocks. If you are directly using Shiki-specific APIs, you need to check the migration guide.
Legacy content collection retirement
A complete migration to the Content Layer API introduced in Astro 5.0 is now mandatory. Legacy collections (those using type: 'content') are not supported.
Migration work:
- Rename
src/content/config.ts→src/content.config.ts - Add
loaderto collection definition (usingglob()) - Remove declaration of
type: 'content' getEntryBySlug()→getEntry()post.slug→post.id(file name isfilePath)entry.render()→render(entry)(imported fromastro:content)
Temporary migration flags are also available:
export default defineConfig({
legacy: {
collectionsBackwardsCompat: true,
},
});
Other abolitions/changes
- Rename
<ViewTransitions />→<ClientRouter /> Astro.glob()→ Replaced withimport.meta.glob()emitESMImage()→emitImageMetadata()getImage()is for server only (error when called on client side)- CJS configuration files (
.cjs,.cts) not supported i18n.routing.redirectToDefaultLocaledefaults tofalse- Script and style rendering order changed to code defined order
- Image services now disable cropping and upscaling by default
What you need to do to migrate this blog
We looked at the current project structure and organized the work required when migrating to Astro 6.
Impact: High
Content Collection Migration is the largest. We are currently using the legacy format in src/content/config.ts.
// 現在の定義(Astro 5 レガシー形式)
import { defineCollection, z } from 'astro:content';
const articles = defineCollection({
type: 'content', // ← これが廃止
schema: z.object({ /* ... */ }),
});
After migration:
// Astro 6 Content Layer API
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const articles = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/articles' }),
schema: z.object({ /* ... */ }),
});
In addition, there are many places where article.slug is used. In Astro 6, slug changes to id, so you need to rewrite the reference throughout the project. There are over 20 patterns of slug.split('/').pop().
Changing entry.render() also affects. The way to call render(), which should be used in [slug].astro, has changed.
Impact: Medium
- Zod import change:
from 'astro:content'→from 'astro/zod'. In use insrc/content/config.ts - Zod API changes: The current schema does not use affected APIs like
z.string().email(), but edge cases may occur due to Zod version differences. - Shiki 4: Possibility of changing the display of code blocks. There are many codes in the blog article, so visual confirmation is required.
Impact: Small
- Node.js: Already using Node 22 series. No problem
- Vite 7: If there are few places where Vite settings are directly customized, the impact will be minor.
- Vitest: Fixed in
^4.0.18
Migration flow
graph TD
A[Astro 6へのアップグレード] --> B[npx @astrojs/upgrade]
B --> C[コンテンツコレクション移行]
C --> D[config.ts → content.config.ts<br/>リネーム]
D --> E[loaderの追加<br/>type: content 削除]
E --> F[slug → id 参照の<br/>全箇所書き換え]
F --> G[render関数の<br/>呼び出し変更]
G --> H[Zodインポート変更<br/>astro:content → astro/zod]
H --> I[ビルド確認]
I --> J{エラーあり?}
J -->|Yes| K[個別修正]
K --> I
J -->|No| L[表示確認<br/>コードブロック・画像等]
L --> M[デプロイ]
Do you need to move quickly?
The Astro 5 series is still being maintained and is running without problems on ^5.16.8. Astro 6.0 was just released (4 days ago), so it’s safe to wait and see how the ecosystem supports it before migrating.
In fact, there are some issues that peripheral packages have not yet caught up with, such as an issue where @astrojs/rss is broken due to a breaking change in Zod 4. It is realistic to wait for 1-2 months and wait until the patch releases have settled down before migrating.
The story of Vite 8 and Rolldown
Astro 6 is based on Vite 7, but Vite 8.0 was released three days after Astro 6 was released (March 13th). The biggest change in Vite 8 is that esbuild and Rollup have been abolished and integrated into the Rust bundler Rolldown, eliminating the difference in bundle behavior between development and production. Build speed improvements in actual projects have also been significant, with numbers reported such as 46 seconds → 6 seconds (87% reduction) for Linear and 64% reduction for Beehiiv.
Astro 6’s development server redesign leverages Vite’s Environment API, so there is no direct dependency on Vite 8’s Rolldown integration. However, it’s likely that Astro 7 will move to Vite 8, which would combine a Rolldown-based build pipeline with Astro’s experimental Rust compiler. More and more entire toolchains are being replaced by Rust.
In terms of settings, there are breaking changes such as the esbuild option being changed to oxc and build.rollupOptions being changed to build.rolldownOptions in Vite 8. Astro should absorb this, but be careful if you are customizing Vite settings directly.
It’s difficult to decide whether to migrate to Astro 6 now or wait for the Astro 6.
The Fonts API and CSP are features I’ve been looking for. The main hurdle for migration is rewriting the content collection, and the most troublesome part seems to be modifying all the slug → id sections. The experimental Rust compiler and queued rendering features are a stepping stone towards v7, and we’re looking forward to the performance aspects.