name: astro-v6-upgrade description: Guide for upgrading Astro projects from v5 to v6. Use when users mention upgrading Astro, Astro v6, Astro 6, the Cloudflare adapter v13, or errors related to content collections, ViewTransitions, Astro.glob, Zod schemas, or the Content Layer API.
Astro v6 Upgrade Guide
This skill is based on the final Astro v6 upgrade guide and the Cloudflare adapter v13 migration.
Quick Start
- Check Node version: Astro v6 requires Node
22.12.0or higher. Check withnode -v, and update.nvmrc/ deployment config if needed. Ensure that you include CI workflows in this update if applicable. - Upgrade Astro and official integrations together:
npx @astrojs/upgrade # npm pnpm dlx @astrojs/upgrade # pnpm yarn dlx @astrojs/upgrade # yarn - Check for legacy content collections (see below). v5 supported legacy collections via backwards-compat even without the flag. v6 removes that entirely.
- If deploying to Cloudflare: the
@astrojs/cloudflarev13 upgrade has significant changes. Load cloudflare.md. - Fix any errors using this guide.
Check: Legacy Content Collections
Before upgrading, check if the project needs content-collection migration. Many v5 projects silently relied on legacy backwards-compat (no flag required) and will break on v6.
Decision tree:
- Does
src/content/config.{js,ts,mjs,mts}exist?- Yes → needs migration (legacy config location).
- Are there content folders in
src/content/but no config file anywhere?- Yes → needs migration (implicit legacy collections).
- Otherwise, check
src/content.config.{js,ts,mjs,mts}for:- Any collection without a
loaderproperty → needs migration. - Any collection with
type:set → needs migration.
- Any collection without a
- Does the code use
getEntryBySlug(),getDataEntryById(),entry.slug, orentry.render()?- Yes → needs migration.
If any apply, load content-collections.md.
Temporary escape hatch (if migration can't happen immediately):
export default defineConfig({
legacy: {
collectionsBackwardsCompat: true,
},
});
This preserves the v4-style behavior: src/content/config.ts location, type: 'content'/'data' without loaders, entry.slug, entry.render(), path-based IDs. It is explicitly a migration helper and should be removed as soon as the project moves to the Content Layer API.
Note: The old legacy.collections: true flag is removed. Remove it if present.
Quick Fixes
These are simple renames/replacements. Apply directly.
ViewTransitions → ClientRouter
---
// Before
import { ViewTransitions } from 'astro:transitions';
// After
import { ClientRouter } from 'astro:transitions';
---
<!-- Before -->
<ViewTransitions />
<!-- After -->
<ClientRouter />
Also remove the handleForms prop if present - it is now removed entirely (form handling has been built in by default since v4).
Astro.glob() → import.meta.glob()
---
// Before
const posts = await Astro.glob('./posts/*.md');
// After - note: no longer returns a Promise
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
---
Consider content collections for content, or fast-glob for runtime globbing.
Zod imports
// Before (deprecated)
import { z } from 'astro:content';
import { z } from 'astro:schema';
// After
import { z } from 'astro/zod';
astro:schema and z from astro:content are deprecated - import z from astro/zod directly. For Zod 4 API changes (string formats, error messages, transforms), see zod.md.
Deprecated APIs
// Astro.site in getStaticPaths → import.meta.env.SITE
export function getStaticPaths() {
const site = import.meta.env.SITE; // was Astro.site
}
// Astro.generator in getStaticPaths → just remove it
// import.meta.env.ASSETS_PREFIX → astro:config/server
import { build } from 'astro:config/server';
const prefix = build.assetsPrefix;
Removed: emitESMImage()
// Before
import { emitESMImage } from 'astro/assets/utils';
const result = await emitESMImage(imageId, false, false);
// After
import { emitImageMetadata } from 'astro/assets/utils';
const result = await emitImageMetadata(imageId);
Removed: <ClientRouter /> handleForms prop
Forms are handled automatically since v4. Remove the prop:
<!-- Before -->
<ClientRouter handleForms />
<!-- After -->
<ClientRouter />
Removed: prefetch() with option
// Before
prefetch('/about', { with: 'fetch' });
// After
prefetch('/about');
Removed: rewrite() from Actions context
// Inside an Action handler - remove any context.rewrite() calls.
// Use custom endpoints instead if you need redirect/rewrite behavior.
Removed: exposed astro:transitions internals
If imported, remove these or use plain string event names:
// Before
import {
createAnimationScope,
isTransitionBeforePreparationEvent,
TRANSITION_AFTER_SWAP,
} from 'astro:transitions/client';
console.log(isTransitionBeforePreparationEvent(event));
console.log(TRANSITION_AFTER_SWAP);
// After
console.log(event.type === 'astro:before-preparation');
console.log('astro:after-swap');
// createAnimationScope has no replacement - remove it.
Removed: exposed astro:actions internals
serializeActionResult and deserializeActionResult are no longer exported from astro:actions. Use getActionContext() in middleware:
import { defineMiddleware } from 'astro:middleware';
import { getActionContext } from 'astro:actions';
export const onRequest = defineMiddleware(async (context, next) => {
const { serializeActionResult, deserializeActionResult } = getActionContext(context);
// ...
});
Also remove imports of: ACTION_ERROR_CODES, ActionInputError, appendForwardSlash, astroCalledServerError, callSafely, formDataToObject, getActionQueryString, type Actions, type ActionAccept, type AstroActionContext, type SerializedActionResult.
Removed: CommonJS config files
astro.config.cjs and astro.config.cts are no longer supported. Rename to .mjs, .js, .ts, or .mts.
Changed: getStaticPaths params must be strings
// Before - numbers were auto-stringified
return [{ params: { id: 1 } }];
// After - must be string or undefined
return [{ params: { id: "1" } }];
Changed: Session driver config
// Before
import { defineConfig } from 'astro/config';
export default defineConfig({
session: {
driver: 'redis',
options: { url: process.env.REDIS_URL },
},
});
// After
import { defineConfig, sessionDrivers } from 'astro/config';
export default defineConfig({
session: {
driver: sessionDrivers.redis({ url: process.env.REDIS_URL }),
},
});
Removed: Percent-encoding in route filenames
%25 is no longer allowed in filenames. Rename any src/pages/test%25file.astro etc.
Error Quick Reference
| Error | Fix |
|---|---|
LegacyContentConfigError |
Move src/content/config.ts → src/content.config.ts |
ContentCollectionMissingALoaderError |
Add loader to collection - see content-collections.md |
ContentCollectionInvalidTypeError |
Remove type: 'content' or type: 'data' from collection |
GetEntryDeprecationError |
Replace getEntryBySlug()/getDataEntryById() with getEntry() |
ContentSchemaContainsSlugError |
Replace .slug with .id, use .filePath for filename |
Cannot find ViewTransitions |
Use ClientRouter (see above) |
Cannot find Astro.glob |
Use import.meta.glob() (see above) |
| Node version error | Upgrade to Node 22.12.0+ |
| Zod validation errors | Check zod.md for Zod 4 changes |
Cloudflare: Astro.locals.runtime is undefined |
See cloudflare.md - access moved |
Deep Dive Files
Load these only when needed:
| File | When to load |
|---|---|
| content-collections.md | Legacy content collections need migration |
| zod.md | Using Zod schemas with .email(), .url(), custom errors, transforms, or .default() |
| behavior-changes.md | Subtle issues: i18n redirects, script/style order, env vars, image sizing, Vitest, Shiki, SVGs |
| integration-api.md | Building integrations or adapters |
| cloudflare.md | Deploying to Cloudflare Workers / Pages, upgrading @astrojs/cloudflare to v13 |
Experimental Flags to Remove
These flags are now stable, default, or renamed. Remove from config:
export default defineConfig({
experimental: {
// Remove all of these - now stable or default:
csp: true, // stable: use `security.csp`
fonts: true, // stable
liveContentCollections: true, // stable
preserveScriptOrder: true, // now default - see behavior-changes.md
staticImportMetaEnv: true, // now default - see behavior-changes.md
headingIdCompat: true, // now default - see behavior-changes.md
failOnPrerenderConflict: true, // renamed to `prerenderConflictBehavior`
},
});
Official Adapter Upgrades
All official adapters need a major upgrade alongside Astro v6 (to accompany Vite 7 + Environment API):
@astrojs/cloudflare→ v13 (significant breaking changes - see cloudflare.md)@astrojs/netlify@astrojs/node@astrojs/vercel