Client Directives
Turning a component into an island
Section titled “Turning a component into an island”A framework component imported into an .astro file renders to static HTML — no interactivity — until you add a client directive. The directive tells Astro two things: hydrate this component on the client, and when to do it.
---import Counter from '../components/Counter.jsx';---<Counter /> <!-- static HTML, no JS ships --><Counter client:load /> <!-- an island: hydrates immediately -->The “when” is the whole game. Hydrating earlier makes a component interactive sooner but costs upfront JavaScript and main-thread time; hydrating later keeps the page lean but delays interactivity. Each directive is a different point on that trade-off.
The five directives
Section titled “The five directives”client:load— hydrate immediately, as soon as the page loads. Use for above-the-fold controls that must work the instant the page is usable: a header search, a critical toggle. The most eager, most expensive option.client:idle— hydrate when the browser is idle (viarequestIdleCallback), after the main work of loading is done. Good for interactivity that’s important but not instant — it waits for a quiet moment.client:visible— hydrate when the component scrolls into view (viaIntersectionObserver). The best default for anything below the fold: a comments widget, a footer newsletter form, a carousel far down the page. Its JavaScript never loads if the user never scrolls there.client:media— hydrate only when a CSS media query matches, e.g.client:media="(max-width: 50em)". Use for interactivity that only exists at some breakpoints — a mobile-only hamburger menu that’s pointless on desktop.client:only="react"— skip server rendering entirely and render only on the client. You must name the framework ("react","vue","svelte", …) because Astro won’t render it on the server to detect it. Use for components that can’t render on the server at all — ones that touchwindow,localStorage, or a browser-only library on first render.
flowchart LR start["page loads"] --> load["client:load — hydrate now"] load --> idle["client:idle — when browser is idle"] idle --> visible["client:visible — when scrolled into view"] visible --> media["client:media — when a breakpoint matches"] only["client:only — skip server render, client only"]
Choosing the right one
Section titled “Choosing the right one”A simple decision path covers most cases:
- Must it work the moment the page loads, above the fold? →
client:load. - Is it below the fold, or not needed instantly? →
client:visible(the safest lean default). - Important but can wait for a quiet moment? →
client:idle. - Only relevant at a certain screen size? →
client:media. - Can it not render on the server at all? →
client:only="react".
The instinct to reach for client:load on everything is exactly what islands exist to prevent. Ship the least JavaScript that delivers the interactivity, and let most of your islands be client:visible.