Astro Islands Architecture Explained
Astro's islands architecture ships static HTML by default and hydrates only the interactive components that need JavaScript. Here's how it works.
Islands architecture is a rendering pattern where a page is built mostly from static, server-rendered HTML, and only specific components — the “islands” — ship JavaScript and become interactive in the browser. Everything else stays inert markup. Astro popularized the term, but the idea shows up anywhere a framework tries to avoid shipping a full client-side app for a page that’s mostly static content.
The problem islands solve
Most frameworks that render on the server still ship a matching bundle of client-side JavaScript so the page can “hydrate” — attach event listeners and rebuild component state in the browser. Traditionally, this happens for the entire page, even if only a search box and a like button are actually interactive. The browser downloads, parses, and executes JavaScript for content that will never change.
Islands architecture flips the default: nothing is interactive unless you say so. A blog post with a comment widget ships HTML for the article body and hydrates only the comment widget. The rest of the page — headings, paragraphs, images — is never touched by a JavaScript framework at runtime. This directly improves Core Web Vitals, particularly metrics tied to main-thread work like Interaction to Next Paint.
How Astro implements it
In Astro, every .astro component renders to static HTML on the server (or at build time) by default — it ships zero JavaScript unless you explicitly opt a component into hydration with a client:* directive:
client:load— hydrate immediately on page load.client:idle— hydrate once the browser is idle, usingrequestIdleCallback.client:visible— hydrate when the component scrolls into the viewport, via anIntersectionObserver.client:media— hydrate only if a media query matches, useful for mobile-only or desktop-only widgets.client:only— skip server rendering entirely and render purely on the client.
Each directive is a deliberate trade-off between how soon a component becomes usable and how much JavaScript the browser has to process upfront. A search-as-you-type component might need client:load; a “was this helpful” widget at the bottom of an article is a perfect fit for client:visible.
Because hydration is opt-in per component, a single page can mix islands built with different frameworks — a React component here, a Svelte component there — each hydrating independently. Astro calls this “framework-agnostic,” and it’s one reason teams migrating between frameworks (see Svelte vs React) sometimes reach for Astro as neutral ground rather than a big-bang rewrite.
Islands vs. full hydration
| Islands architecture | Full-page hydration (typical SPA) | |
|---|---|---|
| Default JS shipped | None; only opted-in components | Entire component tree |
| Hydration granularity | Per component | Whole page (or route) |
| Best for | Content-heavy sites with pockets of interactivity | Highly dynamic, app-like interfaces |
| Time to interactive | Fast — most of the page needs no hydration | Slower — proportional to bundle size |
| Client-side routing | Usually none (full page navigations) | Common, avoids full reloads |
Neither model is strictly better — they optimize for different page shapes. React Server Components attempt something adjacent from the other direction: keeping a single-page-app model but pushing more rendering to the server and shrinking the client bundle for the parts that don’t need it. Islands architecture instead starts from a mostly-static page and adds interactivity as the exception.
Where islands architecture falls short
Islands work best when a page has a clear split between static content and isolated interactive widgets. They’re a worse fit for interfaces that are interactive nearly everywhere — a spreadsheet app, a design tool, a chat client. In those cases, most of the page would need hydration anyway, and the overhead of coordinating many independent islands (each with its own bundle, its own hydration trigger) can outweigh the benefit versus just shipping one coherent client-side app.
Islands also don’t eliminate the need to think about hydration mismatches. If server-rendered markup for an island differs from what the client would render — because of a timezone-dependent date format, for instance — you still get the same class of bug that affects any SSR setup. See what hydration actually is for the mechanics of why that mismatch happens.
Deploying an islands-based site
Because most of the output is static HTML, islands-based sites deploy well to CDN-backed static hosts and edge platforms — there’s comparatively little server work per request beyond serving files (plus any endpoints you’ve added). If you’re deploying an Astro site, the process of shipping an Astro project to Cloudflare Pages is a common path: static assets go to the edge, and only opted-in islands add any client-side cost.
The takeaway
Islands architecture makes “ship no JavaScript” the default and “hydrate this one component” the explicit exception, instead of the other way around. For content-heavy sites — blogs, documentation, marketing pages — that inversion often means faster interactivity with far less client-side code, at the cost of being a weaker fit for pages that are interactive almost everywhere. Astro’s client:* directives are the mechanism; picking the right one per component is where the actual performance tuning happens.
Keep reading
Takina · · 4 min read What Is the Beacon API? navigator.sendBeacon()
The Beacon API lets a page send one last async request as it unloads, without blocking navigation or racing the browser's page teardown.
Takina · · 3 min read What Is Fetch Priority? The fetchpriority Attribute
fetchpriority lets you tell the browser which resources matter most, overriding its default heuristics to load critical assets sooner.
Takina · · 5 min read CSS will-change Explained: Compositing and Performance
The CSS will-change property hints the browser to prepare an element for an upcoming change, moving it to its own compositor layer. When to use it and when not to.