From ascorbic-skills
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.
npx claudepluginhub joshuarweaver/cascade-code-languages-misc-2 --plugin ascorbic-skillsThis skill uses the workspace's default tool permissions.
This skill is based on the final [Astro v6 upgrade guide](https://docs.astro.build/en/guides/upgrade-to/v6/) and the [Cloudflare adapter v13 migration](https://docs.astro.build/en/guides/integrations-guide/cloudflare/#upgrading-to-v13-and-astro-6).
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
This skill is based on the final Astro v6 upgrade guide and the Cloudflare adapter v13 migration.
22.12.0 or higher. Check with node -v, and update .nvmrc / deployment config if needed. Ensure that you include CI workflows in this update if applicable.npx @astrojs/upgrade # npm
pnpm dlx @astrojs/upgrade # pnpm
yarn dlx @astrojs/upgrade # yarn
@astrojs/cloudflare v13 upgrade has significant changes. Load cloudflare.md.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:
src/content/config.{js,ts,mjs,mts} exist?
src/content/ but no config file anywhere?
src/content.config.{js,ts,mjs,mts} for:
loader property → needs migration.type: set → needs migration.getEntryBySlug(), getDataEntryById(), entry.slug, or entry.render()?
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.
These are simple renames/replacements. Apply directly.
---
// 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).
---
// 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.
// 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.
// 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;
// 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);
<ClientRouter /> handleForms propForms are handled automatically since v4. Remove the prop:
<!-- Before -->
<ClientRouter handleForms />
<!-- After -->
<ClientRouter />
prefetch() with option// Before
prefetch('/about', { with: 'fetch' });
// After
prefetch('/about');
rewrite() from Actions context// Inside an Action handler - remove any context.rewrite() calls.
// Use custom endpoints instead if you need redirect/rewrite behavior.
astro:transitions internalsIf 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.
astro:actions internalsserializeActionResult 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.
astro.config.cjs and astro.config.cts are no longer supported. Rename to .mjs, .js, .ts, or .mts.
// Before - numbers were auto-stringified
return [{ params: { id: 1 } }];
// After - must be string or undefined
return [{ params: { id: "1" } }];
// 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 }),
},
});
%25 is no longer allowed in filenames. Rename any src/pages/test%25file.astro etc.
| 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 |
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 |
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`
},
});
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