Islands Architecture In Depth: Partial Hydration
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
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
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 (viarequestIdleCallback). For interactivity that matters but isn't urgent.client:visible— hydrates only when the component scrolls into the viewport (viaIntersectionObserver). 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.
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 distinctiveChoosing the Right Directive
| Directive | Hydrates When | Best For |
|---|---|---|
| client:load | Immediately | A critical, above-the-fold widget |
| client:idle | Main thread is idle | Important but non-urgent interactivity |
| client:visible | Scrolled into view | Anything below the fold |
| client:media | A media query matches | Device- or viewport-specific components |
| client:only | Client only, no SSR at all | Components using browser-only APIs |
Coding Challenges
Add a React (or Vue/Svelte) counter component to an Astro page with client:load, and confirm the button actually increments when clicked.
📄 View solutionPlace 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 solutionBuild 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 solutionChapter 7 Quick Reference
- No directive — static HTML only, even for a React/Vue/Svelte component; nothing hydrates
client:load— hydrates immediatelyclient:idle— hydrates when the main thread is idleclient:visible— hydrates on scroll into view; defers the JS download, not just executionclient:media="(query)"— hydrates only when a media query matches; unique among this series' frameworksclient:only="react"— client-only rendering, skips SSR entirely- Next chapter: Using React, Vue, or Svelte Components Inside Astro