Skip to content

What is an Island?

In a React or Next.js app the browser receives one large JavaScript bundle. React boots up, walks the entire component tree, and attaches event listeners everywhere — even on components that never do anything interactive. This is full-app hydration.

The Island Architecture, coined by Jason Miller and popularised by Astro, inverts the default: pages are inert HTML. Only the components that need interactivity are hydrated, and they hydrate independently — like islands in a sea of static markup.

React
// Next.js page — the WHOLE tree hydrates.
// Even <Header> and <Footer> download JS.
export default function BlogPost({ post }) {
return (
<>
<Header /> {/* JS downloaded + hydrated */}
<article>{post.content}</article> {/* JS downloaded + hydrated */}
<LikeButton postId={post.id} /> {/* JS downloaded + hydrated */}
<RelatedPosts posts={post.related} />{/* JS downloaded + hydrated */}
<Footer /> {/* JS downloaded + hydrated */}
</>
);
}
// Total JS budget: EVERYTHING
Astro
---
// Astro page — only <LikeButton> needs JS.
import Header from '../components/Header.astro';
import LikeButton from '../components/LikeButton.jsx';
import RelatedPosts from '../components/RelatedPosts.astro';
import Footer from '../components/Footer.astro';
const { post } = Astro.props;
---
<Header /> <!-- 0 KB JS -->
<article>{post.content}</article> <!-- 0 KB JS -->
<LikeButton client:visible postId={post.id} /> <!-- hydrated -->
<RelatedPosts posts={post.related} /> <!-- 0 KB JS -->
<Footer /> <!-- 0 KB JS -->
{/* Total JS budget: ONE component */}

On a typical content page — a blog post, a landing page, a docs site — the vast majority of components are purely presentational. They display data; they never respond to clicks. Hydrating them wastes CPU and delays Time to Interactive.

Astro measures this precisely. A page with a single LikeButton island only downloads the JavaScript for that button and its React runtime — nothing else. A Next.js equivalent ships React plus every component on the page.

Typical real-world results on content-heavy sites:

  • 90%+ reduction in JavaScript shipped
  • Faster Largest Contentful Paint — the HTML arrives parsed, no hydration blocking rendering
  • Lower Time to Interactive — only one small island needs to boot

The pattern is called partial hydration because only a part of the page is hydrated. Each island:

  1. Renders to HTML on the server/build (like any .astro component)
  2. Ships its own JS bundle to the browser
  3. Hydrates independently — islands do not share a React root

That last point matters: islands are isolated. There is no single React tree connecting them, which means no shared React context across islands by default. You will learn how to work with that in When to Use an Island.

React
// React: ONE root, one reconciler, shared context
import ReactDOM from 'react-dom/client';
ReactDOM.createRoot(document.getElementById('root'))
.render(<App />);
// Every component inside <App> is part of this tree.
// React.createContext() works across the whole tree.
Astro
---
// Astro: MULTIPLE roots, one per island
// Astro mounts each island independently:
// ReactDOM.createRoot(islandEl).render(<Counter />)
// ReactDOM.createRoot(likeEl).render(<LikeButton />)
// Each island has its own React root.
// React.createContext() does NOT cross island boundaries.
import Counter from '../components/Counter.jsx';
import LikeButton from '../components/LikeButton.jsx';
---
<Counter client:load />
<LikeButton client:visible postId={42} />
What is 'partial hydration'?
In Astro, how many React roots are created for a page with three React islands?
Why does full-app hydration increase Time to Interactive even on mostly-static pages?
Does React Context work across two separate Astro islands on the same page?