If you’re asking how to improve LCP (Largest Contentful Paint), the answer is to find the element that counts as your LCP, then split its load time into four sub-parts: time to first byte, resource load delay, resource load duration and element render delay. LCP is good at 2.5 seconds or less for 75% of page visits. Whichever sub-part is too big decides your fix, so you stop guessing.
Most slow LCP scores I see come from two things. Either the browser discovers the main image far too late because it’s hidden in CSS or JavaScript, or the server takes so long to send the first byte of HTML that everything after it starts late. Both are fixable. No redesign needed.
This is the order I use when a client’s LCP sits in orange or red. Twelve fixes, grouped by the sub-part they shrink.
What Is LCP and What Score Do You Need?
LCP measures how long it takes for the largest image or text block in the viewport to render. Per web.dev’s LCP documentation, the element can be an <img>, an <image> inside an SVG, a video’s poster or first frame, an element with a CSS background image, or a block of text.
The thresholds are 2.5 seconds or less for good and more than 4 seconds for poor, with “needs improvement” in between. Google measures them at the 75th percentile of page loads, split into mobile and desktop. So one fast visit on your office Wi-Fi tells you almost nothing. I’ve heard plenty of owners say their site feels instant, right before PageSpeed Insights shows their real mobile visitors waiting well past the 4-second line for the main image.
A few details catch people out. The browser stops reporting new LCP candidates once the user taps, scrolls or presses a key. It also ignores elements with zero opacity, elements that cover the whole viewport, and low-entropy placeholder images. If your hero is a blurry placeholder that fades into the real photo, the real photo is usually the one that counts.
For how LCP sits next to INP, CLS and your Lighthouse score, I’ve written a separate piece on page speed vs Core Web Vitals. This guide stays on LCP only.
The Four Sub-Parts of Your LCP Time
Every LCP has the same four pieces, in order. Web.dev’s Optimize LCP guide names them and suggests a rough budget for each.
| Sub-part | What it measures | Rough target share of LCP |
|---|---|---|
| Time to first byte (TTFB) | User starts loading until the first byte of HTML arrives | About 40% |
| Resource load delay | TTFB until the browser starts fetching the LCP resource | Under 10% |
| Resource load duration | How long the LCP resource itself takes to download | About 40% |
| Element render delay | Resource finished until the element is fully rendered | Under 10% |
Read that table twice. The two “delay” rows should be tiny. When I open a trace and see resource load delay eating a second, I already know the fix is about discovery, not about compressing the image harder.
If your LCP element is text, there’s no resource to fetch. Your time is just TTFB plus render delay, which usually points at web fonts or render-blocking CSS.
How Do You Find Your LCP Element?
You need field data to confirm a real problem and lab data to see the cause. I always start with the field.
PageSpeed Insights. The top section shows LCP from real Chrome users over the last 28 days, for the URL or the whole origin. The lab section underneath names the LCP element and shows its breakdown into the four sub-parts. If the field number fails but the lab looks fine, read my guide on a Core Web Vitals assessment that failed before you change anything.
Search Console. The Core Web Vitals report groups similar URLs, so you can tell whether the problem lives in one template, like all product pages, or across the site.
Chrome DevTools. Open the Performance panel and record a page load. Chrome’s documentation describes an “LCP breakdown” insight with the same four sub-parts, plus an “LCP request discovery” insight. That second one checks three things: whether the image is discoverable in the initial HTML, whether fetchpriority="high" is applied, and whether loading="lazy" is wrongly set.
Turn on CPU and network throttling first. Your laptop isn’t your customer’s three-year-old phone. Big difference.
Log LCP Attribution From Real Visitors
When the lab can’t reproduce the problem, the web-vitals library can report it from real users. Its attribution build splits LCP into the four sub-parts and names the element, according to the web-vitals README:
import {onLCP} from 'web-vitals/attribution';
onLCP(({name, value, attribution}) => {
const {
target,
url,
timeToFirstByte,
resourceLoadDelay,
resourceLoadDuration,
elementRenderDelay,
} = attribution;
// Replace console.log with your analytics call.
console.log(name, value, {
target,
url,
timeToFirstByte,
resourceLoadDelay,
resourceLoadDuration,
elementRenderDelay,
});
});
Install it with npm install web-vitals. target is a selector for the LCP element and url is the image file, when there is one. After a week of data you’ll know which template and which sub-part to attack.
Which Sub-Part Should You Fix First?
Pick the one furthest over its budget, not the one that’s easiest. Here’s the condition map I use:
- If TTFB is over about 40% of LCP, the problem starts before the browser sees any HTML. Go to fixes 1 to 3.
- If resource load delay is big, the browser found the LCP resource late. Fixes 4 to 7.
- If resource load duration is big, the file is too heavy or too far away. Fixes 8 and 9.
- If element render delay is big, the file arrived but something blocked the paint. Fixes 10 to 12.
Honestly, number 2 is the most common on the sites I audit. It’s also the cheapest to fix, because moving an image out of a CSS background or deleting one loading attribute takes minutes, while cutting server time can mean a hosting migration.
How to Improve LCP When TTFB Is the Problem
Web.dev counts a TTFB of 0.8 seconds or less as good, though it isn’t a Core Web Vital itself. A slow first byte pushes every other sub-part later.
Fix 1: Cache Full HTML Pages
On WordPress and similar CMSs, every uncached request builds the page from scratch, running PHP, querying the database and assembling the theme before a single byte leaves the server. A page cache serves stored HTML instead. Many hosts include one, and in my experience it’s the single biggest TTFB win on small sites.
Fix 2: Put a CDN in Front of the HTML
A CDN serves pages from a location near the visitor. Web.dev also warns against unique URL parameters, like tracking strings added per visit, because they stop the CDN from serving a cached copy.
Fix 3: Remove Redirect Chains
Every redirect adds a full round trip before the real HTML arrives. Ouch. Links to http:// versions, missing trailing slashes and old campaign URLs are the usual suspects. Update internal links and ads to point straight at the final URL.
Fixes 4 to 7: Kill Resource Load Delay
The goal is simple. The browser should start downloading the LCP image the moment it reads the HTML.
Fix 4: Put the LCP Image in the HTML
Web.dev lists three patterns that hide the LCP image from the browser’s preload scanner: a CSS background image, an <img> added by JavaScript, and a lazy-load library that stores the real URL in data-src or data-srcset. A plain <img> with a real src in the server’s HTML fixes all three.
Fix 5: Add fetchpriority=”high”
Browsers don’t know an image is important until layout runs. The fetchpriority attribute tells them early:
<img src="/images/hero-1200.webp"
srcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w, /images/hero-1800.webp 1800w"
sizes="100vw"
width="1200" height="675"
alt="Technician fitting a new boiler in a small kitchen"
fetchpriority="high">
Web.dev’s Fetch Priority guide lists support in Chrome and Edge 102+, Firefox 132+ and Safari 17.2+. It’s a hint, so use it on one image, not ten. The same guide reports Google Flights cutting LCP from 2.6 to 1.9 seconds with it, which is Google’s own result, not a promise for your page.
Fix 6: Never Lazy-Load the LCP Image
Web.dev puts it bluntly: never lazy-load your LCP image. I still find it on WordPress audits far more often than I’d like, usually added by a speed plugin that lazy-loads everything on the page, hero included, because nobody told it which image matters most. loading="lazy" makes the browser wait for layout before it even asks for the file. Leave the hero without a loading attribute, so it loads eagerly by default, and lazy-load only images further down.
Fix 7: Preload What the Browser Can’t See
If the LCP really must be a CSS background or a font-driven text block, preload it. For a responsive hero, imagesrcset and imagesizes mirror the image’s srcset and sizes, per MDN:
<link rel="preload" as="image" fetchpriority="high"
href="/images/hero-1200.webp"
imagesrcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w, /images/hero-1800.webp 1800w"
imagesizes="100vw">
Chrome’s docs say to set fetchpriority="high" on both the preload and the image element itself. If the image lives on another domain, add a <link rel="preconnect"> to that origin too, since web.dev notes cross-origin images carry an extra connection cost.
Fixes 8 and 9: Shrink the Download
Once discovery is fast, resource load duration is pure file size and distance.
Fix 8: Serve the Right Format at the Right Size
WebP and AVIF are usually smaller than JPEG at similar quality, and srcset with sizes lets a phone fetch an 800-pixel file instead of a 2,400-pixel one. I go deeper on formats, compression and responsive markup in my image optimization for SEO guide, so I won’t repeat it here.
My quick check: open the LCP image URL in a new tab and look at the file size. A full-width hero that weighs several megabytes is a red flag on any site. Fix that first.
Fix 9: Use an Image CDN and Long Cache Headers
An image CDN resizes, compresses and serves files near the visitor. Web.dev also recommends an efficient Cache-Control policy, so repeat visitors load the hero from their cache. For text LCPs, set font-display to anything other than auto or block, so text can paint before the web font arrives.
Fixes 10 to 12: Remove Element Render Delay
The file is ready, yet nothing appears. Something is blocking the paint. In my experience it’s almost always one of three things: a heavy stylesheet, a synchronous script in the head, or a JavaScript framework that hasn’t built the element yet.
Fix 10: Trim Render-Blocking CSS
Stylesheets in the <head> block rendering until they load. Web.dev’s advice is to keep critical CSS smaller than the LCP resource itself, either by inlining it or by cutting unused rules, then defer the rest. Page builders that ship a 400 KB stylesheet to every page are a frequent cause on WordPress sites I review.
Fix 11: Make Head Scripts Async or Deferred
Web.dev says it’s almost never necessary to put synchronous scripts in the <head>. Add defer to scripts the first screen doesn’t need. Easy win.
<script src="/js/app.js" defer></script>
Watch A/B testing tools closely. Many hide the page until the test loads, and web.dev lists that hiding as a direct cause of render delay.
Fix 12: Render the Main Content on the Server
With client-side rendering, the LCP element doesn’t exist until JavaScript downloads, runs and fetches data. Server-side rendering or static generation puts the element and its image URL straight into the HTML. If you can’t switch, at least server-render the hero section and break up long JavaScript tasks.
How Does LCP Work on WordPress and Shopify?
Both platforms already help. Each has a trap.
Since WordPress 6.3, core automatically adds fetchpriority="high" to the image it judges most likely to be the LCP, according to the WordPress core team. It also skips lazy-loading for images it expects above the fold. The trap: sliders, page-builder background images and lazy-load plugins often bypass that logic. Check the rendered HTML, not the settings screen. View source, search for your hero file name, and look at which attributes actually sit on that tag after every plugin and theme filter has had its turn.
On Shopify, the image_tag filter lazy-loads images in sections further down the page unless you pass preload: true. Themes sometimes get the first section wrong. My Shopify speed optimization guide covers the store side in detail.
Edge Cases That Change the Plan
- If the LCP element differs between mobile and desktop, fix the mobile one first. Mobile usually fails first, and the hero is often a different crop or a text heading.
- If a carousel is the LCP, give the first slide
fetchpriority="high"and the restfetchpriority="low", the pattern web.dev shows. Better still, replace it with one image. - If the LCP is a video, the poster image or first frame counts. Treat the poster like a hero image.
- If LCP is only slow for first-time visitors, look at TTFB and cache misses, not image size.
LCP Decision Matrix
| If the biggest sub-part is | And you see | Then start with |
|---|---|---|
| TTFB | Slow uncached pages | Fix 1 (page cache), then Fix 2 (CDN) |
| TTFB | Redirects in the waterfall | Fix 3 |
| Resource load delay | CSS background or data-src hero | Fix 4, then Fix 7 |
| Resource load delay | loading="lazy" on the hero | Fix 6, then Fix 5 |
| Resource load duration | Multi-megabyte hero file | Fix 8 |
| Resource load duration | Image from a far or uncached origin | Fix 9 |
| Element render delay | Large CSS or sync head scripts | Fix 10 and Fix 11 |
| Element render delay | React or Vue app renders the hero | Fix 12 |
When Is LCP a Job for a Developer?
Fixes 3, 5, 6 and 11 are often a few lines of HTML. Server-side rendering, custom image pipelines and hosting changes are different. If your LCP lives inside a JavaScript framework or a theme you can’t edit, you need someone who reads performance traces.
That work is part of our technical SEO service. The other two metrics have their own guides: how to improve INP and how to fix CLS. Want to know which pages fail first? Ask for a free SEO audit.
Frequently Asked Questions
What Is a Good LCP Score?
A good LCP is 2.5 seconds or less at the 75th percentile of page loads. Above 4 seconds is poor, and anything in between needs improvement, according to web.dev. Mobile and desktop are scored separately.
Should I Preload My LCP Image?
Only if the browser can’t find it in the HTML, such as a CSS background. A normal <img> in the server HTML with fetchpriority="high" is usually enough. Preloading too many files makes them compete with each other.
Does Lazy Loading Hurt LCP?
Yes, when it’s applied to the LCP image. Lazy loading delays the request until layout is known, which adds resource load delay. Lazy-load images below the fold and load the main one eagerly.
How Long Until LCP Improvements Show in Search Console?
Search Console and PageSpeed Insights field data use a rolling 28-day window of Chrome user data. Expect gradual movement after you deploy, with the full effect after about four weeks.
Last updated: October 2026 by Mizanur Rahman



