astro-v6-upgrade

Installation
SKILL.md

Astro v6 Upgrade Guide

This skill is based on the final Astro v6 upgrade guide and the Cloudflare adapter v13 migration.

Quick Start

  1. Check Node version: Astro v6 requires Node 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.
  2. Upgrade Astro and official integrations together:
    npx @astrojs/upgrade      # npm
    pnpm dlx @astrojs/upgrade # pnpm
    yarn dlx @astrojs/upgrade # yarn
    
  3. Check for legacy content collections (see below). v5 supported legacy collections via backwards-compat even without the flag. v6 removes that entirely.
  4. If deploying to Cloudflare: the @astrojs/cloudflare v13 upgrade has significant changes. Load cloudflare.md.
  5. 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:

  1. Does src/content/config.{js,ts,mjs,mts} exist?
    • Yes → needs migration (legacy config location).
  2. Are there content folders in src/content/ but no config file anywhere?
    • Yes → needs migration (implicit legacy collections).
  3. Otherwise, check src/content.config.{js,ts,mjs,mts} for:
    • Any collection without a loader property → needs migration.
    • Any collection with type: set → needs migration.
  4. Does the code use getEntryBySlug(), getDataEntryById(), entry.slug, or entry.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.tssrc/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):

Resources

Installs
11
Repository
ascorbic/skills
GitHub Stars
3
First Seen
Feb 19, 2026