Skip to content

Engineering

Shipping genuinely fast websites with Astro

Why static-first architecture wins for content sites, and the concrete techniques I use to keep Lighthouse scores at 100 without giving up interactivity.

Alex CarterUpdated2 min read

Every few years the pendulum swings between “ship everything as JavaScript” and “ship as little JavaScript as possible”. For content sites — blogs, portfolios, docs — the second camp has clearly won, and Astro is the sharpest tool in it.

The islands model, briefly

Astro renders your entire page to static HTML at build time. Components that need interactivity opt in explicitly:

src/pages/index.astro
---
import Chart from '../components/Chart.tsx';
---
<!-- Rendered to HTML, zero JS shipped -->
<h1>Traffic report</h1>
<!-- Hydrated only when it scrolls into view -->
<Chart client:visible />

Everything else — headers, footers, article bodies, cards — ships as plain HTML and CSS. The result is that your baseline JavaScript cost is close to zero, and every kilobyte you do ship is a deliberate decision.

Techniques that actually move the needle

Prefetch on hover and viewport

Astro’s built-in prefetch turns full page loads into near-instant navigations:

astro.config.ts
export default defineConfig({
site: 'https://example.com',
prefetch: {
prefetchAll: true,
defaultStrategy: 'viewport',
},
});

By the time a visitor clicks a link, the next page’s HTML is usually already in the cache. You get SPA-feeling navigation without a router, a hydration step, or a client-side state bug budget.

Self-host your fonts

A @fontsource import costs one line and removes an entire third-party origin:

import '@fontsource-variable/inter';

No connection setup to fonts.googleapis.com, no layout shift from late-loading fonts, no consent-mode headaches. Variable fonts mean one file covers every weight you use.

Optimize the images you forgot about

Hero images get attention; the avatar in your footer doesn’t. Audit with the network tab sorted by size — the offenders are rarely where you expect. Set explicit width and height everywhere so the browser can reserve space before the image arrives.

What I stopped worrying about

  • Client-side routing. Prefetched MPA navigation is indistinguishable for content sites.
  • Skeleton screens. If the page renders in under 100ms, a skeleton is just a flash of fake content.
  • Complex state management. Static pages don’t have state. This is a feature.

The scoreboard

This theme — the one you’re reading — ships roughly 4KB of JavaScript on a typical page: the theme toggle, mobile navigation, and a lazily-loaded search index. Lighthouse gives it a 100 in performance, and more importantly, it feels instant on a mid-range phone over 4G.

Fast is a feature. Static-first is how you afford it.

Share
All articles

Alex Carter

Full-stack Developer

Related articles

navigate openesc close