Skip to main content
Ganesh Joshi
Back to Blogs

Islands architecture: ship less JavaScript with partial hydration

February 17, 20265 min read
Tips
Code and component architecture on screen

The islands architecture keeps most of the page as static HTML and only hydrates small "islands" of interactivity. This reduces JavaScript and improves performance, especially on content-heavy sites.

The problem with full hydration

Traditional SPAs and SSR frameworks hydrate the entire page:

Full page hydration:
┌─────────────────────────────────────────┐
│ Header (JS)                             │
├─────────────────────────────────────────┤
│ Navigation (JS)                         │
├─────────────────────────────────────────┤
│ Article content (JS)                    │
│ ... 2000 words of text ...              │
│ (still needs JS runtime)                │
├─────────────────────────────────────────┤
│ Comments (JS)                           │
├─────────────────────────────────────────┤
│ Footer (JS)                             │
└─────────────────────────────────────────┘

JavaScript: 200KB+

Even static content requires the framework runtime. The user downloads JS for text that never changes.

How islands architecture works

Islands architecture:
┌─────────────────────────────────────────┐
│ Header (static HTML)                    │
├─────────────────────────────────────────┤
│ Navigation (static HTML)                │
├─────────────────────────────────────────┤
│ Article content (static HTML)           │
│ ... 2000 words of text ...              │
│ (no JS needed)                          │
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ Comments Widget (ISLAND - React)    │ │
│ │ Interactive: pagination, submit     │ │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ Footer (static HTML)                    │
└─────────────────────────────────────────┘

JavaScript: 40KB (just for comments)

Only the interactive parts load JavaScript.

Benefits

Benefit Description
Less JavaScript Only interactive components load JS
Faster LCP Static HTML renders immediately
Better TTI Less JS to parse and execute
Smaller bundles Ship only what's needed
Progressive enhancement Works without JS

Astro implementation

Astro is the most popular islands framework.

Default: zero JavaScript

---
// src/pages/index.astro
import Header from '../components/Header.astro';
import Article from '../components/Article.astro';
---

<Header />
<Article />
<!-- No JavaScript shipped -->

Adding an island

---
import Header from '../components/Header.astro';
import Article from '../components/Article.astro';
import Comments from '../components/Comments.tsx'; // React component
---

<Header />
<Article />

<!-- This is an island - loads React + component code -->
<Comments client:load />

Hydration directives

Directive When JS loads
client:load Immediately on page load
client:idle When browser is idle
client:visible When component enters viewport
client:media When media query matches
client:only Client-side render only
<!-- Load immediately -->
<SearchBox client:load />

<!-- Load when visible (lazy) -->
<Comments client:visible />

<!-- Load when idle -->
<Analytics client:idle />

<!-- Load on mobile only -->
<MobileMenu client:media="(max-width: 768px)" />

Mixing frameworks

---
import ReactCounter from '../components/Counter.tsx';
import VueCarousel from '../components/Carousel.vue';
import SvelteForm from '../components/Form.svelte';
---

<ReactCounter client:visible />
<VueCarousel client:visible />
<SvelteForm client:load />

<!-- Each loads only its framework runtime -->

Fresh (Deno) implementation

Fresh uses islands by default:

project/
  routes/
    index.tsx      # Server-rendered
  islands/
    Counter.tsx    # Automatically an island
// routes/index.tsx
import Counter from '../islands/Counter.tsx';

export default function Home() {
  return (
    <div>
      <h1>Welcome</h1>
      <p>This is static.</p>
      <Counter start={0} /> {/* This is an island */}
    </div>
  );
}

Components in the islands/ folder are automatically hydrated.

When to use islands

Good fit

  • Content sites - Blogs, documentation, news
  • Marketing pages - Landing pages, product pages
  • E-commerce - Product listings (interactive cart island)
  • Portfolios - Mostly static with contact forms
  • Documentation - Text-heavy with search widget

Not ideal

  • Full SPAs - Dashboard apps with constant interaction
  • Real-time apps - Chat, collaborative editing
  • Highly dynamic UIs - Apps where everything is interactive

Islands vs Server Components

Both reduce client JavaScript:

Aspect Islands Server Components
Framework Astro, Fresh Next.js, React
Unit Component Component tree
Mixing Multiple frameworks React only
Streaming Yes Yes
Partial hydration Explicit Implicit

Server Components are React-specific and work within the React ecosystem. Islands are framework-agnostic and work with any UI library.

Implementation patterns

Static wrapper with interactive child

---
import ProductCard from '../components/ProductCard.astro';
import AddToCartButton from '../components/AddToCartButton.tsx';
---

<ProductCard>
  <img src="/product.jpg" alt="Product" slot="image" />
  <h2 slot="title">Product Name</h2>
  <p slot="description">Product description...</p>
  
  <!-- Only the button needs JS -->
  <AddToCartButton client:visible productId="123" slot="action" />
</ProductCard>

Lazy-loaded interactive sections

---
import Article from '../components/Article.astro';
import Comments from '../components/Comments.tsx';
import RelatedProducts from '../components/RelatedProducts.tsx';
---

<Article />

<!-- Load comments when scrolled into view -->
<Comments client:visible />

<!-- Load related products when idle -->
<RelatedProducts client:idle />

Progressive enhancement

---
import SearchForm from '../components/SearchForm.astro';
import SearchWithAutocomplete from '../components/SearchWithAutocomplete.tsx';
---

<!-- Works without JS -->
<noscript>
  <SearchForm />
</noscript>

<!-- Enhanced with JS -->
<SearchWithAutocomplete client:load />

Performance comparison

Scenario Full hydration Islands
Blog post (2000 words) 150KB JS 0KB JS
+ Comment form +50KB 50KB
+ Search box +30KB +30KB
Total 230KB 80KB

Islands ship JavaScript only for interactive parts.

Summary

Islands architecture:

  1. Renders static HTML for most of the page
  2. Hydrates only islands that need interactivity
  3. Reduces JavaScript significantly
  4. Improves performance (LCP, TTI, TBT)
  5. Supports multiple frameworks in one project

Use islands for content-heavy sites with scattered interactivity. For full-app interactivity, traditional frameworks may be simpler.

The Astro documentation and Fresh documentation have complete guides for implementing islands.

Frequently Asked Questions

Islands architecture keeps most of the page as static HTML and only hydrates small 'islands' of interactivity. This reduces JavaScript and improves performance.

SPAs hydrate the entire page with JavaScript. Islands only hydrate the components that need interactivity. Static content stays static with zero JS.

Astro is the most popular. Others include Fresh (Deno), Eleventy with is-land, and Marko. Next.js Server Components have similar benefits.

Islands work best for content-heavy sites with few interactive elements: blogs, documentation, marketing pages. For highly interactive apps, traditional frameworks may be simpler.

Yes. Astro lets you use React, Vue, Svelte, and others in the same project. Each island loads only its framework runtime.

Related Posts