Skip to content

Performance & Zero-JS

Most frameworks make you claw performance back: you build the app, it’s slow, and you optimize. Astro inverts that — you start at a fast baseline (static HTML, zero JavaScript) and your job is to not lose it. Every kilobyte of client JavaScript is one you added deliberately, so the question is always “does this really need to ship?”

The islands lesson covered what the client directives do; here’s the performance framing. The directive you pick decides how much JavaScript runs, and when:

DirectiveShips & runs JSUse when
client:loadImmediately on page loadCritical, above-the-fold interactivity (rare)
client:idleWhen the browser goes idleInteractive, but not needed instantly
client:visibleWhen it scrolls into viewBelow-the-fold islands (the best default)
client:mediaOnly at a breakpointA mobile-only or desktop-only widget

The rule of thumb: reach for client:visible (or client:idle) before client:load. A carousel three screens down doesn’t need its JavaScript during initial load; hydrating it with client:visible keeps that cost off the critical path. Overusing client:load quietly rebuilds the “everything hydrates upfront” cost Astro exists to avoid.

flowchart LR
  load["client:load"] --> upfront["JS on the critical path (heaviest)"]
  idle["client:idle"] --> later["JS after first paint"]
  visible["client:visible"] --> onscroll["JS only when scrolled into view (lightest)"]
Directives move JS cost off the critical path

Fast first load is half the story; fast navigation is the other half. Astro can prefetch a page’s HTML before the user clicks, so the next page is essentially instant. Enable it in config, then it prefetches links on hover/viewport by default, or opt a link in explicitly:

<a href="/pricing" data-astro-prefetch>Pricing</a>

Paired with <ClientRouter />, prefetch makes multi-page navigation feel like a SPA — the HTML is already in the browser cache when the click happens — without a client-side data layer.

Don’t optimize by vibes. Two things to look at:

  • The islands you shipped. Build the site and check which components actually became islands (and with which directive). A component you thought was static but is hydrating is wasted JavaScript; a client:load that could be client:visible is misplaced cost.
  • Lighthouse / Core Web Vitals. Run Lighthouse (in Chrome DevTools) against the built site. Watch Largest Contentful Paint (usually your hero image — see the assets lesson), Cumulative Layout Shift (reserve image/font space), and Total Blocking Time (your island JavaScript).

The Astro baseline usually scores near-perfect out of the box. When a score drops, it’s almost always one of: an unoptimized image, a font causing shift, or an island hydrating more eagerly than it needs to.

What is Astro’s performance philosophy?
Which client directive is the best default for a below-the-fold island?
What does prefetch do, and how does it pair with view transitions?
A Lighthouse score drops on an Astro page. What is the most likely cause?