Fix LCP in Astro
Largest Contentful Paint (LCP) is a critical Core Web Vital that measures how quickly the main content of your page becomes visible. In Astro applications, common issues include unoptimized images, render-blocking resources, and inefficient data loading patterns that delay the largest visible element.
This guide walks through five targeted fixes for LCP in Astro, with real code examples and before/after performance comparisons. Each step addresses a specific bottleneck in the Astro rendering pipeline.
Expected results
Following all five steps typically produces these improvements:
Before
3.1s
LCP score (Needs Improvement) -- unoptimized images, render-blocking scripts, no preloading in Astro project
After
0.9s
LCP score (Good) -- optimized with astro:assets, preloading, zero-JS islands, and edge deployment
Step-by-step fix
Use astro:assets for automatic image optimization
Astro's built-in <Image /> component from astro:assets automatically converts images to WebP/AVIF, generates responsive sizes, and adds width/height attributes to prevent layout shift. For the LCP image, combine it with eager loading and fetchpriority.
<img> tags for above-the-fold images. The Astro Image component handles format conversion, resizing, and optimization at build time, producing smaller files than any runtime solution.
---
// src/pages/index.astro
---

---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
Preload critical resources in the head
Astro renders pages to static HTML by default, but the browser still needs hints about which resources to fetch first. Add <link rel="preload"> directives in your layout's <head> for fonts, hero images, and critical CSS. This tells the browser to start downloading these resources immediately rather than waiting to discover them during parsing.
---
// src/layouts/Base.astro
const { title, heroImage } = Astro.props;
---
{title}
{heroImage && (
)}
Keep client-side JavaScript at zero for LCP content
Astro's islands architecture ships zero JavaScript by default. Only components with client:* directives send JS to the browser. For the LCP content area (hero section, above-the-fold text and images), never add client directives. Interactive elements below the fold should use client:visible to defer hydration until the user scrolls to them.
---
import Hero from '../components/Hero.astro'; // Static -- zero JS
import SearchBar from '../components/SearchBar'; // React component
import Comments from '../components/Comments'; // React component
---
Enable content collections with static generation
Astro generates static HTML at build time by default, which means near-zero TTFB when deployed to a CDN. Use content collections with getStaticPaths() to pre-render all pages. Avoid using server output mode unless you specifically need server-side rendering -- it adds server processing time that directly increases LCP.
import { defineConfig } from 'astro/config';
export default defineConfig({
// 'static' is the default -- pre-renders all pages
output: 'static',
// Enable image optimization
image: {
service: { entrypoint: 'astro/assets/services/sharp' },
},
// Build optimizations
build: {
inlineStylesheets: 'auto', // Inline small CSS
},
// Vercel adapter for edge deployment
// adapter: vercel({ edgeMiddleware: true }),
});
Optimize font loading with font-display and subsetting
Self-host your fonts and use font-display: swap to show fallback text immediately while custom fonts load. Subset fonts to include only the characters you need. In Astro, define font-face rules in a global CSS file and preload the WOFF2 files in your layout's head.
/* src/styles/global.css */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var-latin.woff2') format('woff2');
font-weight: 100 900;
font-display: swap; /* Show fallback immediately */
unicode-range: U+0000-00FF; /* Latin subset only */
}
/* Size-adjust fallback to minimize layout shift */
@font-face {
font-family: 'Inter Fallback';
src: local('Arial');
ascent-override: 90.49%;
descent-override: 22.56%;
line-gap-override: 0%;
size-adjust: 107.64%;
}
body {
font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
}
Quick checklist
-
Hero image uses
<Image />fromastro:assetswithloading="eager" -
Critical images have
fetchpriority="high" -
Fonts preloaded in layout head with
font-display: swap -
No
client:*directives on above-the-fold components -
Site uses
output: 'static'mode (default) -
CSS inlined automatically via
build.inlineStylesheets - Deployed to CDN edge (Vercel, Netlify, Cloudflare Pages)
Frequently asked questions
Astro sites routinely achieve LCP under 1.2 seconds because they ship zero JavaScript by default and generate static HTML at build time. With optimized images via astro:assets and edge deployment, sub-second LCP is achievable. If your Astro site has LCP above 2 seconds, check for unoptimized images, render-blocking scripts added via client directives, or slow server response from non-edge hosting.
Yes, significantly. Because Astro only sends JavaScript for components explicitly marked with client:* directives, the LCP content area is pure HTML and CSS with no hydration delay. This is fundamentally different from React-based frameworks where the entire page hydrates. The key is ensuring your above-the-fold content uses Astro components (not framework components with client directives).
Use SSG (static) mode for best LCP. Static pages serve directly from CDN edge nodes with near-zero TTFB. Only switch to SSR mode if you need per-request personalization or real-time data. For content that updates periodically, use hybrid mode where most pages are static and only specific routes use server rendering.
Astro's View Transitions API enables smooth page-to-page animations without a full-page reload. For the initial page load, view transitions have minimal LCP impact since they primarily affect subsequent navigations. However, ensure the transition:animate directive on your LCP element does not add unnecessary delay. Use transition:animate='none' on hero images to avoid animation overhead.
In most benchmarks, Astro achieves better LCP scores than Next.js because it ships zero client-side JavaScript by default. CrUX data shows Astro sites have an 82% CWV pass rate versus 68% for Next.js. However, Astro is best suited for content-heavy sites, while Next.js offers more flexibility for highly interactive applications that need client-side state management.
Google rates LCP as 'good' when it is under 2.5 seconds at the 75th percentile. For Astro applications specifically, aim for under 2.0 seconds. Measure with field data from Chrome User Experience Report (CrUX) through PageSpeed Insights, as lab tests may not reflect real-user experience with third-party scripts and varying network conditions.
Continue learning
Fix CLS in Astro
Related performance optimization for the same framework.
GuideComplete LCP Guide
Deep dive into LCP -- thresholds, measurement, and optimization strategies.
DataCWV Data: April 2026
Astro leads all frameworks with 82% CWV pass rate.
ToolCWV Score Explainer
Enter your scores for personalized fix recommendations.