Building Astro Sites for Performance
A deep dive into optimizing Astro sites for Core Web Vitals, from font loading to lazy image strategies.
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
Imagecomponent 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.