Frontend / Rendering modes / 06_islands_architecture.md

Islands Architecture — Astro, Fresh, Iles

Updated 5 interview angles 6 min read source
On this page13
  1. What does an island look like?
  2. client:* directives.
  3. Multi-framework support.
  4. When do islands beat React/Next.js?
  5. How is RSC + selective hydration similar / different?
  6. Astro’s content collections.
  7. Server endpoints in Astro.
  8. How does deployment compare?
  9. When did “islands” become a thing?
  10. Gotchas
  11. Related
  12. Further reading
  13. Interview angle

Islands Architecture — Astro, Fresh, Iles

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.

What does an island look like?

Astro example:

astro
---
// 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.

client:* directives.

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 touch window and 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).

Multi-framework support.

Astro can host React + Vue + Svelte + Solid components in the same page. Each island ships only its framework runtime, shared across same-framework islands.

astro
<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.

When do islands beat React/Next.js?

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.

How is RSC + selective hydration similar / different?

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).

Astro’s content collections.

Built-in API for typed markdown/MDX content:

ts
// 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),
    }),
  }),
};
astro
---
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.

Server endpoints in Astro.

Astro can be hybrid (some routes pre-rendered, some SSR’d) — output: "hybrid" in config. API routes work like Next.js:

ts
// 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.

How does deployment compare?

  • 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.

When did “islands” become a thing?

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

  • 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:only skips 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.

Further reading

Interview angle 5

  • “What is islands architecture?” - a mostly-static HTML page with independent interactive components that hydrate on their own. The default is zero JavaScript, and interactivity is opted into per component rather than assumed for the whole tree.
  • “What does it give you that React’s approach does not?” - explicit hydration triggers. client:load, client:visible, client:idle and client:media let you say when each island becomes interactive, which React’s automatic prioritisation does not expose. A below-the-fold widget can simply never hydrate for a user who does not scroll.
  • “How does multi-framework hosting work?” - each island declares its own framework, and the runtime for that framework ships only if an island on the page uses it. A page with one React island and no Vue island pays for React only.
  • “Islands or RSC?” - different mental models converging on the same outcome. Islands start from a static page and sprinkle interactivity in; RSC starts from a React application and marks parts as server-only. Pick islands for content sites, React for app-shaped products, and a hybrid deployment is a legitimate answer.
  • “What is the deployment difference?” - a static islands build is files on a CDN with no compute at all, which is the cheapest thing you can run. Turning on SSR mode brings back a server and most of the operational questions with it.