Astro 4.12: Server Islands

By
Erika
Matthew Phillips

Astro 4.12 is out now! This release includes the first experimental release of Server Islands, our new solution to integrate high performance static HTML and dynamic server-generated components together. Improvements to pagination and syntax highlighting are also included.

This release includes the following highlights:

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: Server Islands

In 2021 Astro brought the idea of islands architecture to the mainstream, allowing you to create “islands” of interactive client-side components while most of your page is generated statically.

With Server islands we are extending this same architecture to the server. Server islands make it easy to combine high performance static HTML and dynamic server-generated components.

Within any given web page you might have content that is:

  • Completely static and never changes.
  • Dynamically backed by a database that changes infrequently, but more often than you deploy.
  • Personalized content, tailored to individual users.

Currently, you must choose one caching strategy for all of these types of content, and if the page is a logged-in experience that usually means no caching at all. Now, with Server islands you get the best of both worlds.

A diagram showing the server island population parts of the page from the server.

Server islands are used for your most dynamic content; personalized content like a user’s avatar, their shopping cart, and product reviews. When these components are deferred, you’re free to more aggressively cache the page itself.

This means that users, logged in or not, will see the most critical parts of the page instantly, as they are caching on Edge CDNs. Fallback content will be visible for a short amount of time before the dynamic islands are then loaded.

Each island is loaded independent from the rest; this means a slower island, such as one connected to a legacy backend, won’t delay the rest of personalized content from being seen and interacted with.

Performance

To test the viability of this idea we built server-islands.com. This demo is inspired by the Next.js Partial Prerendering demo, reskinned to suit Astro’s personality. Server Islands has a lot in common with Partial Prerendering (PPR), especially in its goal of allowing increased cacheability of pages.

Metricspartialprerendering.comserver-islands.com
TTFB0.838s0.766s
FCP1.740s1.251s
LCP1.740s1.405s
TBT0.093s0.018s
SourceWebPageTestWebPageTest

server-islands.com performs consistently faster in repeated runs; it scores a 20% faster Largest Contentful Paint, which is one of Google’s 3 Core Web Vitals impacting both user experience and SEO.

Instead of starting the request to the origin on the server like PPR, Server Islands do so from the browser. This means that while Server islands paint earlier, they might finish later than PPR.

Both Server islands and PPR work best in sites like these, where the main page content can be edge cached. They work well for ecommerce but would probably not be something you want to use for a social network feed. Due to the page being edge cached, that wait for the island request is minimal.

Ultimately, we are confident in this tradeoff as being the best overall performance for Astro users across both the user experience and passing Core Web Vitals without requiring any special backend infrastructure or support to get started.

Portability

We built Server islands with portability in mind. It doesn’t depend on any server infrastructure so it will work with any host you have, from a Node.js server in a Docker container to the serverless provider of your choice.

Our implementation happens mostly at build-time, where component content is swapped out for a small script, one that can almost fit into a Tweet.

Each of the islands marked with server:defer are split off into their own special route which the script fetches at run time. The trade-off is that the island doesn’t get a head-start rendering as it might if it depended on edge handling, but we think the performance numbers favor the simpler approach we’ve taken thus far.

Trying out Server Islands

We’re still working on polishing the feature, but you can try out Astro Server Islands today by enabling the feature in Astro 4.12:

import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
output: 'hybrid',
adapter: netlify(),
experimental: {
serverIslands: true,
},
})

Server islands, as the name implies, run on the server, so you’ll need to use either 'server' or 'hybrid' output. Once you’ve enabled the feature you can use them on any component using the server:defer directive:

<Avatar server:defer>
<GenericUserImage slot="fallback" />
</Avatar>

When Astro builds your site it will omit the component and inject a script in its place, in addition to the content you’ve marked with slot="fallback". When the page loads in the browser, these components along with any props you pass to them, will be requested to a special endpoint that renders them and returns the HTML.

Server Islands are normal Astro components and can use any of the features you expect from Astro like middleware, slots, client: islands.

Getting involved

Server Islands have an active RFC. We’re still finalizing the details and features; we’ll likely be adding encrypted props for example. Try out the release in 4.12 and let us know what you think.

Added first and last URLs to pagination data

The paginate() helper now returns the first and last properties in the pagination data. These properties contain the first and last page urls, respectively. Thanks to @tsawada for contributing this feature!

Added support for the Shiki’s defaultColor option

The defaultColor option allows you to override the values of a theme’s inline style, adding only CSS variables to give you more flexibility in applying multiple color themes. This option can be configured in your Shiki config to apply throughout your site, or passed to Astro’s built-in <Code> component to style an individual code block. Thank you to @madcampos for contributing this!

New inferRemoteSize function

In Astro 4.4, we added a new property to the Image and Picture components and getImage() to automatically infer the size of remote images. Thanks to @itsmatteomanf, this release now includes a new function, inferRemoteSize(), that allows you to get the size of a remote image indendently of these components or getImage().

This can be useful if you’re using the image width to generate different densities, or if you need the image dimension for styling purposes.

---
import { inferRemoteSize, Image } from 'astro:assets';
const { width, height } = await inferRemoteSize('https://...');
---
<Image src={"https://..."} width={width / 2} height={height} densities={[1.5, 2]} />

Some more things

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

Special thanks to @sarah11918, @Fryuni, @ARipeAppleByYoursTruly and everyone else who contributed to this release.