Islands and deployment

Choose a client directive

Match hydration timing to the importance and visibility of the interaction.

A framework component is static HTML until you add a client directive. The directive tells Astro to ship the component’s JavaScript and run it in the browser. It also tells Astro when.

Here are the options, one per component:

<SearchBox client:load />
<NewsletterForm client:idle />
<Comments client:visible />
<MobileMenu client:media="(max-width: 48rem)" />

Each directive is a promise about timing:

  • client:load hydrates as soon as the page loads. For interaction the visitor needs right away.
  • client:idle waits until the browser is done with the important work, using requestIdleCallback. For things that matter, but not in the first second.
  • client:visible waits until the component scrolls into view. For anything below the fold.
  • client:media waits until a media query matches. The mobile menu above only hydrates on small screens. On desktop, it’s plain HTML forever.
  • client:only="react" skips server rendering entirely. The component renders only in the browser.

Hydration is not rendering

This confuses people coming from a single-page app. With any directive except client:only, Astro still renders the component to HTML on the server. The visitor sees the search box immediately. Hydration attaches the JavaScript to that existing HTML so the box starts responding.

So the HTML is there at time zero. The behavior arrives when the directive says. That’s why a low-priority directive is safe: the content never disappears, only the interactivity waits.

client:only

client:only gives up the server HTML. Until the JavaScript downloads and runs, that spot on the page is empty. Reserve it for components that truly can’t render on the server, because they touch window or a browser-only library at import time. A map widget is the classic case.

You must name the framework in the value, client:only="react", because without a server render Astro can’t tell which renderer to use.

How I choose

Start with no directive. Look at the static output. Is it useful on its own? Good.

Then add the least urgent directive that still meets the requirement. A search box in the header is client:load. A newsletter form at the bottom of a post is client:visible. I rarely need client:load on more than one or two components per page.

Test it

Open DevTools, throttle the network to slow 3G, and reload. The client:visible component should not load its bundle until you scroll to it. Then disable JavaScript. A widget may stop working, but the content and navigation must still be there. If they aren’t, the component is doing work that belongs in HTML.

Lesson completed