Changelog
✨ Major Changes
-
#12008
5608338Thanks @Princesseuh! - Welcome to the Astro 5 beta! This release has no changes from the latest alpha of this package, but it does bring us one step closer to the final, stable release.Starting from this release, no breaking changes will be introduced unless absolutely necessary.
To learn how to upgrade, check out the Astro v5.0 upgrade guide in our beta docs site.
🐞 Patch Changes
- Updated dependencies [
5608338]:- @astrojs/markdown-remark@6.0.0-beta.1
-
✨ Major Changes
-
#11982
d84e444Thanks @Princesseuh! - Adds a default exclude and include value to the tsconfig presets.{projectDir}/distis now excluded by default, and{projectDir}/.astro/types.d.tsand{projectDir}/**/*are included by default.Both of these options can be overridden by setting your own values to the corresponding settings in your
tsconfig.jsonfile. -
#11987
bf90a53Thanks @florian-lefebvre! - Thelocalsobject can no longer be overriddenMiddleware, API endpoints, and pages can no longer override the
localsobject in its entirety. You can still append values onto the object, but you can not replace the entire object and delete its existing values.If you were previously overwriting like so:
ctx.locals = {one: 1,two: 2,};This can be changed to an assignment on the existing object instead:
Object.assign(ctx.locals, {one: 1,two: 2,});
🍿 Minor Changes
-
#11980
a604a0cThanks @matthewp! - ViewTransitions component renamed to ClientRouterThe
<ViewTransitions />component has been renamed to<ClientRouter />. There are no other changes than the name. The old name will continue to work in Astro 5.x, but will be removed in 6.0.This change was done to clarify the role of the component within Astro’s View Transitions support. Astro supports View Transitions APIs in a few different ways, and renaming the component makes it more clear that the features you get from the ClientRouter component are slightly different from what you get using the native CSS-based MPA router.
We still intend to maintain the ClientRouter as before, and it’s still important for use-cases that the native support doesn’t cover, such as persisting state between pages.
🐞 Patch Changes
-
#11987
bf90a53Thanks @florian-lefebvre! -render()signature now takesrenderOptionsas 2nd argumentThe signature for
app.render()has changed, and the second argument is now an options object calledrenderOptionswith more options for customizing rendering.The
renderOptionsare:addCookieHeader: Determines whether Astro will set theSet-Cookieheader, otherwise the adapter is expected to do so itself.clientAddress: The client IP address used to setAstro.clientAddress.locals: An object of locals that’s set toAstro.locals.routeData: An object specifying the route to use.
-
#11991
d7a396cThanks @matthewp! - Update error link to on-demand rendering guide
-
✨ Major Changes
-
#11864
ee38b3aThanks @ematipico! - ### [changed]:entryPointtype inside the hookastro:build:ssrIn Astro v4.x, theentryPointtype wasRouteData.Astro v5.0 the
entryPointtype isIntegrationRouteData, which contains a subset of theRouteDatatype. The fieldsisIndexandfallbackRouteswere removed.What should I do?
Update your adapter to change the type of
entryPointfromRouteDatatoIntegrationRouteData.import type {RouteData} from 'astro';import type {IntegrationRouteData} from "astro"function useRoute(route: RouteData) {function useRoute(route: IntegrationRouteData) {} -
#11908
518433eThanks @Princesseuh! - Theimage.endpointconfig now allow customizing the route of the image endpoint in addition to the entrypoint. This can be useful in niche situations where the default route/_imageconflicts with an existing route or your local server setup.import { defineConfig } from 'astro/config';defineConfig({image: {endpoint: {route: '/image',entrypoint: './src/image_endpoint.ts',},},}); -
#11806
f7f2338Thanks @Princesseuh! - Removes theassetsproperty onsupportedAstroFeaturesfor adapters, as it did not reflect reality properly in many cases.Now, relating to assets, only a single
sharpImageServiceproperty is available, determining if the adapter is compatible with the built-in sharp image service. -
#11864
ee38b3aThanks @ematipico! - ### [changed]:routestype inside the hookastro:build:doneIn Astro v4.x, theroutestype wasRouteData.Astro v5.0 the
routestype isIntegrationRouteData, which contains a subset of theRouteDatatype. The fieldsisIndexandfallbackRouteswere removed.What should I do?
Update your adapter to change the type of
routesfromRouteDatatoIntegrationRouteData.import type {RouteData} from 'astro';import type {IntegrationRouteData} from "astro"function useRoute(route: RouteData) {function useRoute(route: IntegrationRouteData) {} -
#11864
ee38b3aThanks @ematipico! - ### [changed]:RouteData.distURLis now an array In Astro v4.x,RouteData.distURLwasundefinedor aURLAstro v5.0,
RouteData.distURLisundefinedor an array ofURL. This was a bug, because a route can generate multiple files on disk, especially when using dynamic routes such as[slug]or[...slug].What should I do?
Update your code to handle
RouteData.distURLas 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}}}
🍿 Minor Changes
-
#11806
f7f2338Thanks @Princesseuh! - The value of the different properties onsupportedAstroFeaturesfor adapters can now be objects, with asupportandmessageproperties. The content of themessageproperty will be shown in the Astro CLI when the adapter is not compatible with the feature, allowing one to give a better informational message to the user.This is notably useful with the new
limitedvalue, to explain to the user why support is limited. -
#11955
d813262Thanks @matthewp! - Server Islands introduced behind an experimental flag in v4.12.0 is no longer experimental and is available for general use.Server islands are Astro’s solution for highly cacheable pages of mixed static and dynamic content. They allow you to specify components that should run on the server, allowing the rest of the page to be more aggressively cached, or even generated statically.
Turn any
.astrocomponent into a server island by adding theserver:deferdirective and optionally, fallback placeholder content. It will be rendered dynamically at runtime outside the context of the rest of the page, allowing you to add longer cache headers for the pages, or even prerender them.---import Avatar from '../components/Avatar.astro';import GenericUser from '../components/GenericUser.astro';---<header><h1>Page Title</h1><div class="header-right"><Avatar server:defer><GenericUser slot="fallback" /></Avatar></div></header>If you were previously using this feature, please remove the experimental flag from your Astro config:
import { defineConfig } from 'astro/config';export default defineConfig({experimental {serverIslands: true,},});If you have been waiting for stabilization before using server islands, you can now do so.
Please see the server island documentation for more about this feature.
-
#11806
f7f2338Thanks @Princesseuh! - Adds a newlimitedvalue for the different properties ofsupportedAstroFeaturesfor 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. -
#11925
74722cbThanks @florian-lefebvre! - Updatesastro/configimport to referenceastro/clienttypesWhen importing
astro/config, types fromastro/clientwill be made automatically available to your project. If your projecttsconfig.jsonchanges how references behave, you’ll still have access to these types after runningastro sync.
🐞 Patch Changes
-
#11974
60211deThanks @ascorbic! - Exports theRenderResulttype -
#11939
7b09c62Thanks @bholmesdev! - Adds support for Zod discriminated unions on Action form inputs. This allows forms with different inputs to be submitted to the same action, using a given input to decide which object should be used for validation.This example accepts either a
createorupdateform submission, and uses thetypefield to determine which object to validate against.import { defineAction } from 'astro:actions';import { z } from 'astro:schema';export const server = {changeUser: defineAction({accept: 'form',input: z.discriminatedUnion('type', [z.object({type: z.literal('create'),name: z.string(),email: z.string().email(),}),z.object({type: z.literal('update'),id: z.number(),name: z.string(),email: z.string().email(),}),]),async handler(input) {if (input.type === 'create') {// input is { type: 'create', name: string, email: string }} else {// input is { type: 'update', id: number, name: string, email: string }}},}),};The corresponding
createandupdateforms may look like this:---import { actions } from 'astro:actions';---<!--Create--><form action={actions.changeUser} method="POST"><input type="hidden" name="type" value="create" /><input type="text" name="name" required /><input type="email" name="email" required /><button type="submit">Create User</button></form><!--Update--><form action={actions.changeUser} method="POST"><input type="hidden" name="type" value="update" /><input type="hidden" name="id" value="user-123" /><input type="text" name="name" required /><input type="email" name="email" required /><button type="submit">Update User</button></form>
-
🐞 Patch Changes
-
#11939
7b09c62Thanks @bholmesdev! - Adds support for Zod discriminated unions on Action form inputs. This allows forms with different inputs to be submitted to the same action, using a given input to decide which object should be used for validation.This example accepts either a
createorupdateform submission, and uses thetypefield to determine which object to validate against.import { defineAction } from 'astro:actions';import { z } from 'astro:schema';export const server = {changeUser: defineAction({accept: 'form',input: z.discriminatedUnion('type', [z.object({type: z.literal('create'),name: z.string(),email: z.string().email(),}),z.object({type: z.literal('update'),id: z.number(),name: z.string(),email: z.string().email(),}),]),async handler(input) {if (input.type === 'create') {// input is { type: 'create', name: string, email: string }} else {// input is { type: 'update', id: number, name: string, email: string }}},}),};The corresponding
createandupdateforms may look like this:---import { actions } from 'astro:actions';---<!--Create--><form action={actions.changeUser} method="POST"><input type="hidden" name="type" value="create" /><input type="text" name="name" required /><input type="email" name="email" required /><button type="submit">Create User</button></form><!--Update--><form action={actions.changeUser} method="POST"><input type="hidden" name="type" value="update" /><input type="hidden" name="id" value="user-123" /><input type="text" name="name" required /><input type="email" name="email" required /><button type="submit">Update User</button></form> -
#11968
86ad1fdThanks @NikolaRHristov! - Fixes a typo in the server island JSDoc -
#11983
633eeaaThanks @uwej711! - Remove dependency on path-to-regexp
-
✨ Major Changes
-
#11941
b6a5f39Thanks @Princesseuh! - Merges theoutput: 'hybrid'andoutput: 'static'configurations into one single configuration (now called'static') that works the same way as the previoushybridoption.It is no longer necessary to specify
output: 'hybrid'in your Astro config to use server-rendered pages. The newoutput: 'static'has this capability included. Astro will now automatically provide the ability to opt out of prerendering in your static site with no change to youroutputconfiguration required. Any page route or endpoint can includeexport const prerender = falseto be server-rendered, 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'.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.import { defineConfig } from "astro/config";export default defineConfig({output: 'hybrid',});An adapter is still required to deploy an Astro project with any server-rendered pages. Failure to include an adapter will result in a warning in development and an error at build time.
🍿 Minor Changes
-
#11941
b6a5f39Thanks @Princesseuh! - Adapters can now specify the build output type they’re intended for using theadapterFeatures.buildOutputproperty. This property can be used to always generate a server output, even if the project doesn’t have any server-rendered pages.{'astro:config:done': ({ setAdapter, config }) => {setAdapter({name: 'my-adapter',adapterFeatures: {buildOutput: 'server',},});},}If your adapter specifies
buildOutput: 'static', and the user’s project contains server-rendered pages, Astro will warn in development and error at build time. Note that a hybrid output, containing both static and server-rendered pages, is considered to be aserveroutput, as a server is required to serve the server-rendered pages. -
#11941
b6a5f39Thanks @Princesseuh! - Adds a newbuildOutputproperty to theastro:config:donehook returning the build output type.This can be used to know if the user’s project will be built as a static site (HTML files), or a server-rendered site (whose exact output depends on the adapter).
🐞 Patch Changes
-
#11960
4410130Thanks @ascorbic! - Fixes an issue where the refresh context data was not passed correctly to content layer loaders -
#11952
50a0146Thanks @ascorbic! - Adds support for array patterns in the built-inglob()content collections loaderThe glob loader can now accept an array of multiple patterns as well as string patterns. This allows you to more easily combine multiple patterns into a single collection, and also means you can use negative matches to exclude files from the collection.
const probes = defineCollection({// Load all markdown files in the space-probes directory, except for those that start with "voyager-"loader: glob({ pattern: ['*.md', '!voyager-*'], base: 'src/data/space-probes' }),schema: z.object({name: z.string(),type: z.enum(['Space Probe', 'Mars Rover', 'Comet Lander']),launch_date: z.date(),status: z.enum(['Active', 'Inactive', 'Decommissioned']),destination: z.string(),operator: z.string(),notable_discoveries: z.array(z.string()),}),});
-
✨ Major Changes
-
#11916
46ea29fThanks @bluwy! - Updates how thebuild.clientandbuild.serveroption values get resolved to match existing documentation. With this fix, the option values will now correctly resolve relative to theoutDiroption. So ifoutDiris set to./dist/nested/, then by default:build.clientwill resolve to<root>/dist/nested/client/build.serverwill resolve to<root>/dist/nested/server/
Previously the values were incorrectly resolved:
build.clientwas resolved to<root>/dist/nested/dist/client/build.serverwas 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.
🍿 Minor Changes
-
#11875
a8a3d2cThanks @florian-lefebvre! - Adds a new propertyisPrerenderedto the globalsAstroandAPIContext. This boolean value represents whether or not the current page is prerendered:src/pages/index.astro ---export const prerender = true;---src/middleware.js export const onRequest = (ctx, next) => {console.log(ctx.isPrerendered); // it will log truereturn next();};
🐞 Patch Changes
-
#11927
5b4e3abThanks @florian-lefebvre! - Updates theenvconfiguration reference docs to include a full API reference forenvField. -
#11943
fa4671cThanks @sarah11918! - Updates error messages that assume content collections are located insrc/content/with more generic language
-
🐞 Patch Changes
-
#11879
bd1d4aaThanks @matthewp! - Allow passing a cryptography key via ASTRO_KEYFor Server islands Astro creates a cryptography key in order to hash props for the islands, preventing accidental leakage of secrets.
If you deploy to an environment with rolling updates then there could be multiple instances of your app with different keys, causing potential key mismatches.
To fix this you can now pass the
ASTRO_KEYenvironment variable to your build in order to reuse the same key.To generate a key use:
astro create-keyThis will print out an environment variable to set like:
ASTRO_KEY=PIAuyPNn2aKU/bviapEuc/nVzdzZPizKNo3OqF/5PmQ= -
#11935
c58193aThanks @Princesseuh! - Fixesastro addnot using the proper export point when adding certain adapters
-
🐞 Patch Changes
-
#11902
d63bc50Thanks @ascorbic! - Fixes case where content layer did not update during clean dev builds on Linux and Windows -
#11886
7ff7134Thanks @matthewp! - Fixes a missing error message when actions throws duringastro sync -
#11904
ca54e3fThanks @wtchnm! - perf(assets): avoid downloading original image when using cache
-
✨ Major Changes
-
#11859
3804711Thanks @florian-lefebvre! - Changes the defaulttsconfig.jsonwith better defaults, and makessrc/env.d.tsoptionalAstro’s default
tsconfig.jsonin starter examples has been updated to include generated types and exclude your build output. This means thatsrc/env.d.tsis only necessary if you have added custom type declarations or if you’re not using atsconfig.jsonfile.Additionally, running
astro syncno longer creates, nor updates,src/env.d.tsas it is not required for type-checking standard Astro projects.To update your project to Astro’s recommended TypeScript settings, please add the following
includeandexcludeproperties totsconfig.json:{"extends": "astro/tsconfigs/base","include": ["**/*", ".astro/types.d.ts"],"exclude": ["dist"]}
🍿 Minor Changes
-
#11911
c3dce83Thanks @ascorbic! - The Content Layer API introduced behind a flag in 4.14.0 is now stable and ready for use in Astro v5.0.The new Content Layer API builds upon content collections, taking them beyond local files in
src/content/and allowing you to fetch content from anywhere, including remote APIs. These new collections work alongside your existing content collections, and you can migrate them to the new API at your own pace. There are significant improvements to performance with large collections of local files. For more details, see the Content Layer RFC.If you previously used this feature, you can now remove the
experimental.contentLayerflag from your Astro config:astro.config.mjs import { defineConfig } from 'astro'export default defineConfig({experimental: {contentLayer: true}})Loading your content
The core of the new Content Layer API is the loader, a function that fetches content from a source and caches it in a local data store. Astro 4.14 ships with built-in
glob()andfile()loaders to handle your local Markdown, MDX, Markdoc, and JSON files:src/content/config.ts import { defineCollection, z } from 'astro:content';import { glob } from 'astro/loaders';const blog = defineCollection({// The ID is a slug generated from the path of the file relative to `base`loader: glob({ pattern: '**/*.md', base: './src/data/blog' }),schema: z.object({title: z.string(),description: z.string(),publishDate: z.coerce.date(),}),});export const collections = { blog };You can then query using the existing content collections functions, and use a simplified
render()function to display your content:---import { getEntry, render } from 'astro:content';const post = await getEntry('blog', Astro.params.slug);const { Content } = await render(entry);---<Content />Creating a loader
You’re not restricted to the built-in loaders – we hope you’ll try building your own. You can fetch content from anywhere and return an array of entries:
src/content/config.ts const countries = defineCollection({loader: async () => {const response = await fetch('https://restcountries.com/v3.1/all');const data = await response.json();// Must return an array of entries with an id property,// or an object with IDs as keys and entries as valuesreturn data.map((country) => ({id: country.cca3,...country,}));},// optionally add a schema to validate the data and make it type-safe for users// schema: z.object...});export const collections = { countries };For more advanced loading logic, you can define an object loader. This allows incremental updates and conditional loading, and gives full access to the data store. It also allows a loader to define its own schema, including generating it dynamically based on the source API. See the the Content Layer API RFC for more details.
Sharing your loaders
Loaders are better when they’re shared. You can create a package that exports a loader and publish it to npm, and then anyone can use it on their site. We’re excited to see what the community comes up with! To get started, take a look at some examples. Here’s how to load content using an RSS/Atom feed loader:
src/content/config.ts import { defineCollection } from 'astro:content';import { feedLoader } from '@ascorbic/feed-loader';const podcasts = defineCollection({loader: feedLoader({url: 'https://feeds.99percentinvisible.org/99percentinvisible',}),});export const collections = { podcasts };To learn more, see the Content Layer RFC.
🐞 Patch Changes
-
#11902
d63bc50Thanks @ascorbic! - Fixes case where content layer did not update during clean dev builds on Linux and Windows -
#11914
b5d827bThanks @ascorbic! - Exports types for allLoaderContextproperties fromastro/loadersto make it easier to use them in custom loaders. TheScopedDataStoreinterface (which was previously internal) is renamed toDataStore, to reflect the fact that it’s the only public API for the data store.
-
✨ Major Changes
-
#11861
3ab3b4eThanks @bluwy! - Cleans up Astro-specfic metadata attached tovfile.datain Remark and Rehype plugins. Previously, the metadata was attached in different locations with inconsistent names. The metadata is now renamed as below:vfile.data.__astroHeadings->vfile.data.astro.headingsvfile.data.imagePaths->vfile.data.astro.imagePaths
The types of
imagePathshas also been updated fromSet<string>tostring[]. Thevfile.data.astro.frontmattermetadata 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.
-
#11825
560ef15Thanks @bluwy! - Updates internal Shiki rehype plugin to highlight code blocks as hast (using Shiki’scodeToHast()API). This allows a more direct Markdown and MDX processing, and improves the performance when building the project, but 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 thepostprocesshook as it no longer runs on code blocks in.mdand.mdxfiles. (See the Shiki documentation on transformer hooks for more information).Code blocks in
.mdocfiles and<Code />component do not use the internal Shiki rehype plugin and are unaffected. -
#11819
2bdde80Thanks @bluwy! - Updates the Astro config loading flow to ignore processing locally-linked dependencies with Vite (e.g.npm link, in a monorepo, etc). Instead, they will be normally imported by the Node.js runtime the same way as other dependencies fromnode_modules.Previously, Astro would process locally-linked dependencies which were able to use Vite features like TypeScript when imported by the Astro config file.
However, this caused confusion as integration authors may test against a package that worked locally, but not when published. This method also restricts using CJS-only dependencies because Vite requires the code to be ESM. Therefore, Astro’s behaviour is now changed to ignore processing any type of dependencies by Vite.
In most cases, make sure your locally-linked dependencies are built to JS before running the Astro project, and the config loading should work as before.
🐞 Patch Changes
-
#11878
334948cThanks @ascorbic! - Adds a new functionrefreshContentto theastro:server:setuphook that allows integrations to refresh the content layer. This can be used, for example, to register a webhook endpoint during dev, or to open a socket to a CMS to listen for changes.By default,
refreshContentwill refresh all collections. You can optionally pass aloadersproperty, which is an array of loader names. If provided, only collections that use those loaders will be refreshed. For example, A CMS integration could use this property to only refresh its own collections.You can also pass a
contextobject to the loaders. This can be used to pass arbitrary data, such as the webhook body, or an event from the websocket.{name: 'my-integration',hooks: {'astro:server:setup': async ({ server, refreshContent }) => {server.middlewares.use('/_refresh', async (req, res) => {if(req.method !== 'POST') {res.statusCode = 405res.end('Method Not Allowed');return}let body = '';req.on('data', chunk => {body += chunk.toString();});req.on('end', async () => {try {const webhookBody = JSON.parse(body);await refreshContent({context: { webhookBody },loaders: ['my-loader']});res.writeHead(200, { 'Content-Type': 'application/json' });res.end(JSON.stringify({ message: 'Content refreshed successfully' }));} catch (error) {res.writeHead(500, { 'Content-Type': 'application/json' });res.end(JSON.stringify({ error: 'Failed to refresh content: ' + error.message }));}});});}}} -
Updated dependencies [
3ab3b4e,560ef15,3ab3b4e]:- @astrojs/markdown-remark@6.0.0-alpha.1
-
🐞 Patch Changes
-
#11870
8e5257aThanks @ArmandPhilippot! - Fixes typo in documenting thefallbackTypeproperty in i18n routing -
#11884
e450704Thanks @ascorbic! - Correctly handles content layer data where the transformed value does not match the input schema -
#11900
80b4a18Thanks @delucis! - Fixes the user-facing type of the newi18n.routing.fallbackTypeoption to be optional
-
✨ Major Changes
-
#11826
7315050Thanks @matthewp! - Deprecate Astro.globThe
Astro.globfunction has been deprecated in favor of Content Collections andimport.meta.glob.- If you want to query for markdown and MDX in your project, use Content Collections.
- If you want to query source files in your project, use
import.meta.glob(https://vitejs.dev/guide/features.html#glob-import).
Also consider using glob packages from npm, like fast-glob, especially if statically generating your site, as it is faster for most use-cases.
The easiest path is to migrate to
import.meta.globlike so:const posts = Astro.glob('./posts/*.md');const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true })); -
#11827
a83e362Thanks @matthewp! - Prevent usage ofastro:contentin the clientUsage of
astro:contentin the client has always been discouraged because it leads to all of your content winding up in your client bundle, and can possibly leaks secrets.This formally makes doing so impossible, adding to the previous warning with errors.
In the future Astro might add APIs for client-usage based on needs.
-
#11253
4e5cc5aThanks @kevinzunigacuellar! - Changes the data returned forpage.url.current,page.url.next,page.url.prev,page.url.firstandpage.url.lastto include the value set forbasein your Astro config.Previously, you had to manually prepend your configured value for
baseto the URL path. Now, Astro automatically includes yourbasevalue innextandprevURLs.If you are using the
paginate()function for “previous” and “next” URLs, remove any existingbasevalue 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>
🍿 Minor Changes
-
#11698
05139efThanks @ematipico! - Adds a new property to the globalsAstroandAPIContextcalledroutePattern. TheroutePatternrepresents the current route (component) that is being rendered by Astro. It’s usually a path pattern will look like this:blog/[slug]:src/pages/blog/[slug].astro ---const route = Astro.routePattern;console.log(route); // it will log "blog/[slug]"---src/pages/index.js export const GET = (ctx) => {console.log(ctx.routePattern); // it will log src/pages/index.jsreturn new Response.json({ loreum: 'ipsum' });};
🐞 Patch Changes
-
#11791
9393243Thanks @bluwy! - Updates Astro’s default<script>rendering strategy and removes theexperimental.directRenderScriptoption as this is now the default behavior: scripts are always rendered directly. This new strategy prevents scripts from being executed in pages where they are not used.Scripts will directly render as declared in Astro files (including existing features like TypeScript, importing
node_modules, and deduplicating scripts). You can also now conditionally render scripts in your Astro file.However, this means scripts are no longer hoisted to the
<head>, multiple scripts on a page are no longer bundled together, and the<script>tag may interfere with the CSS styling.As this is a potentially breaking change to your script behavior, please review your
<script>tags and ensure that they behave as expected. -
#11767
d1bd1a1Thanks @ascorbic! - Refactors content layer sync to use a queue
-

🍿 Minor Changes
-
#11729
1c54e63Thanks @ematipico! - Adds a new variantsyncfor theastro:config:setuphook’scommandproperty. This value is set when calling the commandastro sync.If your integration previously relied on knowing how many variants existed for the
commandproperty, you must update your logic to account for this new option. -
#11743
cce0894Thanks @ph1p! - Adds a new, optional propertytimeoutfor theclient:idledirective.This value allows you to specify a maximum time to wait, in milliseconds, before hydrating a UI framework component, even if the page is not yet done with its initial load. This means you can delay hydration for lower-priority UI elements with more control to ensure your element is interactive within a specified time frame.
<ShowHideButton client:idle={{ timeout: 500 }} /> -
#11677
cb356a5Thanks @ematipico! - Adds a new optionfallbackTypetoi18n.routingconfiguration that allows you to control how fallback pages are handled.When
i18n.fallbackis configured, this new routing option controls whether to redirect to the fallback page, or to rewrite the fallback page’s content in place.The
"redirect"option is the default value and matches the current behavior of the existing fallback system.The option
"rewrite"uses the new rewriting system to create fallback pages that render content on the original, requested URL without a browser refresh.For example, the following configuration will generate a page
/fr/index.htmlthat will contain the same HTML rendered by the page/en/index.htmlwhensrc/pages/fr/index.astrodoes not exist.astro.config.mjs export default defineConfig({i18n: {locals: ['en', 'fr'],defaultLocale: 'en',routing: {prefixDefaultLocale: true,fallbackType: 'rewrite',},fallback: {fr: 'en',},},}); -
#11708
62b0d20Thanks @martrapp! - Adds a new objectswapFunctionsto expose the necessary utility functions onastro:transitions/clientthat allow you to build custom swap functions to be used with view transitions.The example below uses these functions to replace Astro’s built-in default
swapfunction with one that only swaps the<main>part of the page:<script>import { swapFunctions } from 'astro:transitions/client';document.addEventListener('astro:before-swap', (e) => { e.swap = () => swapMainOnly(e.newDocument) });function swapMainOnly(doc: Document) {swapFunctions.deselectScripts(doc);swapFunctions.swapRootAttributes(doc);swapFunctions.swapHeadElements(doc);const restoreFocusFunction = swapFunctions.saveFocus();const newMain = doc.querySelector('main');const oldMain = document.querySelector('main');if (newMain && oldMain) {swapFunctions.swapBodyElement(newMain, oldMain);} else {swapFunctions.swapBodyElement(doc.body, document.body);}restoreFocusFunction();};</script>See the view transitions guide for more information about hooking into the
astro:before-swaplifecycle event and adding a custom swap implementation. -
#11843
5b4070eThanks @bholmesdev! - Exposeszfrom the newastro:schemamodule. This is the new recommended import source for all Zod utilities when using Astro Actions.zwill no longer be exposed fromastro:actions. To usezin your actions, import it fromastro:schemainstead:import {defineAction,z,} from 'astro:actions';import { z } from 'astro:schema'; -
#11843
5b4070eThanks @bholmesdev! - The Astro Actions API introduced behind a flag in v4.8.0 is no longer experimental and is available for general use.Astro Actions allow you to define and call backend functions with type-safety, performing data fetching, JSON parsing, and input validation for you.
Actions can be called from client-side components and HTML forms. This gives you to flexibility to build apps using any technology: React, Svelte, HTMX, or just plain Astro components. This example calls a newsletter action and renders the result using an Astro component:
src/pages/newsletter.astro ---import { actions } from 'astro:actions';const result = Astro.getActionResult(actions.newsletter);---{result && !result.error && <p>Thanks for signing up!</p>}<form method="POST" action={actions.newsletter}><input type="email" name="email" /><button>Sign up</button></form>If you were previously using this feature, please remove the experimental flag from your Astro config:
import { defineConfig } from 'astro'export default defineConfig({experimental: {actions: true,}})If you have been waiting for stabilization before using Actions, you can now do so.
For more information and usage examples, see our brand new Actions guide.
🐞 Patch Changes
-
#11677
cb356a5Thanks @ematipico! - Fixes a bug in the logic ofAstro.rewrite()which led to the value forbase, if configured, being automatically prepended to the rewrite URL passed. This was unintended behavior and has been corrected, and Astro now processes the URLs exactly as passed.If you use the
rewrite()function on a project that hasbaseconfigured, you must now prepend the base to your existing rewrite URL:astro.config.mjs export default defineConfig({base: '/blog',});src/middleware.js export function onRequest(ctx, next) {return ctx.rewrite("/about")return ctx.rewrite("/blog/about")} -
#11862
0e35afeThanks @ascorbic! - BREAKING CHANGE to experimental content layer loaders only!Passes
AstroConfiginstead ofAstroSettingsobject to content layer loaders.This will not affect you unless you have created a loader that uses the
settingsobject. If you have, you will need to update your loader to use theconfigobject instead.export default function myLoader() {return {name: 'my-loader'async load({ settings }) {const base = settings.config.base;async load({ config }) {const base = config.base;// ...}}}Other properties of the settings object are private internals, and should not be accessed directly. If you think you need access to other properties, please open an issue to discuss your use case.
-
#11772
6272e6cThanks @bluwy! - Usesmagicastto update the config forastro add -
#11845
440a4beThanks @bluwy! - Replacesexecawithtinyexecinternally -
#11858
8bab233Thanks @ascorbic! - Correctly resolves content layer images when filePath is not set
-
🐞 Patch Changes
-
#11847
45b599cThanks @ascorbic! - Fixes a case where Vite would be imported by the SSR runtime, causing bundling errors and bloat. -
#11822
6fcaab8Thanks @bluwy! - Marks internalvite-plugin-fileurlplugin withenforce: 'pre' -
#11713
497324cThanks @voidfill! - Prevents prefetching of the same urls with different hashes. -
#11814
2bb72c6Thanks @eduardocereto! - Updates the documentation for experimental Content Layer API with a corrected code example -
#11842
1ffaae0Thanks @stephan281094! - Fixes a typo in theMissingImageDimensionerror message -
#11828
20d47aaThanks @bholmesdev! - Improves error message when invalid data is returned by an Action.
-
✨ Major Changes
-
#11798
e9e2139Thanks @matthewp! - Unflag globalRoutePriorityThe previously experimental feature
globalRoutePriorityis now the default in Astro 5.This was a refactoring of route prioritization in Astro, making it so that injected routes, file-based routes, and redirects are all prioritized using the same logic. This feature has been enabled for all Starlight projects since it was added and should not affect most users.
-
#11679
ea71b90Thanks @florian-lefebvre! - Theastro:envfeature introduced behind a flag in v4.10.0 is no longer experimental and is available for general use. If you have been waiting for stabilization before usingastro:env, you can now do so.This feature lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client.
To configure a schema, add the
envoption to your Astro config and define your client and server variables. If you were previously using this feature, please remove the experimental flag from your Astro config and move your entireenvconfiguration unchanged to a top-level option.import { defineConfig, envField } from 'astro/config';export default defineConfig({env: {schema: {API_URL: envField.string({ context: 'client', access: 'public', optional: true }),PORT: envField.number({ context: 'server', access: 'public', default: 4321 }),API_SECRET: envField.string({ context: 'server', access: 'secret' }),},},});You can import and use your defined variables from the appropriate
/clientor/servermodule:---import { API_URL } from 'astro:env/client';import { API_SECRET_TOKEN } from 'astro:env/server';const data = await fetch(`${API_URL}/users`, {method: 'GET',headers: {'Content-Type': 'application/json',Authorization: `Bearer ${API_SECRET_TOKEN}`,},});---<script>import { API_URL } from 'astro:env/client';fetch(`${API_URL}/ping`);</script> -
#11788
7c0ccfcThanks @ematipico! - Updates the default value ofsecurity.checkOrigintotrue, which enables Cross-Site Request Forgery (CSRF) protection by default for pages rendered on demand.If you had previously configured
security.checkOrigin: true, you no longer need this set in your Astro config. This is now the default and it is safe to remove.To disable this behavior and opt out of automatically checking that the “origin” header matches the URL sent by each request, you must explicitly set
security.checkOrigin: false:export default defineConfig({security: {checkOrigin: false}}) -
#11741
6617491Thanks @bluwy! - Removes internal JSX handling and moves the responsibility to the@astrojs/mdxpackage directly. The following exports are also now removed:astro/jsx/babel.jsastro/jsx/component.jsastro/jsx/index.jsastro/jsx/renderer.jsastro/jsx/server.jsastro/jsx/transform-options.js
If your project includes
.mdxfiles, you must upgrade@astrojs/mdxto the latest version so that it doesn’t rely on these entrypoints to handle your JSX. -
#11782
9a2aaa0Thanks @Princesseuh! - Makes thecompiledContentproperty of Markdown content an async function, this change should fix underlying issues where sometimes when using a custom image service and images inside Markdown, Node would exit suddenly without any error message.---import * as myPost from "../post.md";const content = myPost.compiledContent();const content = await myPost.compiledContent();---<Fragment set:html={content} /> -
#11770
cfa6a47Thanks @Princesseuh! - Removed support for the Squoosh image service. As the underlying librarylibsquooshis no longer maintained, and the image service sees very little usage we have decided to remove it from Astro.Our recommendation is to use the base Sharp image service, which is more powerful, faster, and more actively maintained.
import { squooshImageService } from "astro/config";import { defineConfig } from "astro/config";export default defineConfig({image: {service: squooshImageService()}});If you are using this service, and cannot migrate to the base Sharp image service, a third-party extraction of the previous service is available here: https://github.com/Princesseuh/astro-image-service-squoosh
🐞 Patch Changes
-
#11780
c6622adThanks @Princesseuh! - Deprecates the Squoosh image service, to be removed in Astro 5.0. We recommend migrating to the default Sharp service. -
#11732
4cd6c43Thanks @matthewp! - Use GET requests with preloading for Server IslandsServer Island requests include the props used to render the island as well as any slots passed in (excluding the fallback slot). Since browsers have a max 4mb URL length we default to using a POST request to avoid overflowing this length.
However in reality most usage of Server Islands are fairly isolated and won’t exceed this limit, so a GET request is possible by passing this same information via search parameters.
Using GET means we can also include a
<link rel="preload">tag to speed up the request.This change implements this, with safe fallback to POST.
-
#11773
86a3391Thanks @ematipico! - Changes messages logged when using unsupported, deprecated, or experimental adapter features for clarity -
#11774
c6400abThanks @florian-lefebvre! - Fixes the path returned byinjectTypes -
#11771
49650a4Thanks @florian-lefebvre! - Fixes an error thrown byastro syncwhen anastro:envvirtual module is imported inside the Content Collections config -
#11744
b677429Thanks @bluwy! - Disables the WebSocket server when creating a Vite server for loading config files
-
🐞 Patch Changes
-
#11809
62e97a2Thanks @bholmesdev! - Fixes usage of.transform(),.refine(),.passthrough(), and other effects on Action form inputs. -
#11812
260c4beThanks @bholmesdev! - ExposesActionAPIContexttype from theastro:actionsmodule. -
#11813
3f7630aThanks @bholmesdev! - Fixes unexpectedundefinedvalue when calling an action from the client without a return value.
-
🐞 Patch Changes
-
#11794
3691a62Thanks @bholmesdev! - Fixes unexpected warning log when using Actions on “hybrid” rendered projects. -
#11801
9f943c1Thanks @delucis! - Fixes a bug where thefilePathproperty was not available on content collection entries when using the content layerfile()loader with a JSON file that contained an object instead of an array. This was breaking use of theimage()schema utility among other things.
-