LCP image lazy-loaded: when the hero image is told to wait

Lazy loading tells the browser not to fetch an image until it is near the viewport. That is right for images further down the page and wrong for the hero image, which is usually the Largest Contentful Paint element. Remove loading="lazy" from that one image and give it fetchpriority="high".

Google's threshold for a good LCP is 2.5 seconds or less. Core Web Vitals are a modest ranking input compared with relevance, but a slow hero image is among the cheapest LCP problems to fix.

What it looks like

Before

html
<img src="/wp-content/uploads/hero.jpg" width="1600" height="900" alt="Stoneware mugs on a shelf" loading="lazy">

After

html
<img src="/wp-content/uploads/hero.jpg" width="1600" height="900" alt="Stoneware mugs on a shelf" fetchpriority="high">

Why lazy loading hurts here

For an eager image, the browser's preload scanner finds the <img> while the HTML is still arriving and starts the download at once. For a lazy image, the browser must first lay out the page to know whether the image is near the viewport, and only then request it. On a slow connection that wait is significant. JavaScript-based lazy loaders are worse: the real URL sits in a data-src attribute, invisible to the preload scanner, until a script has downloaded, parsed and run.

Find your LCP element

  • PageSpeed Insights. Run the URL and open the diagnostics. The "Largest Contentful Paint element" entry shows the element. Lighthouse has reported this problem under the name "Largest Contentful Paint image was lazily loaded"; newer versions fold it into their LCP request discovery insight.
  • Chrome DevTools. In the Performance panel, record a page load; the LCP marker in the timings track identifies the element when you click it.
  • By inspection. View source, find the first large image and read its attributes.

Test on a mobile viewport as well as desktop, since the LCP element often differs between them.

The LCP element may not be an <img> at all. If it is a CSS background image, the loading attribute does not apply, and the image is discovered late for a different reason; see the last section.

What WordPress does by default

WordPress adds loading="lazy" to images automatically, with safeguards for the top of the page:

  • It leaves the attribute off the first few content images. The count is set by the wp_omit_loading_attr_threshold filter, which defaults to 3.
  • It adds fetchpriority="high" to the first sufficiently large image it encounters, where the area is at least 50,000 square pixels by default, and that image is never marked lazy.

So on a stock theme the hero is usually handled. The issue arises when something works outside that logic:

  • Hard-coded attributes. A theme template or page-builder widget prints loading="lazy" on every image itself.
  • A lazy-load plugin or optimisation feature that rewrites all images to data-src, including the first.
  • Images added by blocks or shortcodes that bypass core's image functions, so no fetchpriority is added and the builder's own default applies.
  • Counting that does not match the layout. The logo, an avatar and a small icon can use up the first three "content images" in markup, leaving the visually dominant hero as the fourth and lazy.
  • Sliders. The first slide is treated like the rest.

Fix it

In the block editor. WordPress decides automatically; if the hero is being marked lazy because several small images precede it, raise the threshold:

php
add_filter( 'wp_omit_loading_attr_threshold', function () {
    return 5;
} );

In a theme template. When you print the hero yourself, pass the attributes explicitly:

php
echo wp_get_attachment_image( $hero_id, 'full', false, array(
    'loading'       => 'eager',
    'fetchpriority' => 'high',
) );

In a lazy-load or performance plugin. Use its exclusion setting. Most offer "skip the first N images" or exclusion by CSS class or filename. Exclude the hero and the logo. If the plugin duplicates what WordPress already does natively, turning its image lazy loading off altogether is often the cleanest fix.

In a page builder. Look for a per-image "lazy load" toggle on the hero widget and switch it off, and check the builder's global performance settings for a first-image exclusion.

Use fetchpriority="high" on one image per page. Applied to many, it stops meaning anything.

Background images and other hidden heroes

A hero set with background-image in CSS is not found until the stylesheet has been downloaded and parsed. Two options:

  1. Replace it with a real <img> positioned behind the text with object-fit: cover. This is the better fix, since it also allows srcset and alt text.
  2. Keep the background and add a preload hint in the head:
html
<link rel="preload" as="image" href="/wp-content/uploads/hero.jpg" fetchpriority="high">

Preload exactly the file the page will use at that viewport, or the browser downloads two images.

Do not swing too far

Keep lazy loading on everything below the fold. Removing it site-wide makes pages heavier at load and competes with the hero for bandwidth, which is what no lazy loading describes. The aim is one prioritised image at the top and lazy images beneath it. Our guide to improving Core Web Vitals in WordPress covers the rest of the LCP chain, from server response to image size.

Measure the change

View source and confirm the hero has no loading="lazy" and does have fetchpriority="high". Rerun PageSpeed Insights: the lazily-loaded audit should pass, and in the LCP breakdown the "load delay" portion should shrink. Lab numbers change immediately. The field data in Search Console's Core Web Vitals report is a rolling 28-day window of real visits, so allow about a month for it to reflect the fix.

Common questions

Should every above-the-fold image be eager?

Images visible without scrolling should not be lazy. Only the single most important one needs fetchpriority="high".

Is loading="eager" necessary?

Eager is the default when the attribute is absent. Writing it explicitly is useful mainly to stop other code adding lazy.

Will fixing this improve rankings?

It improves a Core Web Vitals metric, which Google uses as one of many signals. Expect a better experience first and, at most, a small ranking effect.

The hero is the first slide of a slider. What should I do?

Mark the first slide's image eager with high priority and keep the remaining slides lazy. Better still, consider whether a single static hero would serve visitors as well.