Islands

Astro’s islands architecture is the core idea that makes it different from React-based frameworks. Most of the page is static HTML. Interactive parts are isolated “islands” that hydrate independently.

The mental model

In Next.js or a typical React app, the entire page is a React tree. Even static content carries the React runtime.

In Astro, the page is HTML. Interactive components are opt-in islands floating in that HTML. Each island hydrates on its own, loads its own JS, and does not block other islands.

+--------------------------------------------------+
|  Static HTML header (no JS)                       |
+--------------------------------------------------+
|  Static article content (no JS)                   |
|                                                    |
|  +------------+        +----------------------+   |
|  | Search bar |        | Comments section     |   |
|  | (island)   |        | (island)             |   |
|  +------------+        +----------------------+   |
|                                                    |
|  Static footer (no JS)                            |
+--------------------------------------------------+

Client islands

By default, all UI framework components (React, Svelte, Vue, etc.) render to static HTML with zero JS:

<!-- Renders to static HTML, no JavaScript shipped -->
<ReactCounter />

To make it interactive, add a client:* directive:

<!-- Now it hydrates and becomes interactive -->
<ReactCounter client:load />

Hydration directives

DirectiveWhen it hydratesUse for
client:loadImmediately on page loadAbove-the-fold interactive elements
client:idleWhen browser becomes idleBelow-the-fold components, non-urgent
client:visibleWhen component scrolls into viewportLazy content like comments, carousels
client:media="(query)"When media query matchesMobile-only components
client:only="react"Client-only, no SSRComponents that require browser APIs

Choosing the right directive

  • client:load for things users interact with immediately (nav menus, search bars, theme toggles)
  • client:idle for things that should be ready soon but are not blocking (newsletter signup forms)
  • client:visible for things far down the page (image carousels, comment sections, embedded widgets)
  • client:only for components that cannot server-render at all (canvas-based visualizations, components using window directly)

Server islands

Server islands are the counterpart: they defer expensive server-side rendering out of the critical path.

---
import UserAvatar from '../components/UserAvatar.astro';
---
<!-- Main page renders immediately; avatar loads async from server -->
<UserAvatar server:defer />

Server islands:

  • render on the server, not the client
  • load in parallel with the main page
  • support fallback content while loading
  • are useful for personalized content on otherwise cacheable pages (e.g. user avatar on a product page)
<UserAvatar server:defer>
  <!-- Fallback shown while island loads -->
  <img src="/default-avatar.png" alt="Loading..." slot="fallback" />
</UserAvatar>

Mixing frameworks

Because islands are independent, you can use multiple UI frameworks on the same page:

---
import ReactCounter from '../components/Counter.jsx';
import SvelteToggle from '../components/Toggle.svelte';
import VueCard from '../components/Card.vue';
---
<ReactCounter client:load />
<SvelteToggle client:visible />
<VueCard client:idle />

Each framework only loads its own runtime for its own island.

Sharing state between islands

Islands are isolated by default. To share state, use a framework-agnostic store like Nano Stores:

// stores/counter.js
import { atom } from 'nanostores';
export const count = atom(0);
// React island
import { useStore } from '@nanostores/react';
import { count } from '../stores/counter';

export default function Counter() {
  const $count = useStore(count);
  return <button onClick={() => count.set($count + 1)}>{$count}</button>;
}

Both React and Svelte islands can subscribe to the same Nano Store.

Comparison with Next.js

ConceptNext.jsAstro
Default behaviorFull React tree ships to clientZero JS ships to client
Interactivity"use client" marks client componentsclient:* directives on islands
Hydration controlAll-or-nothing per component boundaryPer-island, with timing control
Multiple frameworksReact onlyAny supported framework, mixed
Server componentsReact Server ComponentsAll .astro components are server-only by default