Which client directive to use depends on two things: whether the component needs to be interactive at all, and when the user might interact with it. If it doesn’t need state or event handlers, use no directive. If it’s below the fold, use client:visible. If it’s visible on load but not urgent, use client:idle. Use client:load only for controls people touch straight away, client:media for UI that exists at one screen size, and client:only only when the component can’t render on the server.
Key Takeaways
- No directive is the best directive: the component renders to HTML and ships no JavaScript.
client:visibleis the right default for most interactive components.client:loadcompetes with your main content for bandwidth and CPU; reserve it.- Small interactions are often better as a plain
<script>in an Astro component.
Decision flow
- Does it need state, effects or event handlers?
No → no directive (or rewrite it as an
.astrocomponent). - Is it a small interaction, like a toggle, tabs or a copy button?
Yes → consider an Astro component with a
<script>instead of a framework island. - Does it only matter at certain screen sizes?
Yes →
client:media="(query)". - Does it break when rendered on the server (uses
windowwhile rendering)? Yes → try moving that code into an effect first; if you can’t →client:only="framework". - Is it below the fold?
Yes →
client:visible(addrootMarginfor heavy components). - Will users interact with it within the first second or two?
Yes →
client:load. No →client:idle.
The directives compared
| Directive | JS loads | Server HTML | Typical use |
|---|---|---|---|
| none | Never | Yes | Static rendering of a framework component |
client:visible |
On scroll into view | Yes | Carousels, comments, maps, below-fold forms |
client:idle |
When browser is idle | Yes | Header search, non-urgent widgets |
client:load |
Immediately | Yes | Above-the-fold forms, primary controls |
client:media |
When query matches | Yes | Mobile nav, desktop-only tools |
client:only |
Immediately, browser only | No | Browser-only libraries |
Examples
<!-- Decorative React component, no interactivity -->
<FeatureGrid />
<!-- Newsletter form halfway down a post -->
<Newsletter client:visible />
<!-- Search in the header -->
<SearchBox client:idle />
<!-- Hero sign-up form above the fold -->
<SignupForm client:load />
<!-- Drawer that only exists on mobile -->
<MobileNav client:media="(max-width: 768px)" />
<!-- Map library that touches window on import -->
<Map client:only="react" />
<!-- Heavy chart: start loading before it's on screen -->
<Chart client:visible={{ rootMargin: '400px' }} />
Costs to keep in mind
- Every hydrated framework ships its runtime once per page. One React island means React on that page.
- Two frameworks mean two runtimes. Standardise on one for islands.
client:onlyrenders nothing until JavaScript runs: reserve space to avoid layout shift, and keep important content out of it.
To measure what each island adds, see finding what’s bloating your Astro bundle. The timing details are in lazy-loading islands, and all directives are listed in the directives cheatsheet. For the broader picture, read the Astro components guide.
FAQ
What’s the default client directive in Astro?
There isn’t one. Without a directive, a framework component ships no JavaScript.
Is client:visible bad for SEO?
No. The server-rendered HTML is in the page. Only the JavaScript waits.
The directives reference has the formal rules.