Islands Architecture In Depth: Partial Hydration

Chapter 7
Islands Architecture In Depth: Partial Hydration
client:load, client:idle, client:visible, client:media, client:only

Chapter 1 claimed Astro ships zero JavaScript by default. Here's where that claim gets paid off concretely: client directives are the only mechanism that opts an individual component into hydration — without one, even a React or Svelte component renders to static HTML and nothing more.

No Directive: Static HTML, No Exceptions

--- import Counter from '../components/Counter.jsx'; --- <!-- renders the button's markup, but it's inert — no JS shipped --> <Counter />

Even though Counter is a real React component with its own useState, without a client directive it's treated exactly like any Astro component: rendered once to HTML, then discarded. Clicking the button does nothing.

The Five Client Directives

<Counter client:load /> <Counter client:idle /> <Counter client:visible /> <Counter client:media="(max-width: 768px)" /> <Counter client:only="react" />
  • client:load — hydrates immediately once the page loads. For genuinely critical, above-the-fold interactivity.
  • client:idle — hydrates once the browser's main thread goes idle (via requestIdleCallback). For interactivity that matters but isn't urgent.
  • client:visible — hydrates only when the component scrolls into the viewport (via IntersectionObserver). Ideal for anything below the fold.
  • client:media="(query)" — hydrates only when a CSS media query matches — a mobile-only menu, for instance, that never hydrates at all on desktop.
  • client:only="react" — skips server rendering entirely, rendering only in the browser. For components that depend on browser-only APIs that would error during the build.
This isn't just deferred execution — the JS never even downloads until needed
client:visible and client:media aren't only deferring when a component's JavaScript runs — they defer whether its bundle is even downloaded at all. An island using client:visible placed far down a long page genuinely doesn't fetch its own JS bundle until the user scrolls near it; a client:media-gated mobile menu never downloads its JS on a desktop visit at all. This is a real, measurable difference from a typical single-page application, which downloads its entire framework runtime and every component's code upfront regardless of whether the visitor ever scrolls that far or is on that device.
client:media is genuinely distinctive
Among every framework covered in this series, a conditional-on-a-media-query hydration directive is unique to Astro. React, Vue, and Svelte all have ways to conditionally render markup based on viewport size, but none has a first-class mechanism for conditionally shipping and hydrating a component's own JavaScript based on a media query — that's specifically an islands-architecture idea, not something a monolithic-runtime framework needs or offers.

Choosing the Right Directive

DirectiveHydrates WhenBest For
client:loadImmediatelyA critical, above-the-fold widget
client:idleMain thread is idleImportant but non-urgent interactivity
client:visibleScrolled into viewAnything below the fold
client:mediaA media query matchesDevice- or viewport-specific components
client:onlyClient only, no SSR at allComponents using browser-only APIs

Coding Challenges

Challenge 1

Add a React (or Vue/Svelte) counter component to an Astro page with client:load, and confirm the button actually increments when clicked.

📄 View solution
Challenge 2

Place the same counter far down a long page using client:visible instead, and confirm via your browser's Network tab that its JS bundle only loads once it's scrolled into view.

📄 View solution
Challenge 3

Build a mobile-only interactive component (e.g. a hamburger menu) using client:media, and confirm by resizing your browser that it only hydrates below the breakpoint.

📄 View solution

Chapter 7 Quick Reference

  • No directive — static HTML only, even for a React/Vue/Svelte component; nothing hydrates
  • client:load — hydrates immediately
  • client:idle — hydrates when the main thread is idle
  • client:visible — hydrates on scroll into view; defers the JS download, not just execution
  • client:media="(query)" — hydrates only when a media query matches; unique among this series' frameworks
  • client:only="react" — client-only rendering, skips SSR entirely
  • Next chapter: Using React, Vue, or Svelte Components Inside Astro