Back to Blog

Islands architecture:
only pay for the interactive bits

Most of a web page never needs JavaScript, yet most frameworks ship it anyway. What islands architecture is, how Astro's client directives work, and the trade-off nobody mentions.

Islands architecture: | only pay for the interactive bits

Most web pages are mostly text. A header, some prose, a few images, a footer. Perhaps a contact form and a menu that opens on mobile.

Yet the standard approach ships a JavaScript framework, a router and a state manager to render all of it, including the parts that will never change once they land on screen.

Islands architecture asks a reasonable question: what if you only paid for the parts that actually do something?

Where the idea comes from

The term component island was coined by Etsy's frontend architect Katie Sylor-Miller in 2019, then expanded on by Preact creator Jason Miller in a post published in August 2020.

It has since moved from curiosity to a first class option, supported by Astro, Fresh, Marko and others. Astro is where most people encounter it today.

The problem it solves

To understand islands, you need to understand hydration.

Server side rendering sends the browser finished HTML, which is good. But for that HTML to become interactive, the framework then re-executes the entire application in the browser to attach event listeners and rebuild its internal state.

The work is effectively done twice. The visitor sees content quickly, then waits while the page becomes usable. That gap is what Total Blocking Time measures, and it is a common cause of poor Interaction to Next Paint scores.

Most JavaScript frameworks hydrate and render the whole site as one large application, which is where single page apps get their page load problems.

The model

Islands invert the default.

The page is static HTML. Interactive components are isolated islands within it, and each one hydrates independently. The prose around them never hydrates at all, because it does not need to.

The pattern is also known as partial or selective hydration.

Picture a typical article page: header, body copy, an image carousel, a comments widget, footer. Under an islands model, only the carousel and the comments widget ship JavaScript. Everything else is HTML that arrives ready to read.

What this looks like in Astro

Components are static by default. You opt into interactivity with a client directive:

astro
--- import ContactForm from '../components/ContactForm.jsx'; import MobileNav from '../components/MobileNav.jsx'; --- <Hero /> <MobileNav client:media="(max-width: 768px)" /> <article><slot /></article> <ContactForm client:visible />

Hero ships no JavaScript. The other two hydrate, but only under specific conditions.

The directives worth knowing:

  • client:load hydrates immediately. Use sparingly, and only for things visible above the fold that must work at once.
  • client:idle waits until the browser is idle. Good for anything non urgent.
  • client:visible waits until the component scrolls into view. The best default for anything below the fold.
  • client:media hydrates only when a media query matches. Ideal for mobile navigation that desktop visitors never use.
  • client:only skips server rendering entirely, for components that genuinely cannot render without a browser.

The interesting one is client:visible. A comments widget at the bottom of a long article may never load its JavaScript at all, because most readers never scroll that far.

How to decide what is an island

A useful test: does this component respond to the user after the page has loaded?

If it does not, it is not an island. Headers, hero sections, feature grids, testimonials, footers and body copy are all static, however they were authored.

If it does, make it an island and pick the laziest directive that still works. Start with client:visible and move up only if the behaviour breaks.

Most marketing sites end up with two or three islands. It is usually fewer than people expect.

The honest trade off

Islands are isolated by design, and that isolation has a cost.

Two React islands on the same page do not share state. They are separate applications that happen to sit on the same document, so a context provider wrapping both will not work in the way you expect.

For genuinely shared state you need something outside the component tree, such as nanostores, or you rethink the boundary so the state lives inside a single island.

This is a real constraint. If your interface is a densely interconnected application, islands are the wrong model, and a conventional single page app is the better fit. Islands suit content sites with pockets of interactivity, which is the majority of the web but by no means all of it.

Why it matters beyond performance

The obvious benefit is speed. Less JavaScript means faster loading, better Core Web Vitals and lower processing cost on mid range phones, which is what most of your visitors are actually using.

The less obvious benefit is robustness. When your content lives in the HTML rather than in a bundle waiting to execute, it is visible to every crawler, preview bot and AI agent without special handling. No user agent detection, no prerendering layer, no headless browser that might time out.

Your content is simply there, in the response, for everyone.

That is a quieter advantage than the performance numbers, but for anything that depends on search traffic it may be the more valuable one.