Skip to content

Astro PageSpeed Optimization Guide

A current, measurement-first guide to optimizing Astro images, islands, rendering, fonts, scripts, caching, and Lighthouse results.

GuideProgramming

By

Updated 5 min read

Astro starts from a useful performance model: .astro components render to HTML, and framework components only send client-side JavaScript when you opt into hydration. That reduces accidental JavaScript, but it does not make every Astro site fast automatically. Large images, eager islands, third-party scripts, slow on-demand rendering, and unstable layouts can still dominate the result.

This guide focuses on current Astro APIs and a repeatable way to find the real bottleneck before changing code.

Measure before changing anything

Start with the exact production URL and record the environment. A Lighthouse performance score is a simulated lab result, while Core Web Vitals are field measurements collected from real visits. The two answer different questions. The PageSpeed Insights guide explains how to read them together.

For lab comparisons:

  1. Test the same URL and device profile.
  2. Run Lighthouse several times and compare a representative median run rather than one unusually good result.
  3. Record LCP, CLS, TBT, FCP, and Speed Index, not only the overall score.
  4. Change one bottleneck at a time and repeat the same test.

Use Chrome DevTools Performance when you need a trace showing which request, task, or layout event caused the metric. Use field data when it exists to confirm that the change helps actual visitors.

Keep islands intentional

Astro renders UI framework components as HTML by default. Client-side JavaScript is added only when a component uses a client:* directive, as described in Astro's islands architecture documentation.

Choose the least eager directive that still meets the interaction requirement:

<SearchBox client:load />
<Reviews client:visible />

client:load is appropriate for interaction needed immediately. client:visible can delay an off-screen widget until it approaches the viewport. Other directives can hydrate after idle time or under a media condition. The important step is to inspect what each island ships: a small component can still pull in a large dependency graph.

Avoid wrapping an entire page in one hydrated framework component for convenience. Keep static headings, article text, navigation, and product details in server-rendered Astro markup where possible. Split interactive controls into focused islands, and load a heavy component conditionally when the visitor actually requests it.

Optimize the LCP image

Use the current astro:assets APIs rather than the retired @astrojs/image integration. Astro's image guide and astro:assets reference document the supported Image and Picture components.

After importing Image from astro:assets and a local image in the component script, render it with explicit responsive intent:

<Image
  src={hero}
  alt="Analytics dashboard showing weekly traffic trends"
  widths={[640, 960, 1280]}
  sizes="(max-width: 720px) 100vw, 1200px"
  priority
/>

Astro's current priority prop applies eager loading, synchronous decoding, and high fetch priority for an above-the-fold image. Reserve it for the image that is genuinely likely to become LCP. Prioritizing every image competes for bandwidth. For images below the fold, keep lazy loading and provide realistic responsive sizes so the browser does not download a needlessly large candidate.

Always provide descriptive alt text when the image conveys information, and let the component preserve intrinsic dimensions to reduce layout movement. If an image is decorative, use an empty alt attribute instead of stuffing keywords into it.

If LCP is still slow, use the trace to separate its phases: server response, resource discovery, resource download, and render delay. An optimized file cannot compensate for an image discovered late behind client-side rendering.

Choose static or on-demand rendering per route

Astro pre-renders routes at build time by default. Current Astro also supports on-demand rendering with an adapter and route-level opt-outs. The official on-demand rendering guide recommends starting with static output until a route genuinely needs request-time work.

Use static rendering for content that can be generated during a build. Use on-demand rendering for personalization, request-specific authorization, or data that must be fresh on every request. In a mixed site, keep the static default and opt individual routes into server rendering:

export const prerender = false;

Place that export in the page's Astro component script, then perform request-specific work there and render the result in the template.

For on-demand routes, profile the whole server path: database queries, external APIs, cold starts, cache behavior, and HTML streaming. Cache only responses that are safe to share, define an invalidation strategy, and verify the resulting HTTP headers at the CDN rather than assuming a platform default.

Control fonts, scripts, and layout shifts

Fonts should be a deliberate subset of the weights and styles used by the page. Prefer WOFF2, preload only a font needed for initial text, and use a fallback with compatible metrics. font-display controls rendering behavior, but it does not by itself guarantee that text will never shift. The CLS guide covers font metric overrides and other layout-shift sources.

Astro processes ordinary component <script> tags with bundling and deduplication. Its client-side scripts guide also explains when inline scripts behave differently. Keep first-party scripts small and treat every analytics, chat, video, and consent script as a performance dependency.

For third-party code:

  • load it only on routes where it is needed;
  • use defer or an interaction-based loader when its behavior permits;
  • reserve space for embeds before they initialize;
  • verify consent behavior without loading the vendor first;
  • measure main-thread work and network cost after enabling it.

Treat transitions as a product decision

Astro's earlier <ViewTransitions /> component was replaced by <ClientRouter /> in Astro 5. Current view transitions documentation distinguishes browser-native cross-document transitions from Astro's enhanced client-side router.

Native transitions can preserve normal document navigation without adding a client router. <ClientRouter /> adds navigation behavior and lifecycle considerations: scripts may need to reinitialize after page swaps, and persistent state can change how a page behaves. Add it because the experience needs it, then profile navigation and test keyboard, history, focus, and reduced-motion behavior.

Use a release checklist

Before shipping an Astro performance change, verify:

  • the production route uses the intended static or on-demand mode;
  • only necessary islands hydrate, with the least eager suitable directive;
  • the LCP resource is discoverable in the initial HTML and not lazy-loaded;
  • responsive images have correct dimensions, sizes, and useful alternative text;
  • third-party scripts are scoped and scheduled intentionally;
  • caches have an explicit freshness and invalidation policy;
  • Lighthouse is compared across repeated, equivalent runs;
  • field Core Web Vitals are monitored separately from the lab score.

You can review published performance measurements on IndieTools Speed. For the scoring model and its limitations, continue with How to Get a 100 PageSpeed Score and What Are Core Web Vitals?.

More guide articles

How to Analyze Technology Adoption in an Indie Product Directory — IndieTools guide
IndieTools

How to Analyze Technology Adoption in an Indie Product Directory

Analyze technology adoption in a product directory by defining what the records can actually represent. A founder-reported catalog can describe its own disclosed stack patterns, but it is not automatically a representative survey of all startups or all software in production.

Payment Provider Tech Stacks: Comparing What Indie Founders Disclose — IndieTools guide
IndieTools

Payment Provider Tech Stacks: Comparing What Indie Founders Disclose

A payment-provider label is the beginning of billing research, not a complete account of how a SaaS sells, provisions and supports subscriptions. Compare the commercial model, checkout experience and lifecycle integration separately. Verify current provider eligibility and terms before making a decision.

OpenAI-Powered SaaS Products: Compare the Workflow, Not the Badge — IndieTools guide
IndieTools

OpenAI-Powered SaaS Products: Compare the Workflow, Not the Badge

Compare OpenAI-powered SaaS products by the task they help a user complete, the evidence behind their outputs and the controls around failure. A model-provider label tells you something about a dependency, not whether the finished product fits your workflow.