Islands Architecture — Astro, Fresh, Iles
TL;DR
Islands architecture = mostly static HTML with discrete interactive “islands” that hydrate independently. The page ships ~0 KB JS for static content; only the interactive components ship their framework + code. Astro is the production-ready leader; Fresh (Deno), Iles, Marko are alternatives. The senior framing: complementary to React’s RSC + selective hydration — both achieve “interactive bits hydrate, the rest is static.” Pick islands for content-heavy sites (blogs, docs, marketing); pick RSC for app-shaped products.
Interview Q&A
Q: What does an island look like?
A: Astro example:
---
// build/server time
import Counter from "./Counter.tsx"; // React component
import VueChart from "./VueChart.vue"; // Vue component
import { posts } from "./data";
---
<html>
<head><title>Blog</title></head>
<body>
<h1>My Blog</h1>
<ul>
{posts.map(p => <li><a href={p.url}>{p.title}</a></li>)}
</ul>
<!-- Static — never ships JS -->
<Counter client:load /> <!-- React island, hydrates immediately -->
<VueChart client:visible /> <!-- Vue island, hydrates when in viewport -->
</body>
</html>
The HTML around the islands is rendered at build/server and never hydrates. Only <Counter> and <VueChart> load their framework runtime and component code.
Bundle for a page with 1 React island: ~10 KB (React + tiny runtime). Pure static for the rest. Compare to a full React app: ~40-100 KB minimum.
Q: client:* directives.
A: Astro’s syntax — controls when each island hydrates:
| Directive | When |
|---|---|
client:load |
immediately on page load |
client:idle |
when the browser is idle (requestIdleCallback) |
client:visible |
when scrolled into viewport (IntersectionObserver) |
client:media="(max-width: 600px)" |
when media query matches |
client:only="react" |
client-only — don’t SSR, render as fallback then hydrate |
Each is a different hydration trigger. Use cases:
client:load— header search box, immediately interactive.client:visible— below-fold widgets that load when scrolled to.client:idle— analytics opt-in, low-priority interactivity.client:media— mobile-only menu component.client:only— components that touchwindowand can’t SSR.
This granularity is unique to islands frameworks. React Suspense doesn’t have an equivalent (selective hydration is auto-prioritized, not directive-controlled).
Q: Multi-framework support.
A: Astro can host React + Vue + Svelte + Solid components in the same page. Each island ships only its framework runtime, shared across same-framework islands.
<ReactWidget client:load />
<VueChart client:visible />
<SvelteForm client:idle />
Three runtimes, each loaded only when needed. Useful for:
- Migrating between frameworks gradually.
- Using best-fit framework per widget.
- Adopting Astro for a marketing site without abandoning existing React/Vue components.
Fresh and Iles are Preact-only (Fresh) or Vue-focused (Iles). Astro is the multi-framework standout.
Q: When do islands beat React/Next.js?
A:
Islands win:
- Content-heavy sites — blogs, docs, marketing, e-commerce catalog browsing.
- Sites with mostly-static + a few interactive widgets — newsletter signup, search, comments.
- Multi-framework teams — adopt incrementally.
- Strict bundle size targets — astro pages routinely ship < 20 KB JS.
React/Next wins:
- App-shaped products — dashboards, admins, full SPAs where most of the tree is interactive.
- Heavy state coordination between components (cart, auth, real-time).
- Existing React ecosystem dependencies that don’t fit islands.
The rough rule: if your page is “80% static prose, 20% interactive widgets,” islands are great. If it’s “80% interactive widgets,” React/Next.
Q: How is RSC + selective hydration similar / different?
A:
| Islands (Astro) | RSC + selective hydration | |
|---|---|---|
| Static rendering | build/server time | server runtime (RSC) |
| Hydration trigger | explicit directives | implicit (Suspense boundaries + interaction) |
| Multi-framework | yes (Astro) | React only |
| Granularity | per-component, per-trigger | per-Suspense-boundary |
| Mental model | “static page + sprinkled interactivity” | “React app with server-only parts” |
| Streaming | no (build/serve static) | yes (streamed with Suspense) |
Both achieve “minimize hydration cost.” Islands take it further by being content-first (static is the default). RSC keeps the React mental model but moves work to the server.
For a content-first site: islands. For an interactive app: RSC. For an interactive app with some content pages: hybrid (Next.js for app, separate Astro for marketing — or all Next.js with RSC).
Q: Astro’s content collections.
A: Built-in API for typed markdown/MDX content:
// src/content/config.ts
import { z, defineCollection } from "astro:content";
export const collections = {
blog: defineCollection({
schema: z.object({
title: z.string(),
date: z.coerce.date(),
draft: z.boolean().default(false),
}),
}),
};
---
import { getCollection } from "astro:content";
const posts = await getCollection("blog", ({ data }) => !data.draft);
---
<ul>
{posts.map(p => <li>{p.data.title}</li>)}
</ul>
Zod-validated, type-safe content. Way better than hand-rolled markdown parsing for a blog.
Q: Server endpoints in Astro.
A: Astro can be hybrid (some routes pre-rendered, some SSR’d) — output: "hybrid" in config. API routes work like Next.js:
// src/pages/api/posts.ts
import type { APIRoute } from "astro";
export const GET: APIRoute = async () => {
return new Response(JSON.stringify({ posts }), {
headers: { "Content-Type": "application/json" },
});
};
For most content sites, you don’t need many — the static + a few endpoints model fits.
Q: How does deployment compare?
A:
- Astro (static mode) — produces a
dist/folder, deploy to any static host (S3 + CloudFront, Vercel static, Netlify, GitHub Pages). Zero compute cost. - Astro (SSR mode) — needs a server runtime; deploys to Vercel, Netlify Functions, Cloudflare Pages, Deno Deploy.
- Next.js with App Router — needs Node or Edge runtime; Vercel is the natural fit, others work.
Astro static is the cheapest possible deployment ($0 + CDN bandwidth). Next.js is more flexible but always needs compute.
Q: When did “islands” become a thing?
A: Coined by Katie Sylor-Miller / Jason Miller (Preact creator) circa 2019; popularized by Astro starting in 2021. The architecture predates the name — sites have always shipped mostly static HTML with sprinkled interactivity. The frameworks made it ergonomic.
The convergence: React’s RSC (2023+) brought a similar concept to React-land, validating the architecture. Now both ecosystems independently arrived at “minimize JS, opt into interactivity.”
Gotchas / edge cases
- Islands can’t easily share state — each is isolated. For shared state, use a state library (Nano Stores in Astro) or postMessage between islands.
client:onlyskips SSR — the component’s HTML doesn’t pre-render. SEO sees a fallback or nothing. Use sparingly.- Hydration cost still real per island — many small islands = many small hydrations. For dense interactivity, the React-app model is more efficient.
- Astro + heavy React app — Astro is content-first; if your app is mostly React with React Router, you’re using Astro as a worse Next.js.
- Markdown processing at build — large content sites have long build times. ISR / on-demand build for new content (Astro supports SSR or hybrid mode for this).
- CSS isolation — Astro scopes styles per component by default; works across frameworks. Don’t expect Tailwind utility classes to “just work” without explicit setup.
What a senior is expected to say
- “Islands architecture = mostly static HTML + interactive components that hydrate independently. Astro is the leader; Fresh, Iles, Marko are alternatives.”
- “Hydration triggers per island (
client:load,client:visible,client:idle,client:media) — explicit control React’s auto-prioritization doesn’t have.” - “Multi-framework: Astro hosts React + Vue + Svelte + Solid in one page. Each framework’s runtime ships only when an island uses it.”
- “Use islands for content-heavy sites (blogs, docs, marketing). Use React/Next for app-shaped products. Hybrid OK — Astro for marketing, Next for app.”
- “RSC + selective hydration is React’s parallel-evolution answer. Different mental model (React app + server-only) vs Astro’s (static page + sprinkled interactivity).”
- “Astro static deploys to any CDN for free; SSR mode needs compute. Content Collections + Zod is excellent for typed markdown.”
Cross-references
- React Server Components (parallel approach): 03_rsc_deeper.md
- Hydration models (where partial hydration fits): 02_hydration_models.md
- Frontend system design — when to pick what: ../14_frontend_system_design/
- Component libraries shipped to islands: ../14_frontend_system_design/09_design_a_component_library.md
Further reading
- Astro: https://astro.build/
- Fresh (Deno): https://fresh.deno.dev/
- Jason Miller — “Islands Architecture”: https://jasonformat.com/islands-architecture/
- patterns.dev — Islands Architecture: https://www.patterns.dev/vanilla/islands-architecture/