Skip to content

Upgrade to Astro v3

This guide will help you migrate from Astro v2 to Astro v3.

Need to upgrade an older project to v2? See our older migration guide.

Update your project’s version of Astro to the latest version using your package manager. If you’re using Astro integrations, please also update those to the latest version.

Terminal window
# Upgrade to Astro v3.x
npm install astro@latest
# Example: upgrade React and Tailwind integrations
npm install @astrojs/react@latest @astrojs/tailwind@latest

Astro v3.0 Experimental Flags Removed

Section titled Astro v3.0 Experimental Flags Removed

Remove the following experimental flags from astro.config.mjs:

import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
assets: true,
viewTransitions: true,

These features are now available by default:

  • View Transitions for animated page transitions and persistent islands. See view transitions API breaking changes and upgrading advice if you were using this experimental flag.
  • A new image services API astro:assets for using images in Astro, including a new <Image /> component and getImage() function. Please read the detailed image upgrade advice whether or not you were using this experimental flag to see how this might affect your project.

Read more about these two exciting features and more in the 3.0 Blog post!

Astro v3.0 includes some breaking changes, as well as the removal of some previously deprecated features. If your project doesn’t work as expected after upgrading to v3.0, check this guide for an overview of all breaking changes and instructions on how to update your codebase.

See the changelog for full release notes.

Node 16 is scheduled to reach its End of Life in September 2023.

Astro v3.0 drops Node 16 support entirely so that all Astro users can take advantage of Node’s more modern features.

Check that both your development environment and your deployment environment are using Node 18.14.1 or higher.

  1. Check your local version of Node using:

    Terminal window
    node -v
  2. Check your deployment environment’s own documentation to verify that they support Node 18.

    You can specify Node 18.14.1 for your Astro project either in a dashboard configuration setting or a .nvmrc file.


Removed: Support for TypeScript 4

Section titled Removed: Support for TypeScript 4

In Astro v2.x, the tsconfig.json presets include support for both TypeScript 4.x and 5.x.

Astro v3.0 updates the tsconfig.json presets to only support TypeScript 5.x. Astro now assumes that you use TypeScript 5.0 (March 2023), or that your editor includes it (e.g. VS Code 1.77).

If you have installed TypeScript locally, update to at least v5.0.

Terminal window
npm install typescript@latest --save-dev

In Astro v2.x, Astro offered an official image integration that included Astro <Image /> and <Picture /> components.

Astro v3.0 removes this integration from the codebase entirely. Astro’s new solution for images is a built-in image services API: astro:assets.

Remove the @astrojs/image integration from your project. You will need to not only uninstall the integration but also update or remove any import statements and existing <Image /> and <Picture /> components. You might also need to configure a preferred default image processing service.

You will find complete, step-by-step instructions for removing the old image integration in our Images guide.

Migrating to astro:assets will also bring some new image options and features that you may now wish to use. Please see the full v3.0 Image Upgrade Advice for full details!

import { defineConfig } from 'astro/config';
import image from '@astrojs/image';
export default defineConfig({
integrations: [

In Astro v1.x, Astro deprecated the <Markdown /> component and moved it to an external package.

Astro v3.0 completely removes the package @astrojs/markdown-component. Astro’s <Markdown /> component will no longer work in your project.

Remove all instances of the @astrojs/markdown-component.

import Markdown from '@astrojs/markdown-component';

To continue using a similar <Markdown /> component in your code, consider using community integrations such as astro-remote. Be sure to update your <Markdown /> component imports and attributes as necessary, according to the integration’s own documentation.

Otherwise, delete all references to importing Astro’s <Markdown /> component and the component itself in your .astro files. You will need to rewrite your content as HTML directly or import Markdown from a .md file.

In Astro v1.x, Astro deprecated our original configuration settings as well as <style global> and <script hoist> support. However, these were still supported for backwards compatibility.

Astro v3.0 removes these deprecated APIs entirely. The officially supported configuration settings and modern <style is:global> and <script> syntax should be used instead.

If you are continuing to use v1.x APIs, use the new APIs for each feature instead:

Removed: Partial shims for Web APIs in server code

Section titled Removed: Partial shims for Web APIs in server code

In Astro v2.x, Astro provided partial shims for Web APIs such as document or localStorage in server-rendered code. These shims were often incomplete and unreliable.

Astro v3.0 removes these partial shims entirely. Web APIs are no longer available in server-rendered code.

If you are using Web APIs in server-rendered components, you will need to either make the usage of those APIs conditional or use the client:only client directive.

Removed: image from astro:content in content collections schema

Section titled Removed: image from astro:content in content collections schema

In Astro v2.x, the content collections API deprecated an image export from astro:content for use in your content collections schemas.

Astro v3.0 removes this export entirely.

If you are using the deprecated image() from astro:content, remove it as this no longer exists. Validate images through the image helper from schema instead:

import { defineCollection, z, image } from "astro:content";
import { defineCollection, z } from "astro:content";
schema: ({ image }) =>
image: image(),

Removed: pre-0.14 Shiki theme names

Section titled Removed: pre-0.14 Shiki theme names

In Astro v2.x, some Shiki theme names had been renamed, but the original names were kept for backwards compatibility.

Astro v3.0 removes the original names in favor of the renamed theme names.

If your project uses any of the themes below, rename them to their updated name:

  • material-darker -> material-theme-darker
  • material-default -> material-theme
  • material-lighter -> material-theme-lighter
  • material-ocean -> material-theme-ocean
  • material-palenight -> material-theme-palenight

In Astro v2.x, the class:list directive used a custom implementation inspired by clsx with a few extra features like deduplication and Set support.

Astro v3.0 now uses clsx directly for class:list, which does not support deduplication or Set values.

Replace any Set elements passed to the class:list directive with a plain Array.

<Component class:list={[
new Set(['c', 'd'])
['c', 'd']
]} />

Removed: passing class:list as a prop

Section titled Removed: passing class:list as a prop

In Astro v2.x, class:list values were sent to components via Astro.props['class:list'].

Astro v3.0 normalizes class:list values into a string before being sent to components via Astro.props['class']

Remove any code that expects to receive the class:list prop.

import { clsx } from 'clsx';
const { class: className, 'class:list': classList } = Astro.props;
const { class: className } = Astro.props;
class:list={[className, classList]}

Removed: kebab-case transform for camelCase CSS variables

Section titled Removed: kebab-case transform for camelCase CSS variables

In Astro v2.x, camelCase CSS variables passed to the style attribute were rendered as both camelCase (as written) and kebab-case (kept for backwards compatibility).

Astro v3.0 removes the kebab-case transform for these camelCase CSS variable names, and only the original camelCase CSS variable is rendered.

const myValue = "red"
<!-- input -->
<div style={{ "--myValue": myValue }}></div>
<!-- output (Astro 2.x) -->
<div style="--my-value:var(--myValue);--myValue:red"></div>
<!-- output (Astro 3.0) -->
<div style="--myValue:red"></div>

If you were relying on Astro to transform kebab-case in your styles, update your existing styles to camelCase to prevent missing styles. For example:

div {
color: var(--my-value);
color: var(--myValue);

Removed: automatic flattening of getStaticPaths()’s return value

Section titled Removed: automatic flattening of getStaticPaths()’s return value

In Astro v2.x, the return value of getStaticPaths() was automatically flattened to allow you to return an array of arrays without errors.

Astro v3.0 removes automatic flattening of getStaticPaths()’s result.

If you’re returning an array of arrays instead of an array of objects (as is expected), .flatMap and .flat should now be used to ensure that you are returning a flat array.

An error message indicating that getStaticPath()’s return value must be an array of objects will be provided if you need to update your code.

Moved: astro check now requires an external package

Section titled Moved: astro check now requires an external package

In Astro v2.x, astro check was included in Astro by default, and its dependencies were bundled in Astro. This meant a larger package whether or not you ever used astro check. This also prevented you from having control over the version of TypeScript and the Astro Language Server to use.

Astro v3.0 moves the astro check command out of Astro core and now requires an external package @astrojs/check. Additionally, you must install typescript in your project to use the astro check command.

Run the astro check command after upgrading to Astro v3.0 and follow the prompts to install the required dependencies, or manually install @astrojs/check and typescript into your project.

Deprecated: build.excludeMiddleware and build.split

Section titled Deprecated: build.excludeMiddleware and build.split

In Astro v2.x, build.excludeMiddleware and build.split were used to change how specific files were emitted when using an adapter in SSR mode.

Astro v3.0 replaces these build config options with new SSR adapter configuration options to perform the same tasks: edgeMiddleware and functionPerRoute.

Update the Astro config file to now use the new options in the adapter configuration directly.

import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel/serverless";
export default defineConfig({
build: {
excludeMiddleware: true
adapter: vercel({
edgeMiddleware: true
import { defineConfig } from "astro/config";
import netlify from "@astrojs/netlify/functions";
export default defineConfig({
build: {
split: true
adapter: netlify({
functionPerRoute: true

In Astro v2.x, the markdown.drafts configuration allowed you to have draft pages that were available in when running the dev server, but not built in production.

Astro v3.0 deprecates this feature in favor of the content collections method of handling draft pages by filtering manually instead, which gives more control over the feature.

To continue to mark some pages in your project as drafts, migrate to content collections and manually filter out pages with the draft: true frontmatter property instead.

Deprecated: returning simple object in endpoints

Section titled Deprecated: returning simple object in endpoints

In Astro v2.x, endpoints could return a simple object, which would be converted to a JSON response.

Astro v3.0 deprecates this behavior in favor of returning a Response object directly.

Update your endpoints to return a Response object directly.

export async function GET() {
return { body: { "title": "Bob's blog" }};
return new Response(JSON.stringify({ "title": "Bob's blog" }));

If you really need to keep the previous format, you can use the ResponseWithEncoding object but will be deprecated in the future.

export async function GET() {
return { body: { "title": "Bob's blog" } };
return new ResponseWithEncoding({ body: { "title": "Bob's blog" }});

Changed default: verbatimModuleSyntax in tsconfig.json presets

Section titled Changed default: verbatimModuleSyntax in tsconfig.json presets

In Astro v2.x, the verbatimModuleSyntax setting was off by default, with its TypeScript 4.x equivalent importsNotUsedAsValues being enabled in the strict preset.

In Astro v3.0, verbatimModuleSyntax is enabled in every preset.

This option requires that types are imported using the import type syntax.

import { type CollectionEntry, getEntry } from "astro:content";

While we recommend keeping it on and properly making your type imports with type (as shown above), you can disable it by setting verbatimModuleSyntax: false in your tsconfig.json file if it causes any issues.

"compilerOptions": {
"verbatimModuleSyntax": false

In Astro v2.x, Astro ran on port 3000 by default.

Astro v3.0 changes the default port to 4321. 🚀

Update any existing references to localhost:3000, for example in tests or in your README, to reflect the new port localhost:4321.

Changed default: import.meta.env.BASE_URL trailingSlash

Section titled Changed default: import.meta.env.BASE_URL trailingSlash

In Astro v2.x, import.meta.env.BASE_URL appended your base setting with a trailingSlash by default. trailingSlash: "ignore" also appended a trailing slash.

Astro v3.0 no longer appends import.meta.env.BASE_URL with a trailing slash by default, nor when trailingSlash: "ignore" is set. (The existing behavior of base in combination with trailingSlash: "always" or trailingSlash: "never" is unchanged.)

If your base already has a trailing slash, no change is needed.

If your base does not have a trailing slash, add one if you wish to preserve the previous default (or trailingSlash: "ignore") behavior:

import { defineConfig } from "astro/config";
export default defineConfig({
base: 'my-base',
base: 'my-base/',

In Astro v2.x, Astro only compressed your emitted HTML when compressHTML was explicitly set to true. The default value was false.

Astro v3.0 now compresses emitted HTML by default.

You can now remove compressHTML: true from your configuration as this is the new default behavior.

import { defineConfig } from "astro/config";
export default defineConfig({
compressHTML: true

You must now set compressHTML: false to opt out of HTML compression.

Changed default: scopedStyleStrategy

Section titled Changed default: scopedStyleStrategy

In Astro v2.x, the default value of scopedStyleStrategy was "where".

Astro v3.0 introduces a new, default value: "attribute". By default, styles are now applied using data-* attributes.

To retain your project’s current style scoping, update the configuration file to the previous default value:

import { defineConfig } from "astro/config";
export default defineConfig({
scopedStyleStrategy: "where"

Changed default: inlineStyleSheets

Section titled Changed default: inlineStyleSheets

In Astro v2.x, all project stylesheets were sent as link tags by default. You could opt in to inlining them into <style> tags every time with "always", or to inlining only stylesheets below a certain size with "auto" by setting the build.inlineStylesheets configuration. The default setting was "never".

Astro v3.0 changes the default value of inlineStylesheets to "auto". Stylesheets smaller than (default: 4kb) are inlined by default. Otherwise, project styles are sent in external stylesheets.

If you want to keep your project’s current behavior, set build.inlineStylesheets to the previous default, "never":

import { defineConfig } from "astro/config";
export default defineConfig({
build: {
inlineStylesheets: "never"

Changed default: image service

Section titled Changed default: image service

In Astro v2.x, Squoosh was the default image processing service.

Astro v3.0 now includes Sharp as the default image processing service and instead provides a configuration option to use Squoosh.

If you would prefer to continue to use Squoosh to transform your images, update your config with the following:

import { defineConfig, squooshImageService } from "astro/config";
export default defineConfig({
image: {
service: squooshImageService(),

Changed: HTTP request methods case

Section titled Changed: HTTP request methods case

In Astro v2.x, HTTP request methods were written using lowercase function names: get, post, put, all, and del.

Astro v3.0 uses uppercase function names, including DELETE instead of del.

Rename all functions to their uppercase equivalent:

  • get to GET
  • post to POST
  • put to PUT
  • all to ALL
  • del to DELETE
export function get() {
export function GET() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));

Changed: Multiple JSX framework configuration

Section titled Changed: Multiple JSX framework configuration

In Astro v2.x, you could use multiple JSX framework integrations (React, Solid, Preact) in the same project without needing to identify which files belonged to which framework.

Astro v3.0 now requires you to specify which framework to use for your files with new include and exclude integration config options when you have multiple JSX framework integrations installed. This allows Astro to better support single-framework usage, as well as advanced features like React Fast Refresh.

If you are using multiple JSX frameworks in the same project, set include (and optionally exclude) to an array of files and/or folders. Wildcards may be used to include multiple file paths.

We recommend placing common framework components in the same folder (e.g. /components/react/ and /components/solid/) to make specifying your includes easier, but this is not required:

import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
// No `include` is needed if you are only using a single framework!
integrations: [
include: ['**/preact/*']
include: ['**/react/*']
include: ['**/solid/*'],

Changed: Astro.cookies.get(key) can return undefined

Section titled Changed: Astro.cookies.get(key) can return undefined

In Astro v2.x, Astro.cookies.get(key) would always return an AstroCookie object, even if the cookie did not exist. To check for its existence, you needed to use Astro.cookies.has(key).

Astro v3.0 returns undefined for Astro.cookies.get(key) if the cookie does not exist.

This change will not break any code that checks for the existence of the Astro.cookie object before using Astro.cookies.get(key), but is now no longer required.

You can safely remove any code that uses has() to check if the value of Astro.cookies is undefined:

if (Astro.cookies.has(id)) {
const id = Astro.cookies.get(id)!;
const id = Astro.cookies.get(id);
if (id) {

Changed: running the Astro CLI programmatically

Section titled Changed: running the Astro CLI programmatically

In Astro v2.x, the "astro" package entrypoint exported and ran the Astro CLI directly. It is not recommended to run Astro this way in practice.

Astro v3.0 removes the CLI from the entrypoint, and exports a new set of experimental JavaScript APIs, including dev(), build(), preview(), and sync().

To run the Astro CLI programmatically, use the new experimental JavaScript APIs:

import { dev, build } from "astro";
// Start the Astro dev server
const devServer = await dev();
await devServer.stop();
// Build your Astro project
await build();

Changed: internal Astro API entry point export paths

Section titled Changed: internal Astro API entry point export paths

In Astro v2.x, you could import internal Astro APIs from astro/internal/* and astro/runtime/server/*.

Astro v3.0 removes the two entry points in favor of the existing astro/runtime/* entrypoint. Additionally, a new astro/compiler-runtime export has been added for compiler-specific runtime code.

These are entry points for Astro’s internal API and should not affect your project. But if you do use these entrypoints, update as shown below:

import 'astro/internal/index.js';
import 'astro/runtime/server/index.js';
import 'astro/server/index.js';
import 'astro/runtime/server/index.js';
import { transform } from '@astrojs/compiler';
const result = await transform(source, {
internalURL: 'astro/runtime/server/index.js',
internalURL: 'astro/compiler-runtime',
// ...

Know a good resource for Astro v3.0? Edit this page and add a link below!

There are currently no known issues.