Skip to content

How to fix Largest Contentful Paint (LCP) in WordPress: find the slow part, then fix it

LCP is the time until the largest image or block of text in the first screen is drawn. It is slow because the server answered late, the browser found the image late, the image downloaded slowly, or drawing was held up. Find which part it is, then fix that one.

By
WP Ministry
Published

In short

  • A good LCP is 2.5 seconds or less for at least 75% of visits, judged separately on mobile and desktop.
  • Every LCP time has four parts. PageSpeed Insights lists the time for each under "LCP breakdown". Start with the largest.
  • View the page's source and find the main image. It should be a real image tag, with no loading="lazy" and with fetchpriority="high".
  • WordPress guesses the main image from the order of images in the page. Where the guess is wrong, set the attributes in the template or with a filter.
  • A CSS background or a slider image is not in the HTML. Use a real image there, or preload the file with a high fetch priority.
  • Search Console reports 28 days of real visits. Check a fix with a lab test today, and expect the report to follow over four weeks.

Largest Contentful Paint (LCP) is the time from the start of a page load until the largest image or block of text in the first screen is drawn. On a WordPress site that is usually the hero image, a featured image or the main heading. It is slow for one of four reasons: the server answered late, the browser found the image late, the image took long to download, or something held up drawing it.

Find which of the four it is before changing anything. A fix aimed at the wrong part leaves the figure where it was. The other two Core Web Vitals, and how the three are judged together, are in how to improve WordPress Core Web Vitals.

The four parts of an LCP time

web.dev, Google's documentation for the metric, splits every LCP time into four parts. They follow one another with no gap and no overlap, and they add up to the whole figure.

PartWhat it coversOn a well-tuned page
Time to First Byte (TTFB)From the start of the visit until the first byte of the page's HTML arrivesAbout 40%
Resource load delayFrom then until the browser starts to fetch the LCP imageUnder 10%
Resource load durationThe download of the image itselfAbout 40%
Element render delayFrom the end of the download until the element is drawnUnder 10%

The shares are web.dev's guide, not a rule. The point is that the two parts with "delay" in the name should be as near zero as you can get them. Nearly all of the time should go on fetching the page and fetching the image.

When the LCP element is a heading or a paragraph in a font the device already has, there is no file to fetch, and the two middle parts are zero.

Shortening one part can simply move the time into another. web.dev's example is a page that hides the hero until a script has loaded: a smaller image downloads sooner, waits longer to be shown, and the LCP does not change.

Find your LCP element and which part is slow

  1. Step 1: Choose the page from field data

    In Search Console, open the Core Web Vitals report and then the mobile report. LCP problems are listed in the table "Why URLs aren't considered good". Open the row. It lists example URLs, each standing for a group of similar pages, with a "Group LCP" figure: three visits in four were that fast or faster over the last 28 days.

    Pick an example that people land on, such as the home page, a landing page or a popular post. With no Search Console, start at the next step with those pages.

  2. Step 2: Run the page through PageSpeed Insights, on mobile

    At pagespeed.web.dev, the top section, "Discover what your real users are experiencing", is field data. Check whether it is showing "This URL" or "Origin": a page with too few visits is given the whole site's figures. The lower section, "Diagnose performance issues", is one lab load.

    In the lower section, open the insight named "LCP breakdown". It shows the element that was measured and a table of "Time to first byte", "Resource load delay", "Resource load duration" and "Element render delay". The largest row is where to start.

    When the element is an image, the insight "LCP request discovery" checks three things about it. Two are worded "fetchpriority=high should be applied" and "Request is discoverable in initial document". The third is that the image is not lazy-loaded.

    Older guides point to an audit named "Largest Contentful Paint element". Lighthouse 13 replaced it with these two insights, and PageSpeed Insights has run Lighthouse 13 since October 20, 2025.

  3. Step 3: Check it in Chrome DevTools

    Open the page in Chrome, open DevTools and go to the Performance panel. It shows the page's LCP on your own computer at once and names the LCP element. Hover over the figure for the four parts, or click "Record and reload" and open the Insights tab for the same two insights.

    Your computer is faster than a visitor's phone. Switch on field data in the panel and it compares your figure with your visitors', and suggests CPU and network throttling to match them.

  4. Step 4: Read the page's source

    View the source of the page and find the image the reports named. Use the source, not the Elements panel in DevTools. The panel shows the page after scripts have changed it, and the browser's early scan for files sees only the HTML the server sent. What to look for is under "The browser found the image late" below.

Search Console reports field data only, and by group, so one page's figure in PageSpeed Insights may not match its group's. A lab test names the same element every time. Real visitors have screens of different sizes, and for some of them a different element is the largest. When the two disagree, go by the field data.

The server answered late

Nothing can be drawn before the first byte of HTML arrives, so a slow answer delays every later part. web.dev counts a TTFB of 0.8 seconds or less as good, and says a high one can make a 2.5 second LCP hard or impossible.

In PageSpeed Insights, the insight "Document request latency" checks whether the request was redirected, whether the server took more than 600 milliseconds to respond, and whether the response was compressed. web.dev names two causes that are easy to miss on an otherwise fast site: a chain of redirects from an ad or a shortened link, and tracking parameters on the address, which can stop a cached copy from being used.

On WordPress the first fix is a page cache, then the server behind it. How to reduce WordPress server response time shows how to measure it and what to change, in order. WordPress caching plugins compared covers the plugins.

To confirm: the "Time to first byte" row falls.

The browser found the image late

web.dev's rule of thumb is that the LCP image should start loading at the same time as the first file the page loads. Two things decide that: when the browser discovers the image, and what priority it gives it.

Tell from the HTML

In the page's source, the main image is in one of four states.

html
<!-- 1. Found at once and fetched first. This is the aim. -->
<img src="https://example.com/wp-content/uploads/2026/10/storefront-hero.jpg" alt="The storefront on Main Street" width="1600" height="900" fetchpriority="high">

<!-- 2. Found at once, then held back until the layout is known. -->
<img src="https://example.com/wp-content/uploads/2026/10/storefront-hero.jpg" alt="The storefront on Main Street" width="1600" height="900" loading="lazy">

<!-- 3. Hidden from the browser until a lazy-loading script runs. -->
<img data-src="https://example.com/wp-content/uploads/2026/10/storefront-hero.jpg" alt="The storefront on Main Street" class="lazyload">

The fourth state is no image tag at all. The picture is a CSS background, or a script adds it to the page. The browser cannot start on it until the stylesheet or the script has loaded.

web.dev says never to lazy-load the LCP image, because it always adds load delay. An image with no fetchpriority is found at once but does not start at a high priority. Chrome raises it only once the layout shows the image is on screen.

What WordPress does by default

Since 5.5, WordPress adds loading="lazy" to images that have a width and a height. It leaves the attribute off the first image in the content since 5.9, and off the first three since 6.3. Since 6.3 it also adds fetchpriority="high" to the first image it judges to be in view that covers at least 50,000 square pixels, width times height. It keeps a loading or fetchpriority attribute that is already on a tag.

WordPress cannot see the layout. It counts images in the order they come, so the guess fails in predictable ways:

  • Small images come first. Three logos or icons before the hero use up the three, and the hero gets loading="lazy".
  • A large image comes before the hero. It takes the one fetchpriority="high".
  • A theme, page builder or plugin writes the tag itself with loading="lazy" on it. WordPress keeps what it finds.
  • The tag has no width or height. It gets neither attribute.

How to lazy-load images and videos in WordPress covers the rest, including how to raise the count of three and how to exempt an image in a plugin's settings. The fixes below are for the main image.

Set the attributes where the image is printed

Where a classic theme's template prints the featured image, pass the two attributes. the_post_thumbnail() and wp_get_attachment_image() take them the same way. Copy the template into the child theme and change the call there.

php
the_post_thumbnail(
	'large',
	array(
		'loading'       => 'eager',
		'fetchpriority' => 'high',
	)
);

Do this only in a template where that image is the first large thing on screen, such as a single post. In a list of posts it would mark every thumbnail as the most important file on the page, and web.dev notes that a high priority on more than one or two images stops helping.

Correct WordPress's guess with a filter

Where you cannot change the call, the wp_get_loading_optimization_attributes filter, added in 6.4, is given the attributes WordPress chose for each image and lets you replace them. This one matches the hero by part of its file name, so it also matches the smaller sizes WordPress made from it.

functions.php
function example_mark_lcp_image( $loading_attrs, $tag_name, $attr, $context ) {
	if ( 'img' !== $tag_name || empty( $attr['src'] ) ) {
		return $loading_attrs;
	}
	if ( isset( $attr['loading'] ) && 'lazy' === $attr['loading'] ) {
		return $loading_attrs;
	}
	if ( false !== strpos( $attr['src'], 'storefront-hero' ) ) {
		unset( $loading_attrs['loading'] );
		$loading_attrs['fetchpriority'] = 'high';
	}
	return $loading_attrs;
}
add_filter( 'wp_get_loading_optimization_attributes', 'example_mark_lcp_image', 10, 4 );

The filter works on images WordPress prints or processes: post content, featured images and images from wp_get_attachment_image().

The second check leaves alone a tag that already has loading="lazy" written into it. WordPress does not take that attribute off, and a high priority beside it would give the browser two opposite instructions. For such a tag, change the setting in the theme, builder or plugin that wrote it.

Preload an image that is not in the HTML

For a CSS background or a slider's first image, the better fix is to show a real image tag there, if the theme or builder allows it. If it does not, tell the browser about the file in the head of the page. The wp_preload_resources filter has done this since 6.1 and has accepted fetchpriority since 6.6.

functions.php
function example_preload_hero_background( $preload_resources ) {
	if ( is_front_page() ) {
		$preload_resources[] = array(
			'href'          => 'https://example.com/wp-content/uploads/2026/10/hero.webp',
			'as'            => 'image',
			'type'          => 'image/webp',
			'fetchpriority' => 'high',
		);
	}
	return $preload_resources;
}
add_filter( 'wp_preload_resources', 'example_preload_hero_background' );

WordPress then prints this near the top of the head, on the front page only:

html
<link rel='preload' href='https://example.com/wp-content/uploads/2026/10/hero.webp' as='image' type='image/webp' fetchpriority='high' />

Give the exact address the page uses, or the file is downloaded for nothing. Keep fetchpriority, because a preloaded image without it is still fetched at a low priority. Preload one image, and only on the pages that show it. If phones get a different picture, the filter also takes media, imagesrcset and imagesizes.

An image tag that is already in the HTML needs no preload. fetchpriority="high" on the tag is enough.

To confirm: clear the page cache, view the source again, and run the lab test again. The "Resource load delay" row should be close to zero and "LCP request discovery" should pass.

The image took long to download

web.dev notes that for most sites this part is not the main bottleneck, so deal with the two delays first. When "Resource load duration" is the largest row, the insight "Improve image delivery" lists the images whose downloads are larger than they need to be.

  • The file is larger than the space it fills. WordPress makes several sizes of each upload and, since 4.4, lists them in srcset so the browser can choose. Its default sizes attribute assumes the image is as wide as the screen, up to the image's own width. In a narrow column the browser can pick a larger file than it needs, and the theme should correct that with the wp_calculate_image_sizes filter. A CSS background or a hand-written tag with no srcset sends one file to every screen.
  • The format is old. AVIF and WebP compress better than JPEG and PNG.
  • The file comes from far away. A CDN serves it from a server near the visitor. web.dev adds that an image on a different hostname costs a new connection, so serving it under your own domain is better where the CDN allows it.
  • Other files compete with it. In a slider, web.dev suggests fetchpriority="low" on the slides that are not shown at the start.

How to optimize WordPress images covers sizes and formats, and how to set up a CDN for WordPress covers the two kinds of CDN.

To confirm: the "Resource load duration" row falls, and the image leaves the "Improve image delivery" list.

Something delayed drawing it

The file has arrived, or there was no file, and the element still is not on screen. web.dev lists the causes.

  • Stylesheets and scripts in the head are still loading. Nothing is drawn until they are done. The insight "Render-blocking requests" lists them, and how to defer render-blocking resources in WordPress shows what is safe to change.
  • The LCP element is text in a web font. Text does not count as drawn while the browser is holding it back for the font. A font-display value of swap or optional in the font's CSS keeps the text visible in a fallback font while the web font loads. The insight "Font display" flags fonts without one.
  • A script adds or reveals the hero. A slider that builds its slides in the browser, an entrance animation that starts with the element invisible, or an A/B testing script that hides the page while it decides, all make the element wait for JavaScript. Put the element in the HTML, visible from the start.
  • The browser is busy. Images are drawn on the same thread that runs JavaScript, so a long script can hold a finished download back. Fewer and lighter scripts is the fix. How to speed up WordPress shows how to find the heavy ones.

To confirm: the "Element render delay" row falls.

Page builders, sliders and hero videos

These are the usual culprits for one reason: they do the things described above, often several at once.

Page builders may set a section's picture as a CSS background, which the browser finds late, write their own image tags, and run their own lazy loading. Check the builder's performance settings before adding code. Elementor's documentation, for one, lists a setting named "Optimized Image Loading" that applies fetchpriority="high" to LCP images and loading="lazy" to images below the fold, and one named "Lazy Load Background Images" that lazy-loads every background image except the first.

Optimization plugins can do the same work. WP Rocket's documentation describes a feature named "Optimize critical images" that detects the LCP image, preloads it, sets fetchpriority="high" and keeps it out of the plugin's lazy loading. Whatever does the work, the test is the same: read the source.

Sliders that build their slides with a script keep the first image out of the HTML, and nothing shows until the script has run. The other slides' images then compete with the first for the connection. A single still image in the HTML avoids all of it. If the slider stays, preload its first image and give the other slides a low priority.

Hero videos count too. For a <video> element, LCP is the moment the poster image or the first frame appears, whichever comes first. Give the video a poster, and preload the poster with a high priority if it is the LCP element. A video embedded from another site sits in an iframe. Chrome's field data counts what is inside the iframe, although the page's own scripts cannot measure it.

What you see, the likely cause and the fix

What the report showsLikely causeFix
"Time to first byte" is the largest rowNo page cache, a slow server, or redirectsA page cache, then the server response guide
"Resource load delay" is large and the image has loading="lazy"WordPress's count, or a theme, builder or plugin, lazy-loaded the heroTake the attribute off in the template, with the filter or in the plugin
"Request is discoverable in initial document" failsA CSS background, a slider, or a script that swaps data-srcA real image tag, or a preload
"fetchpriority=high should be applied"The image is in the HTML at the default priorityAdd fetchpriority="high" to that one image
"Resource load duration" is the largest rowThe file is too large, in an old format, or far awayA smaller file, AVIF or WebP, correct sizes, a CDN
"Element render delay" is large and the element is an imageBlocking CSS or scripts, or a script that reveals the heroDefer what blocks, and put the hero in the HTML
"Element render delay" is large and the element is textA web font holding the text backfont-display: swap or optional
The lab test is good and Search Console still lists the issueThe report covers 28 days and groups pagesWait, and watch the field data

What a good result is, and when the report changes

A good LCP is 2.5 seconds or less. Over 4 seconds is poor. The figure that is judged is the 75th percentile, so at least three visits in four must be at 2.5 seconds or less, and mobile and desktop are judged separately.

Run the lab test straight after a change. Its figures vary from run to run, so run it a few times and compare the parts, not only the total.

Field data follows slowly. The figures in PageSpeed Insights cover the previous 28 days and are updated daily, so a fix shows as a gradual slide over four weeks. In Search Console, "Start Tracking" on the issue's page starts a 28-day check of the affected pages.

If you would rather hand the job over, our speed optimization service measures the pages you name before the work and again after it. It does not promise a particular score.

Common questions

PageSpeed Insights says my LCP is good, but Search Console still reports an LCP issue. Why?

They are different data. The lab figure in PageSpeed Insights is one load on one simulated phone. Search Console reports what real visitors got over the last 28 days, for a group of similar pages. After a fix, the report takes about four weeks to catch up.

My LCP element is a heading, not an image. What do I fix?

A heading has no image to download, so nearly all of the time is in the server response and the render delay. Look at the server response first, then at stylesheets and scripts that block drawing. If the heading uses a web font, check that the font's CSS sets font-display to swap or optional.

Should I preload my hero image?

Only when the browser cannot find it in the HTML, as with a CSS background or an image a script adds. Put fetchpriority="high" on the preload. An ordinary image tag is found at once, and needs only fetchpriority="high" on the tag.

Why is the LCP element different on mobile and desktop?

The largest thing in the first screen depends on the size of the screen. On a wide screen it may be the hero image. On a phone the image may be smaller or further down, and a heading takes its place. Test both, and fix the one that fails in field data.

More on this subject

Speed optimization, done for you

Speed optimization is $149. Before-and-after measurements on named pages. It starts with a free diagnosis.