How to Improve Your LCP Score: A Developer's Guide
Largest Contentful Paint (LCP) measures how long it takes for the biggest visible element to render. It is one of Google's three Core Web Vitals, and a slow LCP score hurts both user experience and search rankings. Here is how to fix it, with code.
What LCP actually measures
LCP tracks the render time of the largest image, video, or text block visible in the viewport during initial page load. For most pages, the LCP element is the hero image or the main heading. The browser keeps updating the LCP candidate until the user interacts with the page (scrolls, taps, or types). Whatever the largest element is at that point becomes the final LCP value.
| LCP Score | Rating | Impact |
|---|---|---|
| < 2.5s | Good | Passes Core Web Vitals |
| 2.5s to 4s | Needs improvement | May affect rankings in competitive results |
| > 4s | Poor | Definite ranking penalty, high bounce rate |
The three most common causes of slow LCP
1. Large, unoptimized images
A 2MB hero image on a landing page is the single most common LCP killer. If your LCP element is an image, optimizing it will likely cut your LCP by 1 to 2 seconds. Convert to WebP or AVIF, use responsive sizes, and add width/height attributes to prevent layout shift.
2. Render-blocking CSS and JavaScript
The browser cannot paint anything until it has downloaded and parsed all CSS in the head. Large CSS files, unused CSS frameworks, and synchronous JavaScript all delay the first render. Every millisecond of render-blocking time adds directly to your LCP.
3. Slow server response (TTFB)
If the server takes 1.5 seconds to respond, your LCP cannot be faster than 1.5 seconds plus download and render time. Common causes: no CDN, unoptimized database queries, server-side rendering without caching.
Fix 1: Optimize your hero image with next/image
If you use Next.js, the next/image component handles format conversion, responsive sizing, and lazy loading automatically. For above-the-fold images (your LCP element), add the priority prop to disable lazy loading and trigger a preload hint.
import Image from "next/image"
export function Hero() {
return (
<section className="relative h-[600px]">
<Image
src="/hero.webp"
alt="Dashboard showing SEO audit results"
fill
priority {/* preloads the image */}
sizes="100vw"
className="object-cover"
/>
<div className="relative z-10">
<h1>Fix your SEO in minutes</h1>
</div>
</section>
)
}The priority prop adds a <link rel="preload"> to the document head. This tells the browser to start downloading the image immediately, before it even parses the component tree.
Fix 2: Preload your fonts
If your LCP element is a text block (a heading, for example), the browser cannot render it until the font file downloads. Custom fonts fetched from Google Fonts or a CDN can add 200 to 500ms to LCP. Use next/font to self-host and preload fonts automatically.
// app/layout.tsx
import { DM_Sans } from "next/font/google"
const dmSans = DM_Sans({
subsets: ["latin"],
display: "swap", // shows fallback font immediately
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={dmSans.className}>
<body>{children}</body>
</html>
)
}The display: "swap" setting renders text immediately with a fallback font, then swaps to the custom font once loaded. This prevents invisible text (FOIT) during loading.
Fix 3: Eliminate render-blocking CSS
Every CSS file in the <head> blocks rendering until it is fully downloaded and parsed. Two strategies to reduce this:
- Remove unused CSS. If you use Tailwind, the purge step removes unused classes automatically at build time. No extra configuration needed in Tailwind v4.
- Inline critical CSS. Next.js does this automatically for CSS Modules and Tailwind. The styles needed for the first paint are inlined in the HTML response.
For third-party CSS (analytics widgets, chat widgets, cookie banners), load them asynchronously or defer them until after the initial paint. They should never block the LCP element.
Fix 4: Reduce server response time
A slow Time to First Byte (TTFB) is the floor for your LCP. If it takes 2 seconds to get the HTML, your LCP will be at least 2 seconds plus render time.
- Use static generation (SSG) where possible. Pre-rendered pages are served from a CDN edge node with near-zero TTFB. In Next.js, any page without dynamic data is statically generated by default.
- Use ISR for pages with data. Incremental Static Regeneration serves a cached page instantly and revalidates in the background.
- Deploy close to users. Vercel, Cloudflare Pages, and Netlify all serve from edge locations globally.
- Cache database queries. If your server-rendered page queries a database, cache the result for at least a few seconds.
Quick wins checklist
- Add
priorityto your above-fold image. - Convert images to WebP or AVIF.
- Use
next/fontinstead of a Google Fonts stylesheet link. - Remove unused CSS frameworks or components.
- Defer third-party scripts (analytics, chat widgets).
- Switch server-rendered pages to static generation where data allows.
- Use a CDN. Do not serve from a single-region origin server.
FAQ
What is a good LCP score?
Google considers LCP under 2.5 seconds as good, between 2.5 and 4 seconds as needs improvement, and over 4 seconds as poor. These thresholds apply to the 75th percentile of page loads, meaning at least 75% of your visitors should experience LCP under 2.5 seconds.
What is the most common cause of slow LCP?
Large, unoptimized hero images are the number one cause. An uncompressed 2MB PNG above the fold will dominate your LCP regardless of how fast everything else is. Converting to WebP or AVIF and using responsive sizing typically cuts LCP by 1-2 seconds.
Does LCP affect SEO rankings?
Yes. LCP is one of the three Core Web Vitals that Google uses as a ranking signal. Pages with poor LCP scores may rank lower than equivalent pages with good scores, especially in competitive search results.
Can SEOLint measure my LCP score?
Yes. SEOLint runs every scan through the Google PageSpeed Insights API, which returns lab LCP data for both mobile and desktop. Issues are flagged with severity levels and include specific fix prompts you can paste into Claude or Cursor.
Find your LCP bottleneck in one scan
SEOLint runs PageSpeed Insights on every scan and shows you exactly what to fix.