NNicheStackTutorials
← All tutorials
Astro

Astro Zero-JS Pages: How I Hit 100 Lighthouse Performance

client:load vs client:visible, islands architecture in practice, and removing every unnecessary hydration point.

·12 min read·Astro
Astro Zero-JS Pages: How I Hit 100 Lighthouse Performance cover

Tested on: Astro 5.7, Lighthouse 12 Site: 28-page docs blog. Score: 100 Performance, 100 Accessibility.

Default Astro is already mostly zero-JS

A .astro page ships HTML only. JS appears when you add client:* directives.

Audit — find accidental hydration

grep -r "client:" src/

My offenders:

  • <Header client:load /> — mobile menu on every page
  • <Search client:load /> — on pages without search box visible
  • Starlight theme toggle client:load on static docs

Fix Header — only hydrate when needed

Before:

---
import Header from '../components/Header.svelte';
---
<Header client:load />

After — mobile menu only:

---
import MobileMenu from '../components/MobileMenu.svelte';
---
<header>
  <nav class="desktop-nav"><!-- pure HTML links --></nav>
  <MobileMenu client:media="(max-width: 768px)" />
</header>

client:media hydrates only on matching viewport. Desktop users: 0 KB menu JS.

Blog post pages — pure static

---
// src/pages/blog/[slug].astro
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((p) => ({ params: { slug: p.slug }, props: { post: p } }));
}

const { post } = Astro.props;
---

<Layout title={post.data.title}>
  <article set:html={await post.render()} />
</Layout>

No client:* anywhere. Zero JS per post.

Error — Starlight forcing JS

@astrojs/starlight adds client:load to theme selector by default

Override in astro.config.mjs if you don't need toggle:

starlight({
  components: {
    ThemeSelect: './src/components/Empty.astro'
  }
})

Empty.astro: --- --- (renders nothing).

Measure

npm run build
# Check dist/_astro/ chunk count
ls dist/_astro/*.js | wc -l

My blog listing page: 0 JS files. Contact page with form: 1 chunk (12 KB).

Lighthouse proof

Performance: 100
FCP: 0.6s
LCP: 0.9s
TBT: 0ms
Total JS: 0 KB (content pages), 12 KB (form page)

Islands aren't magic. They're discipline. Default to no client:*, add only with evidence.

Tags: AstroPerformanceIslandsZero JS