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
| Directive | When it hydrates | Use for |
|---|---|---|
client:load | Immediately on page load | Above-the-fold interactive elements |
client:idle | When browser becomes idle | Below-the-fold components, non-urgent |
client:visible | When component scrolls into viewport | Lazy content like comments, carousels |
client:media="(query)" | When media query matches | Mobile-only components |
client:only="react" | Client-only, no SSR | Components that require browser APIs |
Choosing the right directive
client:loadfor things users interact with immediately (nav menus, search bars, theme toggles)client:idlefor things that should be ready soon but are not blocking (newsletter signup forms)client:visiblefor things far down the page (image carousels, comment sections, embedded widgets)client:onlyfor components that cannot server-render at all (canvas-based visualizations, components usingwindowdirectly)
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
| Concept | Next.js | Astro |
|---|---|---|
| Default behavior | Full React tree ships to client | Zero JS ships to client |
| Interactivity | "use client" marks client components | client:* directives on islands |
| Hydration control | All-or-nothing per component boundary | Per-island, with timing control |
| Multiple frameworks | React only | Any supported framework, mixed |
| Server components | React Server Components | All .astro components are server-only by default |