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:
- Renders static HTML for most of the page
- Hydrates only islands that need interactivity
- Reduces JavaScript significantly
- Improves performance (LCP, TTI, TBT)
- 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.
