title | description | sidebar | i18nReady | ||
---|---|---|---|---|---|
Astro v5.0-beta |
Upgrade your project to Astro v5.0-beta. |
|
true |
import PackageManagerTabs from '/components/tabs/PackageManagerTabs.astro'
import { Steps } from '@astrojs/starlight/components';
import ReadMore from '/components/ReadMore.astro'
import SourcePR from '~/components/SourcePR.astro'
This guide will help you migrate from Astro v4 to Astro v5-beta.
Need to upgrade an older project to v4 first? See our older migration guide.
:::note[5.0-beta docs in progress] There are several new features and breaking changes that may not yet be entirely reflected throughout the rest of this site. Thank you for your patience while we prepare for Astro v5 stable!
For v4 docs, please visit the main Astro Docs website. :::
The following features are new/stable in Astro v5.0-beta:
- The new content collections using the Content Layer API and Content Loader API
- Server Islands
- Type Safe environment variables
- On-demand rendering available in
static
mode (like the previoushybrid
mode)
{/* Need to see the v4 docs? Visit this older version of the docs site (unmaintained v3.6 snapshot). */}
Try the Astro v5-beta in a new project:
```shell npm create astro@latest -- --ref next ``` ```shell pnpm create astro --ref next ``` ```shell yarn create astro --ref next ```Update your existing project's version of Astro and all official integrations to the latest versions using your package manager.
```shell # Upgrade Astro and official integrations together npx @astrojs/upgrade beta ``` ```shell # Upgrade Astro and official integrations together pnpm dlx @astrojs/upgrade beta ``` ```shell # Upgrade Astro and official integrations together yarn dlx @astrojs/upgrade beta ```{/* For beta version, only show upgrade command! You can also upgrade your Astro integrations manually if needed, and you may also need to upgrade other dependencies in your project.
Update your project's version of Astro to the latest beta version using your package manager:
```shell # Upgrade Astro and official integrations together npx @astrojs/upgrade ``` ```shell # Upgrade Astro and official integrations together pnpm dlx @astrojs/upgrade ``` ```shell # Upgrade Astro and official integrations together yarn dlx @astrojs/upgrade ```You can also upgrade your Astro integrations manually if needed, and you may also need to upgrade other dependencies in your project. */}
:::note[Need to continue?] After upgrading Astro, you may not need to make any changes to your project at all!
But, if you notice errors or unexpected behavior, please check below for what has changed that might need updating in your project. :::
Astro v5.0 includes potentially breaking changes, as well as the removal and deprecation of some features.
If your project doesn't work as expected after upgrading to v5.0, check this guide for an overview of all breaking changes and instructions on how to update your codebase.
See the Astro changelog for full release notes.
Astro v5.0 updates to Vite v6.0 as the development server and production bundler.
If you are using Vite-specific plugins, configuration, or APIs, check the Vite migration guide for their breaking changes and upgrade your project as needed.
In Astro v4.x, Astro performed internal JSX handling for the @astrojs/mdx
integration.
Astro v5.0 moves this responsibility to handle and render JSX and MDX to the @astrojs/mdx
package directly. This means that Astro 5.0 is no longer compatible with older versions of the MDX integration.
If your project includes .mdx
files, you must upgrade @astrojs/mdx
to the latest version (v4.0.0) so that your JSX can be handled properly by the integration.
If you are using an MDX server renderer with the experimental Astro Container API you must update the import to reflect the new location:
import mdxRenderer from "astro/jsx/server.js";
import mdxRenderer from "@astrojs/mdx/server.js";
Learn more about using MDX in your project.
{/* // THESE ARE FOR DEPENDENCY UPGRADES
Any major upgrades to Astro's dependencies may cause breaking changes in your project.
Placeholder
If you are using Vite-specific plugins, configuration, or APIs, check the Vite migration guide for their breaking changes and upgrade your project as needed. There are no breaking changes to Astro itself.
*/}
The following features are considered legacy features. They should function normally but are no longer recommended and are in maintenance mode. They will see no future improvements and documentation will not be updated. These features will eventually be deprecated, and then removed entirely.
In Astro 4.x, content collections were defined, queried, and rendered using the Content Collections API first introduced in Astro v2.0.
Astro 5.0 introduces a new version of content collections using the Content Layer API which brings several performance improvements and added capabilities. While old (legacy) and new (Content Layer API) collections can continue exist together in this release, there are potentially breaking changes to existing legacy collections.
We recommend converting any existing collections to the new Content Layer API as soon as you are able and making any new collections using the Content Layer API.
If you are unable to convert your collections, then please consult the legacy collections breaking changes to see whether your existing collections are affected and require updating.
If you are unable to make any changes to your collections at this time, you can enable the legacy.collections
flag which will allow you to keep your colletions in their current state until the legacy flag is no longer supported.
Learn more about the updated content collections.
See the instructions below for converting an existing content collection with Markdown, MDX, Markdoc, or JSON entries to use the Content Layer API.
Step-by-step instructions to update a collection
-
Move the content config file. This file no longer lives within the
src/content/
folder. This file should now exist atsrc/content.config.ts
. -
Edit the collection definition. Your updated collection requires a
loader
, and the option to select a collectiontype
is no longer available.// src/content.config.ts import { defineCollection, z } from 'astro:content'; import { glob } from 'astro/loaders'; const blog = defineCollection({ // For content layer you no longer define a `type` type: 'content', loader: glob({ pattern: '**\/[^_]*.md', base: "./src/data/blog" }), schema: z.object({ title: z.string(), description: z.string(), pubDate: z.coerce.date(), updatedDate: z.coerce.date().optional(), }), });
-
Change references from
slug
toid
. Content layer collections do not have aslug
field. Instead, all updated collections will have anid
. You may also need to update your file name to match an updated getStaticPaths() parameter:// src/pages/[id].astro --- export async function getStaticPaths() { const posts = await getCollection('blog'); return posts.map((post) => ({ params: { slug: post.slug }, params: { slug: post.id }, props: post, })); } ---
-
Switch to the new
render()
function. Entries no longer have arender()
method, as they are now serializable plain objects. Instead, import therender()
function fromastro:content
.--- import { getEntry, render } from 'astro:content'; const post = await getEntry('blog', params.slug); const { Content, headings } = await post.render(); const { Content, headings } = await render(post); --- <Content />
By default, collections that use the old types (content
or data
) and do not define a loader
are now implemented under the hood using the Content Layer API's built-in glob()
loader, with extra backward-compatibility handling.
Additionally, temporary backwards compatibility exists for keeping the content config file in its original location of src/content/config.ts
.
While this backwards compatibility implementation is able to emulate most of the features of legacy collections, allowing many legacy collections to continue to work even without updating your code, there are some differences and limitations that may cause breaking changes to existing collections:
- In previous versions of Astro, collections would be generated for all folders in
src/content/
, even if they were not defined insrc/content/config.ts
. This behavior is now deprecated, and collections should always be defined insrc/content.config.ts
. For existing collections, these can just be empty declarations (e.g.const blog = defineCollection({})
) and Astro will implicitly define your legacy collection for you in a way that is compatible with the new loading behavior. - The special
layout
field is not supported in Markdown collection entries. This property is intended only for standalone page files located insrc/pages/
and not likely to be in your collection entries. However, if you were using this property, you must now create dynamic routes that include your page styling. - Sort order of generated collections is non-deterministic and platform-dependent. This means that if you are calling
getCollection()
, the order in which entries are returned may be different than before. If you need a specific order, you should sort the collection entries yourself. image().refine()
is not supported. If you need to validate the properties of an image you will need to do this at runtime in your page or component.- the
key
argument ofgetEntry(collection, key)
is typed asstring
, rather than having types for every entry.
If you are not yet ready to update your existing collections, you can enable the legacy.collections
flag and your existing collections will continue to function as before.
The following deprecated features are no longer supported and are no longer documented. Please update your project accordingly.
Some deprecated features may temporarily continue to function until they are completely removed. Others may silently have no effect, or throw an error prompting you to update your code.
In Astro v4.x, you could use Astro.glob()
in your .astro
components to query multiple files in your project. This had some limitations (where it could be used, performance, etc.), and using querying functions from the Content Collections API or Vite's own import.meta.glob()
often provided more function and flexibility.
Astro 5.0 deprecates Astro.glob()
in favor of using getCollection()
to query your collections, and import.meta.glob()
to query other source files in your project.
Replace all use of Astro.glob()
with import.meta.glob()
. Note that import.meta.glob()
no longer returns a Promise
, so you may have to update your code accordingly. You should not require any updates to your glob patterns.
---
const posts = await Astro.glob('./posts/*.md');
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
---
{posts.map((post) => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
Where appropriate, consider using content collections to organize your content, which has its own newer, more performant querying functions.
You may also wish to consider using glob packages from NPM, such as fast-glob
.
Learn more about importing files with import.meta.glob
.
In Astro v4.x, you could opt into creating a separate file for each route defined in the project, mirroring your src/pages/
directory in the build folder. By default, Astro emitted a single entry.mjs
file, which was responsible for emitting the rendered page on each request.
Astro v5.0 removes the option to opt out of the default behavior. This behavior is now standard, and non-configurable.
Remove the functionPerRoute
property from your adapterFeatures
configuration. It is no longer available.
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
functionPerRoute: true
}
});
},
},
};
}
Learn more about the Adapter API for building adapter integrations.
In Astro v4.x, integrations accessed routes from the astro:build:done
hook.
Astro v5.0 deprecates the routes
array passed to this hook. Instead, it exposes a new astro:routes:resolved
hook that runs before astro:config:done
, and whenever a route changes in development. It has all the same properties of the deprecated routes
list, except distURL
which is only available during build.
Remove any instance of routes
passed to astro:build:done
and replace it with the new astro:routes:resolved
hook. Access distURL
on the newly exposed assets
map:
const integration = () => {
let routes
return {
name: 'my-integration',
hooks: {
'astro:routes:resolved': (params) => {
routes = params.routes
},
'astro:build:done': ({
routes
assets
}) => {
for (const route of routes) {
const distURL = assets.get(route.pattern)
if (distURL) {
Object.assign(route, { distURL })
}
}
console.log(routes)
}
}
}
}
Learn more about the Integration API astro:routes:resolved
hook for building integrations.
The following features have now been entirely removed from the code base and can no longer be used. Some of these features may have continued to work in your project even after deprecation. Others may have silently had no effect.
Projects now containing these removed features will be unable to build, and there will no longer be any supporting documentation prompting you to remove these features.
In Astro v4.x, Lit was a core-maintained framework library through the @astrojs/lit
package.
Astro v5.0 removes the integration and it will not receive further updates for compatibility with 5.x and above.
You can continue to use Lit for client components by adding a client-side script tag. For example:
<script>
import "../components/MyTabs";
</script>
<my-tabs title="These are my tabs">...</my-tabs>
If you're interested in maintaining a Lit integration yourself, you may wish to use the last published version of @astrojs/lit
as a starting point and upgrade the relevant packages.
Learn more about Astro's official integrations.
In Astro v4.x, Astro provided three rendering output
rendering modes: 'static'
, 'hybrid'
, and 'server'
Astro v5.0 merges the output: 'hybrid'
and output: 'static'
configurations into one single configuration (now called 'static'
) that works the same way as the previous hybrid option.
It is no longer necessary to specify output: 'hybrid'
in your Astro config to use server-rendered pages. The new output: 'static'
has this capability included.
Astro will now automatically allow you to opt out of prerendering in your static site with no change to your output configuration required. Any page route or endpoint can include export const prerender = false
to be server-rendered on demand, while the rest of your site is statically generated.
If your project used hybrid rendering, you must now remove the output: 'hybrid'
option from your Astro config as it no longer exists. However, no other changes to your project are required, and you should have no breaking changes. The previous 'hybrid'
behavior is now the default, under a new name 'static'
.
import { defineConfig } from "astro/config";
export default defineConfig({
output: 'hybrid',
});
If you were using the output: 'static'
(default) option, you can continue to use it as before. By default, all of your pages will continue to be prerendered and you will have a completely static site. You should have no breaking changes to your project.
An adapter is still required to deploy an Astro project with any server-rendered pages, no matter which output
mode your project uses. Failure to include an adapter will result in a warning in development and an error at build time.
Learn more about on-demand rendering in Astro.
In Astro 4.x, you could configure image.service: squooshImageService()
to use Squoosh to transform your images instead of Sharp. However, the underlying library libsquoosh
is no longer maintained and has memory and performance issues.
Astro 5.0 removes the Squoosh image optimization service entirely.
To switch to the built-in Sharp image service, remove the squooshImageService
import from your Astro config. By default, you will use Sharp for astro:assets
.
import { squooshImageService } from "astro/config";
import { defineConfig } from "astro/config";
export default defineConfig({
image: {
service: squooshImageService()
}
});
If you are using a strict package manager like pnpm
, you may need to install the sharp
package manually to use the Sharp image service, even though it is built into Astro by default.
If your adapter does not support Astro's built-in Sharp image optimization, you can configure a no-op image service to allow you to use the <Image />
and <Picture />
components.
Alternatively, you may wish to consider a community-maintained Squoosh image service if you are unable to use the Sharp image service.
If your adapter previously precised its compatibility status with Squoosh, you should now remove this information from your adapter configuration.
supportedAstroFeatures: {
assets: {
isSquooshCompatible: true
}
}
Read more about configuring your default image service.
In Astro v4.x, @types/astro.ts
exposed all types publicly to users, whether or not they were still actively used or only intended for internal use.
Astro v5.0 refactors this file to remove outdated and internal types. This refactor brings improvements to your editor (e.g. faster completions, lower memory usage, and more relevant completion options). However, this refactor may cause errors in some projects that have been relying on types that are no longer available to the public.
Remove any types that now cause errors in your project as you no longer have access to them. These are mostly APIs that have previously been deprecated and removed, but may also include types that are now internal.
See the public types exposed for use.
The following experimental flags have been removed in Astro v5.0 and these features are available for use:
env
serverIslands
Additionally, the following experimental flags have been removed and are now the default or recommended behavior in Astro v5.0.
directRenderScript
(See below for breaking changes to default<script>
behavior.)globalRoutePriority
(See below for breaking changes to default route priority order.)contentLayer
(See guidance for upgrading existing content collections to the new, preferred Content Layer API.)
The following experimental flags have been removed and their corresponding features are not part of Astro v5.0.
contentCollectionsCache
Remove these experimental flags if you were previously using them, and move your env
configuration to the root of your Astro config:
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
directRenderScript: true,
globalRoutePriority: true,
contentLayer: true,
serverIslands: true,
contentCollectionsCache: true,
env: {
schema: {...}
}
},
env: {
schema: {...}
}
})
These features are all available by default in Astro v5.0.
{/* Read about these exciting features and more in the v5.0-beta Blog post. */}
See Astro's current experimental features.
Some default behavior has changed in Astro v5.0 and your project code may need updating to account for these changes.
In most cases, the only action needed is to review your existing project's deployment and ensure that it continues to function as you expect, making updates to your code as necessary. In some cases, there may be a configuration setting to allow you to continue to use the previous default behavior.
In Astro v4.x, The default value of security.checkOrigin
was false
. Previously, you had to explicitly set this value to true
to enable Cross-Site Request Forgery (CSRF) protection.
Astro v5.0 changes the default value of this option to true
, and will automatically check that the "origin" header matches the URL sent by each request in on-demand rendered pages.
If you had previously configured security.checkOrigin: true
, you no longer need this line in your Astro config. This is now the default.
To disable this behavior, you must explicitly set security.checkOrigin: false
.
export default defineConfig({
output: "server",
security: {
checkOrigin: false
}
})
Read more about security configuration options
In Astro v4.x, experimental.globalRoutePriority
was an optional flag that ensured that injected routes, file-based routes, and redirects were all prioritized using the route priority order rules for all routes. This allowed more control over routing in your project by not automatically prioritizing certain kinds of routes and standardizing the route priority order.
Astro v5.0 removes this experimental flag and makes this the new default behavior in Astro: redirects and injected routes are now prioritized equally alongside file-based project routes.
Note that this was already the default behavior in Starlight, and should not affect updated Starlight projects.
If your project includes injected routes or redirects, please check that your routes are building page URLs as expected. An example of the new expected behavior is shown below.
In a project containing the following routes:
- File-based route:
/blog/post/[pid]
- File-based route:
/[page]
- Injected route:
/blog/[...slug]
- Redirect:
/blog/tags/[tag] -> /[tag]
- Redirect:
/posts -> /blog
The following URLs will be built (instead of following the route priority order of Astro v4.x):
/blog/tags/astro
is built by the redirect to/tags/[tag]
(instead of the injected route/blog/[...slug]
)/blog/post/0
is built by the file-based route/blog/post/[pid]
(instead of the injected route/blog/[...slug]
)/posts
is built by the redirect to/blog
(instead of the file-based route/[page]
)
In the event of route collisions, where two routes of equal route priority attempt to build the same URL, Astro will log a warning identifying the conflicting routes.
Read more about the route priority order rules.
In Astro v4.x, experimental.directRenderScript
was an optional flag to directly render <scripts>
as declared in .astro
files (including existing features like TypeScript, importing node_modules
, and deduplicating scripts). This strategy prevented scripts from being executed in places where they were not used.
Astro 5.0 removes this experimental flag and makes this the new default behavior in Astro: scripts are no longer hoisted to the <head>
, multiple scripts on a page are no longer bundled together, and a <script>
tag may interfere with CSS styling.
Please review your <script>
tags and ensure they behave as desired.
Read more about using script
tags in Astro.
The following changes are considered breaking changes in Astro v5.0. Breaking changes may or may not provide temporary backwards compatibility. If you were using these features, you may have to update your code as recommended in each entry.
{/* If you need to refer to the documentation for a v4.x project, you can browse this (unmaintained) snapshot of the docs from before v5.0 was released. */}
In Astro 4.x, Astro's View Transitions API included a <ViewTransitions />
router component to enable client-side routing, page transitions, and more.
Astro 5.0 renames this component to <ClientRouter />
to clarify the role of the component within the API. This makes it more clear that the features you get from Astro's <ClientRouter />
routing component are slightly different from the native CSS-based MPA router.
No functionality has changed. This component has only changed its name.
Replace all occurances of the ViewTransitions
import and component with ClientRouter
:
import { ViewTransitions } from 'astro:transitions';
import { ClientRouter } from 'astro:transitions';
<html>
<head>
...
<ViewTransitions />
<ClientRouter />
</head>
</html>
Read more about view transitions and client-side routing in Astro.
In Astro v4.x, Astro relied on a src/env.d.ts
file for type inferencing and defining modules for features that relied on generated types.
Astro 5.0 instead uses a .astro/types.d.ts
file for type inferencing, and now recommends setting include
and exclude
in tsconfig.json
to benefit from Astro types and avoid checking built files.
Running astro sync
no longer creates, nor updates, src/env.d.ts
as it is not required for type-checking standard Astro projects.
To update your project to Astro's recommended TypeScript settings, add the following include
and exclude
properties to your existing tsconfig.json
:
{
"extends": "astro/tsconfigs/base",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}
Note that src/env.d.ts
is only necessary if you have added custom configurations, or if you're not using a tsconfig.json
file.
Read more about TypeScript configuration in Astro.
In Astro 4.x, actions called from an HTML form would trigger a redirect with the result forwarded using cookies. This caused issues for large form errors and return values that exceeded the 4 KB limit of cookie-based storage.
Astro 5.0 now renders the result of an action as a POST result without any forwarding. This will introduce a "confirm form resubmission?" dialog when a user attempts to refresh the page, though it no longer imposes a 4 KB limit on action return value.
You should update handling for action results that relies on redirects, and optionally address the "confirm form resubmission?" dialog with middleware.
If your HTML form action is directed to a different route (i.e. action={"/success-page" + actions.name}
), Astro will no longer redirect to the previous route on error. You can implement this behavior manually using redirects from your Astro component. This example instead redirects to a new route on success, and handles errors on the current page otherwise:
---
import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.newsletter);
if (!result?.error) {
// Embed relevant result data in the URL if needed
// example: redirect(`/confirmation?email=${result.data.email}`);
return redirect('/confirmation');
}
---
<form method="POST" action={'/confirmation' + actions.newsletter}>
<label>E-mail <input required type="email" name="email" /></label>
<button>Sign up</button>
</form>
To address the "confirm form resubmission?" dialog on refresh, or to preserve action results across sessions, you can now customize action result handling from middleware.
We recommend using a session storage provider as described in our Netlify Blob example. However, if you prefer the cookie forwarding behavior from 4.X and accept the 4 KB size limit, you can implement the pattern as shown in this sample snippet:
import { defineMiddleware } from 'astro:middleware';
import { getActionContext } from 'astro:actions';
export const onRequest = defineMiddleware(async (context, next) => {
// Skip requests for prerendered pages
if (context.isPrerendered) return next();
const { action, setActionResult, serializeActionResult } = getActionContext(context);
// If an action result was forwarded as a cookie, set the result
// to be accessible from `Astro.getActionResult()`
const payload = context.cookies.get('ACTION_PAYLOAD');
if (payload) {
const { actionName, actionResult } = payload.json();
setActionResult(actionName, actionResult);
context.cookies.delete('ACTION_PAYLOAD');
return next();
}
// If an action was called from an HTML form action,
// call the action handler and redirect with the result as a cookie.
if (action?.calledFrom === 'form') {
const actionResult = await action.handler();
context.cookies.set('ACTION_PAYLOAD', {
actionName: action.name,
actionResult: serializeActionResult(actionResult),
});
if (actionResult.error) {
// Redirect back to the previous page on error
const referer = context.request.headers.get('Referer');
if (!referer) {
throw new Error('Internal: Referer unexpectedly missing from Action POST request.');
}
return context.redirect(referer);
}
// Redirect to the destination page on success
return context.redirect(context.originPathname);
}
return next();
})
In Astro 4.x, top level await was included in Markdown modules. This caused some issues with custom image services and images inside Markdown, causing Node to suddenly exit with no error message.
Astro 5.0 makes the compiledContent()
property on Markdown import an async function, requiring an await
to resolve the content.
Update your code to use await
when calling compiledContent()
.
---
import * as myPost from "../blog/post.md";
const content = myPost.compiledContent();
const content = await myPost.compiledContent();
---
<Fragment set:html={content} />
Read more about the compiledContent()
function for returning compiled Markdown.
In Astro 4.x, it was possible to access the astro:content
module on the client.
Astro 5.0 removes this access as it was never intentionally exposed for client use. Using astro:content
this way had limitations and bloated client bundles.
If you are currently currently using astro:content
in the client, pass the data you need through props to your client components instead:
---
import { getCollection } from 'astro:content';
import ClientComponent from '../components/ClientComponent';
const posts = await getCollection('blog');
const postsData = posts.map(post => post.data);
---
<ClientComponent posts={postsData} />
Read more about the astro:content
API.
In Astro v4.x, the Shiki css-variables
theme used the --astro-code-color-text
and --astro-code-color-background
tokens for styling the foreground and background colors of code blocks respectively.
Astro v5.0 renames them to --astro-code-foreground
and --astro-code-background
respectively to better align with the Shiki v1 defaults.
You can perform a global find and replace in your project to migrate to the new token names.
:root {
--astro-code-color-text: #000;
--astro-code-color-background: #fff;
--astro-code-foreground: #000;
--astro-code-background: #fff;
}
Read more about syntax highlighting in Astro.
In Astro 4.x, Astro's internal Shiki rehype plugin highlighted code blocks as HTML.
Astro 5.0 updates this plugin to highlight code blocks as hast. This allows a more direct Markdown and MDX processing and improves the performance when building the project. However, this may cause issues with existing Shiki transformers.
If you are using Shiki transformers passed to markdown.shikiConfig.transformers
, you must make sure they do not use the postprocess
hook. This hook no longer runs on code blocks in .md
and .mdx
files. (See the Shiki documentation on transformer hooks for more information).
Code blocks in .mdoc
files and Astro's built-in <Code />
component do not use the internal Shiki rehype plugin and are unaffected.
Read more about syntax highlighting in Astro.
In Astro 4.0, Markdown and MDX pages (located in src/pages/
) automatically responded with charset=utf-8
in the Content-Type
header, which allowed rendering non-ASCII characters in your pages.
Astro 5.0 updates the behaviour to add the <meta charset="utf-8">
tag instead, and only for pages that do not use Astro's special layout
frontmatter property. Similarly for MDX pages, Astro will only add the tag if the MDX content does not import a wrapping Layout
component.
If your Markdown or MDX pages use the layout
frontmatter property, or if the MDX page content imports a wrapping Layout
component, then the HTML encoding will be handled by the designated layout component instead, and the <meta charset="utf-8">
tag will not be added to your page by default.
If you require charset=utf-8
to render your page correctly, make sure that your layout components contain the <meta charset="utf-8">
tag. You may need to add this if you have not already done so.
Read more about Markdown layouts.
In Astro 4.x, the Astro-specific metadata attached to vfile.data
in remark and rehype plugins was attached in different locations with inconsistent names.
Astro 5 cleans up the API and the metadata is now renamed as below:
vfile.data.__astroHeadings
->vfile.data.astro.headings
vfile.data.imagePaths
->vfile.data.astro.imagePaths
The types of imagePaths
has also been updated from Set<string>
to string[]
. The vfile.data.astro.frontmatter
metadata is left unchanged.
While we don't consider these APIs public, they can be accessed by remark and rehype plugins that want to re-use Astro's metadata. If you are using these APIs, make sure to access them in the new locations.
Read more about using Markdown plugins in Astro.
In Astro 4.x, you could set an endpoint in your image
configuration to use for image optimization.
Astro 5.0 allows you to customize a route
and entrypoint
of the image.endpoint
config. This can be useful in niche situations where the default route /_image
conflicts with an existing route or your local server setup.
If you had previously customized image.endpoint
, move this endpoint to the new endpoint.entrypoint
property. Optionally, you may customize a route
:
import { defineConfig } from "astro/config";
defineConfig({
image: {
endpoint: './src/image-endpoint.ts',
endpoint: {
route: "/image",
entrypoint: "./src/image_endpoint.ts"
}
},
})
Read more about setting an endpoint to use for image optimization.
In Astro v4.x, the build.client
and build.server
options were documented to resolve relatively from the outDir
option, but it didn't always work as expected.
Astro 5.0 fixes the behavior to correctly resolve from the outDir
option. For example, if outDir
is set to ./dist/nested/
, then by default:
build.client
will resolve to<root>/dist/nested/client/
build.server
will resolve to<root>/dist/nested/server/
Previously the values were incorrectly resolved:
build.client
was resolved to<root>/dist/nested/dist/client/
build.server
was resolved to<root>/dist/nested/dist/server/
If you were relying on the previous build paths, make sure that your project code is updated to the new build paths.
Read more about build
configuration options in Astro.
In Astro 4.x, locally-linked JS dependencies (e.g. npm link
, in a monorepo, etc) were able to use Vite features like import.meta.glob
when imported by the Astro config file.
Astro 5 updates the Astro config loading flow to ignore processing locally-linked JS dependencies with Vite. Dependencies exporting raw TypeScript files are unaffected. Instead, these JS dependencies will be normally imported by the Node.js runtime the same way as other dependencies from node_modules
.
This change was made as the previous behavior caused confusion among integration authors who tested against a package that worked locally, but not when published. It also restricted using CJS-only dependencies because Vite required the code to be ESM. While this change only affects JS dependencies, it's also recommended for packages to export JavaScript instead of raw TypeScript where possible to prevent accidental Vite-specific usage as it's an implementation detail of Astro's config loading flow.
Make sure your locally-linked JS dependencies are built before running your Astro project. Then, the config loading should work as before.
Read more about Vite configuration settings in Astro.
In Astro v4.x, the URL returned by paginate()
(e.g. page.url.next
, page.url.first
, etc.) did not include the value set for base
in your Astro config. You had to manually prepend your configured value for base
to the URL path.
Astro 5.0 automatically includes the base
value in page.url
.
If you are using the paginate()
function for these URLs, remove any existing base
value as it is now added for you:
---
export async function getStaticPaths({ paginate }) {
const astronautPages = [{
astronaut: 'Neil Armstrong',
}, {
astronaut: 'Buzz Aldrin',
}, {
astronaut: 'Sally Ride',
}, {
astronaut: 'John Glenn',
}];
return paginate(astronautPages, { pageSize: 1 });
}
const { page } = Astro.props;
// `base: /'docs'` configured in `astro.config.mjs`
const prev = "/docs" + page.url.prev;
const prev = page.url.prev;
---
<a id="prev" href={prev}>Back</a>
Read more about pagination in Astro.
In Astro v4.x, non-boolean HTML attributes may not have included their values when rendered to HTML.
Astro v5.0 renders the values explicitly as ="true"
or ="false"
, matching proper attribute handling in browsers.
In the following .astro
examples, only allowfullscreen
is a boolean attribute:
<!-- src/pages/index.astro -->
<!-- `allowfullscreen` is a boolean attribute -->
<p allowfullscreen={true}></p>
<p allowfullscreen={false}></p>
<!-- `inherit` is *not* a boolean attribute -->
<p inherit={true}></p>
<p inherit={false}></p>
<!-- `data-*` attributes are not boolean attributes -->
<p data-light={true}></p>
<p data-light={false}></p>
Astro v5.0 now preserves the full data attribute with its value when rendering the HTML of non-boolean attributes:
<p allowfullscreen></p>
<p></p>
<p inherit="true"></p>
<p inherit></p>
<p inherit="false"></p>
<p data-light></p>
<p data-light="true"></p>
<p></p>
<p data-light="false"></p>
If you rely on attribute values, for example, to locate elements or to conditionally render, update your code to match the new non-boolean attribute values:
el.getAttribute('inherit') === ''
el.getAttribute('inherit') === 'false'
el.hasAttribute('data-light')
el.dataset.light === 'true'
Read more about using HTML attributes in Astro.
In Astro 4.x, it was possible to completely replace the entire locals
object in middleware, API endpoints, and pages when adding new values.
Astro 5.0 requires you to append values to the existing locals
object without deleting it. Locals in middleware, API endpoints, and pages, can no longer be completely overridden.
Where you previously were overwriting the object, you must now instead assign values to it:
ctx.locals = {
Object.assign(ctx.locals, {
one: 1,
two: 2
}
})
See more about storing data in context.locals
.
In Astro v4.x, params
passed to getStaticPath()
were automatically decoded using decodeURIComponent
.
Astro v5.0 no longer decodes the value of params
passed to getStaticPaths
. You must manually decode them yourself if needed.
If you were previously relying on the automatic decoding, use decodeURI
when passing params
.
---
export function getStaticPaths() {
return [
{ params: { id: "%5Bpage%5D" } },
{ params: { id: decodeURI("%5Bpage%5D") } },
]
}
const { id } = Astro.params;
---
Note that the use of decodeURIComponent
) is discouraged for getStaticPaths
because it decodes more characters than it should, for example /
, ?
, #
and more.
Read more about creating dynamic routes with params
.
In Astro v4.x, the entryPoints
type inside the astro:build:ssr
and astro:build:done
hooks was RouteData
.
Astro v5.0 the entryPoints
type is now IntegrationRouteData
, which contains a subset of the RouteData
type. The fields isIndex
and fallbackRoutes
were removed.
Update your adapter to change the type of entryPoints
from RouteData
to IntegrationRouteData
.
import type {RouteData} from 'astro';
import type {IntegrationRouteData} from "astro"
function useRoute(route: RouteData) {
function useRoute(route: IntegrationRouteData) {
}
See the API reference for IntegrationRouteData
.
In Astro v4.x, RouteData.distURL
was undefined
or a URL
Astro v5.0 updates the shape of IntegrationRouteData.distURL
to be undefined
or an array of URL
s. This fixes a previous error because a route can generate multiple files on disk, especially when using dynamic routes such as [slug]
or [...slug]
.
Update your code to handle IntegrationRouteData.distURL
as an array.
if (route.distURL) {
if (route.distURL.endsWith('index.html')) {
// do something
}
for (const url of route.distURL) {
if (url.endsWith('index.html')) {
// do something
}
}
}
See the API reference for IntegrationRouteData
.
In Astro 4.x, The Adapter API method app.render()
could receive three arguments: a mandatory request
, an object of options or a routeData
object, and locals
.
Astro 5.0 combines these last two arguments into a single options argument named renderOptions
.
Pass an object as the second argument to app.render()
, which can include routeData
and locals
as properties.
const response = await app.render(request, routeData, locals);
const response = await app.render(request, {routeData, locals});
See the Adapter API reference for renderOptions
.
In Astro 4.x, supportedAstroFeatures
, which allows adapter authors to specify which features their integration supports, included an assets
property to specify which of Astro's image services were supported.
Astro 5.0 replaces this property with a dedicated sharpImageService
property, used to determine whether the adapter is compatible with the built-in sharp image service.
v5.0 also adds a new limited
value for the different properties of supportedAstroFeatures
for adapters, which indicates that the adapter is compatible with the feature, but with some limitations. This is useful for adapters that support a feature, but not in all cases or with all options.
Additionally, the value of the different properties on supportedAstroFeatures
for adapters can now be objects, with support
and message
properties. The content of the message
property will show a helpful message in the Astro CLI when the adapter is not compatible with a feature. This is notably useful with the new limited
value, to explain to the user why support is limited.
If you were using the assets
property, remove this as it is no longer available. To specify that your adapter supports the built-in sharp image service, replace this with sharpImageService
.
You may also wish to update your supported features with the new limited
option and include a message about your adapter's support.
supportedAstroFeatures: {
assets: {
supportKind: "stable",
isSharpCompatible: true,
isSquooshCompatible: true,
},
sharpImageService: {
support: "limited",
message: 'This adapter supports the built-in sharp image service, but with some limitations.'
}
}
Read more about specifying supported Astro features in an adapter.
In Astro 4.x, when building a dev toolbar app, it was still possible to use the previously deprecated addDevToolbarApp(string);
signature. The id
, title
, and icon
properties to define the app were then made available through the default export of the app's entrypoint
.
Astro 5.0 completely removes this option entirely in favor of the current object shape when defining a dev toolbar app in an integration that's more intuitive and allows Astro to provide better errors when toolbar apps fail to load correctly.
If you were using the deprecated shape, update your dev toolbar app to use the new shape:
// Old shape
addDevToolbarApp("./my-app.js");
// New shape
addDevToolbarApp({
id: "my-app",
name: "My App",
icon: "<svg>...</svg>",
entrypoint: "./my-app.js",
});
export default {
id: 'my-dev-toolbar-app',
title: 'My Dev Toolbar App',
icon: '🚀',
init() {
// ...
}
}
Read more about developing a dev toolbar app for Astro using the Dev Toolbar API.
Know a good resource for Astro v5.0? Edit this page and add a link below!
Please check Astro's issues on GitHub for any reported issues, or to file an issue yourself.
In most cases, ensuring that your locally-linked dependencies are built to JS before running the Astro project, the config loading should work as before.