Updated November 15, 2024

Building Astro Sites for Performance

A deep dive into optimizing Astro sites for Core Web Vitals, from font loading to lazy image strategies.

#astro #performance #web-vitals

The Performance Imperative

Core Web Vitals matter. Not just for SEO ranking signals, but for user experience and conversion rates. A millisecond-slower site is a real revenue impact.

Astro gives you a head start: zero JavaScript by default, static-first rendering, and built-in optimization hooks. But you still need to be intentional.

Font Loading Strategy

The biggest culprit in LCP failures? Custom fonts blocking paint.

@font-face {
  font-family: "Space Grotesk";
  src: url("/fonts/space-grotesk.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

Use font-display: swap to render fallback text immediately. Pair with rel="preload" in your <head>:

<link
  rel="preload"
  as="font"
  type="font/woff2"
  href="/fonts/space-grotesk.woff2"
  crossorigin
/>

Image Optimization

Astro’s Image component with .astro pages:

---
import { Image } from "astro:assets";
import heroImg from "../assets/hero.png";
---

<Image src={heroImg} alt="Hero" width={1200} height={630} />

The component automatically generates responsive variants and WebP format.

Eliminating Layout Shift

Explicit aspect ratios on all media containers:

.hero-container {
  aspect-ratio: 16 / 9;
  width: 100%;
}

This reserves space before the image loads, preventing CLS.

JavaScript Minimalism

Astro islands let you use React/Vue/Svelte where you need interactivity. Keep everything else static.

If you need vanilla JS, keep it lean:

document.querySelectorAll("[data-theme-toggle]").forEach((btn) => {
  btn.addEventListener("click", () => {
    document.documentElement.dataset.theme =
      document.documentElement.dataset.theme === "dark" ? "light" : "dark";
    localStorage.setItem("theme", document.documentElement.dataset.theme);
  });
});

That’s it. Lightweight, no build tool overhead, no hydration penalty.

Metrics to Watch

  • LCP ≤ 2.5s (Largest Contentful Paint)
  • CLS = 0 (Cumulative Layout Shift)
  • INP ≤ 200ms (Interaction to Next Paint)

Run Lighthouse regularly. Automate it in CI if you can.

Astro Config Optimization

export default defineConfig({
  output: "static",
  compressHTML: true,
  vite: {
    ssr: { external: ["sharp"] },
  },
});

compressHTML minifies all output HTML. output: 'static' means zero server overhead.

The Checklist

  • ✅ Preload critical fonts with font-display: swap
  • ✅ Use Astro’s Image component for all media
  • ✅ Set explicit aspect ratios on containers
  • ✅ Keep JavaScript minimal (islands only)
  • ✅ Enable gzip/brotli compression in your host
  • ✅ Monitor Core Web Vitals in production

Follow these patterns and you’ll hit Google’s “Good” thresholds consistently.