DEV Community

Cover image for Astro Server Islands: How They Work and When to Use Them
Nick Taylor
Nick Taylor Subscriber

Posted on • Originally published at nickyt.co

14 2 1 2 1

Astro Server Islands: How They Work and When to Use Them

Astro introduced a powerful new pattern called Server Islands last year. If you're building with Astro, it's worth understanding what they are, how they work, and where they shine (and where they don’t). Their documentation explains server islands really well, but I wanted to dig a little deeper on the technical side of things.

What Are Server Islands?

Server Islands allow you to render a portion of your page on the server on-demand, without blocking the rest of the page load.

Think of your page as the ocean, mostly static or cached HTML. Now imagine dynamic content rising out of that ocean like islands appearing after the tide goes out. These "server islands" let you render just the parts of the page you need from the server, without slowing down the rest.

This pattern is useful for:

  • Personalized greetings
  • Authenticated user info (avatars, session data)
  • Region-specific content (prices based on country)
  • Anything dynamic that shouldn’t block first render

Unlike traditional Server-Side Rendering (SSR) where the whole page waits for server data, Server Islands let you defer just part of the page until after load.

Server Islands were partially inspired by Next.js’s Partial Pre-Rendering (PPR), which similarly balances static and dynamic content by deferring specific sections to load asynchronously.

How They Work

The API is simple: you use the server:defer directive on an Astro component.

Example:

<SomeServerIsland server:defer />
Enter fullscreen mode Exit fullscreen mode

This tells Astro to replace that component with a placeholder and fetch the real HTML immediately from the server after the page loads. You can also provide a fallback UI that renders in the initial page load server-side while the island is being fetched:

<SomeServerIsland server:defer>
  <div slot="fallback">Loading some server island...</div>
</SomeServerIsland>
Enter fullscreen mode Exit fullscreen mode

What actually happens under the hood:

At build time, Astro replaces the island with a small script.
Here's an expanded view:

<!--[if astro]>server-island-start<![endif]-->
<h1>Hello
  World</h1>
<link rel="preload" as="fetch" href="/_server-islands/SomeServerIsland?e=default&p=&s=%7B%7D" crossorigin="anonymous">
<script type="module" data-astro-rerun data-island-id="137eec60-035b-4630-b4af-365acd683b97">let response = await fetch('/_server-islands/SomeServerIsland?e=default&p=&s=%7B%7D');
  replaceServerIsland('137eec60-035b-4630-b4af-365acd683b97', response);</script>
Enter fullscreen mode Exit fullscreen mode

The script fetches the HTML for that component from a special server endpoint (e.g. /_server-islands/SomeServerIsland?e=default&p=&s=%7B%7D) and replaces the node which may or may not have fallback markup with the rendered HTML once the response is ready.

Here’s the client-side function that makes that swap happen:

async function replaceServerIsland(id, r) {
  let s = document.querySelector(\`script[data-island-id="\${id}"]\`);

  // If there's no matching script, or the request fails then return
  if (!s || r.status !== 200 || r.headers.get('content-type')?.split(';')[0].trim() !== 'text/html') return;

  // Load the HTML before modifying the DOM in case of errors
  let html = await r.text();

  // Remove any placeholder content before the island script
  while (s.previousSibling && s.previousSibling.nodeType !== 8 && s.previousSibling.data !== '[if astro]>server-island-start<![endif]')
    s.previousSibling.remove();
  s.previousSibling?.remove();

  // Insert the new HTML
  s.before(document.createRange().createContextualFragment(html));

  // Remove the script. Prior to v5.4.2, this was the trick to force rerun of scripts.  Keeping it to minimize change to the existing behavior.
  s.remove();
}
Enter fullscreen mode Exit fullscreen mode

A Fun Discovery

Someone in the chat during my livestream asked if Server Islands work inside client components. I mentioned that a Server Island has to be an Astro component, so you can't directly embed one inside a client component like React, Vue, Svelte, or Solid. But I suspected it would probably work if passed as a child.

Believe it or not, it does. Embedding Server Islands inside client components works as long as you pass them as children.

And it makes sense if you think about it. Astro renders the Server Island as plain HTML, so the client-side component renders its markup on the server as usual. The page loads, the Server Island hydration script kicks in and updates the placeholder, and then the client component bootstraps its interactivity like normal.

Limitations and Gotchas

  • Server islands only work with .astro components, not React, Vue, Svelte, Solid, etc.

  • You can't place a server island directly inside a client component, but you can pass it as a child.

  • You must set output: "server" in your Astro config.

  • During development, no adapter is required—but for deployment you'll need one (e.g. Netlify, Vercel, Node).

  • JavaScript must be enabled in the browser for the islands to render properly.

Where Can You Use Them?

Ideal for:

  • Showing personalized or location-based content

  • Displaying user data post-login

  • Incrementally enhancing static pages

  • SEO-friendly dynamic blocks (e.g., “related articles,” recent comments)

Not ideal for:

  • Entirely dynamic pages

  • JS-disabled environments

  • Components that require full client-side interactivity before page load

Wrapping Up

Server Islands are a great way to blend static-first performance with dynamic, server-rendered content. They're lightweight, easy to use, and a smart alternative to full-page SSR when you only need to personalize a section of the page.

If you're already using Astro, this is a feature worth exploring—especially if you're serving authenticated content or want to progressively enhance your pages.

All you need is server:defer, and you’re off to the islands. 🏝️ Learn more about server islands in the official Astro documentation.


Watch the full walkthrough:

Want to connect?
All my socials are at nickyt.online

Cover image by Marek Okon on Unsplash

Redis image

62% faster than every other vector database

Tired of slow, inaccurate vector search?
Redis delivers top recall and low latency, outperforming leading vector databases in recent benchmarks. With built-in ANN and easy scaling, it’s a fast, reliable choice for real-time AI apps.

Get started

Top comments (18)

Collapse
 
aksh247 profile image
Akshat Ramanathan

Beautiful. Astro originally invented the Dynamic Islands concepts. React took it and make RSC where client components are the islands.
Nextjs invented PPR last year to improve SSR performance of slow server components.
Astro took it from them and made server islands which are deferred and until loaded show a fallback slot component. Life is full circle in this world lol I love web dev!

Collapse
 
nickytonline profile image
Nick Taylor • Edited

Glad you enjoyed the blog post! And yes, the web is great!

Small correction in Astro inventing islands. It actually came from a meeting between Preact creator, @_developit and at the time frontend architect at Etsy Katie Sylor-Miller.

See Islands Architecture

Collapse
 
esthersoftwaredev profile image
Esther White • Edited

I think that Katie Sylor-Miller might have moved to BlueSky bsky.app/profile/ksylor.bsky.social since the twitter handle is not finding an account

Thread Thread
 
nickytonline profile image
Nick Taylor

Thanks Esther!

Collapse
 
aksh247 profile image
Akshat Ramanathan

Oh wow. Didn’t know that. That’s cool. Will check out this article thank you!

Collapse
 
motss profile image
Rong Sen Ng

Could not find any source/reference on the requirement of setting output=server for Server Islands. Is that true?

Collapse
 
nickytonline profile image
Nick Taylor

Thanks for reading Rong Sen!

It doesn't appear to be referenced in the docs. I think it's implied by the fact you are doing something server-side, but it could be a great documentation PR to update that if it is indeed missing.

Shia LeBoeuf in character saying Magic!

Collapse
 
dotallio profile image
Dotallio

Really appreciate how you broke down the server:defer behavior and those child-component gotchas - cleared up a bunch for me. Anyone else experimenting with mixing server islands and client frameworks?

Collapse
 
nickytonline profile image
Nick Taylor

Thanks for the kind words. Glad you enjoyed the blog post!

Captain America saluting

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

wow, this is super helpful - i always wonder if using more server stuff actually makes building easier or just adds more stuff to manage long run you ever feel like these things make projects simpler or just different kind of headache

Collapse
 
nickytonline profile image
Nick Taylor

Thanks Nathan! The nice thing about this one is @ascorbic on the Astro core team made the api for this dead simple so that aside from setting your Astro config to output: server, it's just a server:defer directive on an Astro component. 🔥

Chow Yun-fat giving a thumbs up

Collapse
 
aksh247 profile image
Akshat Ramanathan

Just realised how the server islands inside Astro components nested as children works the same way as nesting RSC server components inside client components. It’s coz the defered async HTML is received later and slotted in similar to how the RSCpayload for nested server components are streamed in

Collapse
 
michael_liang_0208 profile image
Michael Liang

Great post

Collapse
 
nickytonline profile image
Nick Taylor • Edited

Thanks!

Hackerman from Kung Fury putting on a Nintendo Power glove

Collapse
 
xwero profile image
david duymelinck

If the request has a session you can generate pages with personalized/localized parts on the server, no islands needed. This is the way backend frameworks have worked for years.
The biggest problem is controlling the cache if you do this. The main benefit is no javascript and subrequests.

I would not recommend to use it for related or recent blocks if the link is only the main subject of the page. If it is combined with location or user then I agree.

I know the concept from Drupal as BigPipe which comes from FaceBook. And they got it from the way microprocessors work. There is nothing new about it :)

Collapse
 
nadeem_zia_257af7e986ffc6 profile image
nadeem zia

Good information given

Collapse
 
nickytonline profile image
Nick Taylor

Thanks!

Multiple TV and movie characters giving a thumbs up

Collapse
 
esthersoftwaredev profile image
Esther White

Great article and video thank you for sharing I am using Astro more and more 👍👍

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

👋 Kindness is contagious

Discover fresh viewpoints in this insightful post, supported by our vibrant DEV Community. Every developer’s experience matters—add your thoughts and help us grow together.

A simple “thank you” can uplift the author and spark new discussions—leave yours below!

On DEV, knowledge-sharing connects us and drives innovation. Found this useful? A quick note of appreciation makes a real impact.

Okay