Astro 4.9

By
Erika
Emanuele Stoppa
Matthew Phillips
Nate Moore
Bjorn Lu
Sarah Rainsberger

Astro 4.9 is out! This release includes the long-awaited Container API, stabilized experimental features, and more. A small but mighty release!

Full release highlights include:

To upgrade an existing project, use the automated @astrojs/upgrade CLI tool. Alternatively, upgrade manually by running the upgrade command for your package manager:

# Recommended:
npx @astrojs/upgrade
# Manual:
npm install astro@latest
pnpm upgrade astro --latest
yarn upgrade astro --latest

Experimental: Container API

Astro 4.9 introduces a first look at the long-awaited Container API. This API allows you to render Astro components outside of an Astro application. This is similar to how you can render React components on the server using libraries like react-dom/server, or Preact components using preact-render-to-string.

Here’s an example of how you can use the Container API to render an Astro component to a string:

import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import Component from './components/Component.astro';
const container = await AstroContainer.create();
console.log(await container.renderToString(Component));

The possibilities that this API adds are endless, but this is just a first step. Our main focus at this stage is to make it easier to render Astro components inside test frameworks powered by Vite, such as Vitest.

import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { expect, test } from 'vitest';
import Card from '../src/components/Card.astro';
test('Card with slots', async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
slots: {
default: 'Card content',
},
});
expect(result).toContain('This is a card');
expect(result).toContain('Card content');
});

To get started testing Astro components with Vitest, we made a new example you can checkout, or create a new project with directly:

npm create astro@latest -- --template container-with-vitest

Our testing guide has also been updated with more information on how to use the Container API for testing. Test everything!

To learn more about the Container API and how to use it, check out the Container API documentation. To give feedback on this experimental and developing feature, visit the Container API RFC.

React 19 support

The first React 19 release candidate is out, and Astro came prepared. We added full support for React 19 to the React integration with some added goodies for Astro Actions.

To use React 19 in your Astro project, follow the React 19 beta installation guide. This unlocks React’s new form actions feature, which lets you call Actions from a <form> and track “pending” and “result” values in your components.

This example uses an Astro Action called like() that accepts a postId and returns the number of likes. With React 19, you can pass this action to the useActionState() hook to track the result. Apply the experimental_withState() function to apply progressive enhancement information automatically:

// src/components/Like.tsx
import { actions } from 'astro:actions';
import { useActionState } from 'react';
import { experimental_withState } from '@astrojs/react/actions';
export function Like({ postId }: { postId: string }) {
const [state, action, pending] = useActionState(
experimental_withState(actions.like),
0, // initial likes
);
return (
<form action={action}>
<input type="hidden" name="postId" value={postId} />
<button disabled={pending}>{state} ❤️</button>
</form>
);
}

You can also access the state stored by useActionState() from your action handler using experimental_getActionState(). See the CHANGELOG for usage examples.

If you don’t need to track the state, you can also apply Astro Actions directly to the action property on any React <form>. This will also apply progressive enhancement information:

// src/components/SignOut.tsx
import { actions } from 'astro:actions';
export function SignOut() {
return (
<form action={actions.signOut}>
<button>Sign Out</button>
</form>
);
}

To learn more about Astro Actions, and to offer feedback on this experimental API, check out the Astro Actions RFC.

Stabilized experimental features

This release stabilizes the CSRF protection (introduced in Astro 4.6) and i18n domains support (introduced in Astro 4.3) features. It is no longer necessary to enable the experimental.csrfProtection and experimental.i18nDomains options in your Astro config to use these features.

The experimental.security.csrfProtection option has been removed in favor of a new security.checkOrigin option. To enable cross-site request forgery protection, set security.checkOrigin to true in your Astro config and remove the experimental.security.csrfProtection option if you have it enabled:

import { defineConfig } from "astro/config";
export default defineConfig({
security: {
checkOrigin: true
}
experimental: {
security: {
csrfProtection: {
origin: true
}
}
},
});

As for i18n domains, you can keep your configuration as is, just without the experimental.i18nDomains option:

import { defineConfig } from "astro/config";
export default defineConfig({
site: "https://example.com",
output: "server",
adapter: node({
mode: 'standalone',
}),
i18n: {
defaultLocale: "en",
locales: ["en", "fr", "pt-br", "es"],
prefixDefaultLocale: false,
domains: {
fr: "https://fr.example.com",
es: "https://example.es",
},
},
experimental: {
i18nDomains: true,
},
});

For more information on these features, see the security and i18n domains sections of the Astro documentation respectively.

Bug Fixes

As it do be, Astro 4.9 includes more bug fixes and smaller improvements that couldn’t make it into this post! Check out the full release notes to learn more!