Skip to main content

Home / guides / Astro

Astro SEO & Core Web Vitals Optimization Guide

Updated · by RankVitals

Astro’s islands architecture ships zero JavaScript by default and hydrates only the components you explicitly mark — which makes it the easiest modern framework to hit perfect scores with, and makes every regression self-inflicted. Slow Astro sites are slow because hydration directives were scattered carelessly, images bypass astro:assets, or third-party scripts moved in. This guide keeps the defaults working for you.

Real-world measurements, July 2026

Mobile lab results for well-known Astro sites, measured with Lighthouse — the same engine behind Google PageSpeed Insights and RankVitals tests. Snapshot taken July 19, 2026 — your numbers will differ, which is the point: measure your own site.

SitePerf scoreLCP (lab)TBTCLS
astro.build98/1002.3 s0 ms0
firebase.blog99/1001.0 s65 ms0.04

Lab data: Lighthouse 13, emulated Moto G Power over simulated 4G — the identical methodology PageSpeed Insights uses for its lab section. Single runs; lab numbers vary a few percent between runs.

What real Astro sites measure

We measured Astro’s own site and the Firebase blog — Google’s developer blog, rebuilt on Astro — with Lighthouse 13, mobile emulation, in July 2026, and they demonstrate the islands thesis better than any synthetic benchmark. astro.build scored 98/100: LCP 2.3 s, CLS 0, and a Total Blocking Time of literally 0 ms — the zero-JavaScript default measured in the wild. firebase.blog scored 99/100 with LCP at 1.0 s, TBT 65 ms, and CLS 0.04. These are not toy pages; they are production sites with search, navigation, theming, and analytics. When the baseline is this high the guide changes character: it is not about digging out of a hole but about not digging one — every regression from here is a hydration directive, an unoptimized image, or a third-party script you chose.

Islands: the discipline is in the directives

Astro renders components to plain HTML at build time; JavaScript reaches the browser only where you add a client directive. That granularity is the whole performance story, so treat directives as a budget. client:load hydrates immediately and belongs only on components that must be interactive at first paint (a mobile nav toggle, a cart button). client:visible defers hydration until the component scrolls into view — the right default for carousels, tabs, and anything below the fold. client:idle sits between them for important-but-not-critical widgets.

The regression pattern to watch for: a React or Svelte component gets client:load “to be safe,” then imports a UI library, and suddenly a content page ships a framework runtime it never needed. Audit with the build output or Astro’s dev toolbar — every island should have a reason for its directive. For interactivity as simple as a toggle or accordion, a plain <script> tag or native <details> element costs less than hydrating any framework island.

Images: astro:assets or nothing

Astro’s built-in image pipeline — the <Image /> and <Picture /> components from astro:assets — resizes, converts to WebP/AVIF, generates srcset, sets width and height (killing image CLS), and lazy-loads by default. The classic Astro image mistake is bypassing it: a plain <img> pointing at a file in public/ gets none of that, and public/ folders full of unoptimized PNGs are the most common finding on slow Astro sites. Keep images in src/, import them, and let the pipeline run at build time.

One deliberate exception: the LCP image. Astro lazy-loads images by default, and a lazy-loaded hero is a self-inflicted LCP penalty — set loading="eager" and fetchpriority="high" on the hero via the component’s props. For remote images (a CMS, a CDN), authorize the domains in astro.config so the pipeline can optimize them instead of passing originals through.

Fonts, styles, and the parts Astro already solved

Astro bundles and minifies CSS per page and inlines small stylesheets automatically — critical CSS is largely handled. Scoped styles in .astro components keep the cascade small. Fonts are yours to manage: self-host WOFF2 files with font-display: swap and preload the primary text face, or use the experimental Fonts API in recent Astro versions which wires optimized loading for you. Avoid third-party font CSS (an extra origin plus a render-blocking stylesheet) when self-hosting is a copy-paste away.

View Transitions (the ClientRouter) add SPA-feel navigation for a few kilobytes — cheap, but not free on TBT-sensitive pages; add them for polish, not by default. And prefetch is built in: enable it and Astro fetches links on hover or when they enter the viewport, which makes subsequent navigations feel instant without a client router at all.

Rendering modes: static unless proven otherwise

Astro pre-renders every page to static HTML by default, and static pages on a CDN are the fastest thing you can serve — TTFB becomes cache latency. Reach for on-demand rendering (an adapter for Node, Vercel, Netlify, or Cloudflare with prerender disabled per route) only for pages that genuinely differ per request: dashboards, search results, personalized content. A blog or marketing site has no business rendering on demand.

When you do go server-rendered, your TTFB is now your code plus your data sources — cache aggressively at the CDN with proper Cache-Control headers, and consider server islands (Astro 5) to keep the shell static while personalized fragments stream in separately. The framework’s zero-JS guarantee still applies either way; rendering mode changes where HTML comes from, not how much JavaScript ships.

Measuring Astro correctly

astro dev serves unbundled, unoptimized modules — never measure it. Run astro build and astro preview locally, or measure the deployed site, where bundling, minification, image optimization, and compression are all real. Because Astro sites ship so little JavaScript, third-party scripts dominate whatever remains: an analytics tag, a chat widget, or an ad script frequently costs more than the entire framework. The waterfall makes this obvious — if TBT is nonzero on an Astro site, the cause is almost always an island that hydrated too eagerly or a third-party script.

The SEO layer needs explicit setup that starter templates skip: @astrojs/sitemap for sitemap generation (plus a robots.txt pointing at it), canonical URLs in your base layout, and JSON-LD where relevant — Astro renders it all statically, so crawlers and AI engines get complete HTML without executing anything. Verify with a crawl rather than assuming the framework did it; Astro deliberately does nothing you didn’t ask for.

What our audit checks on Astro sites

These are real checks from the RankVitals SEO audit that most often fire on Astro sites. Run the audit to see which ones apply to yours.

Frequently asked questions

Why is my Astro site not scoring 100?

Astro’s baseline is near-perfect, so look at what was added: islands hydrated with client:load that could be client:visible, images served from public/ instead of astro:assets, a lazy-loaded hero image, or third-party scripts. On most Astro sites a single analytics or chat script outweighs the entire framework.

When should I use client:load versus client:visible?

client:load only for components that must respond instantly at page load — navigation toggles, cart state. client:visible for everything below the fold; it defers hydration until the user scrolls there. client:idle for above-the-fold widgets that matter soon but not immediately. Default to the laziest directive that doesn’t hurt UX.

Does Astro work for dynamic, logged-in apps?

Yes — with an adapter, routes can render on demand, and server islands let a static shell wrap personalized fragments. But if most of your app is client-state-heavy interactivity, a full framework like Next.js may fit better. Astro’s sweet spot is content-forward sites where most pixels are static.

Do I need a CDN in front of an Astro site?

Static Astro output should be served from a CDN — that is the deployment model, and hosts like Netlify, Vercel, and Cloudflare Pages do it automatically. There is nothing to configure in Astro itself; just avoid serving static builds from a single-region origin server without edge caching.

Measure your Astro site now

Free speed test with waterfall + filmstrip, and an SEO audit in the same tool.

Enter a public website address, including https://. No account is needed to start a trial test.