hero-lcp-wtcMost homepages open with a big image. It looks great in the design file, and then it shows up last on a real phone. Lighthouse calls that moment Largest Contentful Paint, and on a page with a hero, the hero is almost always the element being measured.
The fix isn’t a smaller image or no image. It’s telling the browser, as early as possible, that this one file matters more than everything else.
Find your LCP element
Before changing anything, confirm which element is being measured. Open Chrome DevTools, run a Lighthouse report on mobile, and find the LCP diagnostic, which names the element.
- Open the page in an incognito window so browser extensions don’t skew the result.
- In DevTools, open the Lighthouse tab and choose Mobile.
- Expand the LCP diagnostic and note the element it names.
Load it first
Give the image explicit dimensions and a high fetch priority, and never lazy-load it. The loading="lazy" attribute tells the browser to wait, which is the opposite of what your LCP element needs.
Let wp_get_attachment_image() write the tag. It adds the width, height, srcset and sizes for you, and you pass the rest.
<?php
echo wp_get_attachment_image( $hero['ID'], 'full', false, array(
'fetchpriority' => 'high',
'loading' => 'eager',
'decoding' => 'auto',
'sizes' => '100vw',
'alt' => '',
) );
?>
That prints a tag like this, with the file names from your own uploads:
<img width="1920" height="840"
src="https://example.com/wp-content/uploads/2026/11/hero.webp"
class="attachment-full size-full"
alt=""
fetchpriority="high"
loading="eager"
decoding="auto"
srcset="https://example.com/wp-content/uploads/2026/11/hero.webp 1920w,
https://example.com/wp-content/uploads/2026/11/hero-300x131.webp 300w,
https://example.com/wp-content/uploads/2026/11/hero-768x336.webp 768w,
https://example.com/wp-content/uploads/2026/11/hero-1024x448.webp 1024w,
https://example.com/wp-content/uploads/2026/11/hero-1536x672.webp 1536w"
sizes="100vw" />
Two of those arguments override WordPress defaults. Without loading set to eager, WordPress may add loading="lazy", depending on where the image sits on the page. Without decoding set to auto, it adds decoding="async", which can let the browser paint the hero a beat later.
If the image is set in CSS or comes from a slider, the browser can’t discover it early. Preload it from wp_head instead. Using a looping video instead? Hero Video LCP covers the poster and the video.
add_action( 'wp_head', function () {
if ( ! is_front_page() ) {
return;
}
// Preload the hero so it starts downloading first.
$src = get_theme_file_uri( 'assets/img/hero.webp' );
echo '<link rel="preload" as="image" href="' . esc_url( $src ) . '" fetchpriority="high">';
}, 1 );
Serve the right size
A 1920-pixel image on a 390-pixel phone screen wastes most of the download. srcset lists the sizes WordPress generated, and sizes tells the browser how wide the image displays, so it can pick the smallest file that still looks sharp. Set sizes to match your layout: 100vw for a full-width hero, something narrower for a two-column one.
Since WordPress 6.3, wp_get_attachment_image() also adds fetchpriority="high" to the image it thinks is the LCP element. Setting it yourself is still safer, because WordPress is guessing and you know.
Which attributes do what
| Attribute | Job | On the hero? |
|---|---|---|
fetchpriority |
Moves the file up the download queue | Yes, “high” |
loading |
Delays offscreen images | Never “lazy” |
width / height |
Reserves space and prevents layout shift | Always |
sizes |
Tells the browser how wide the image displays | Yes, match the layout |
Before you ship
- The hero has
fetchpriority="high"and no lazy loading. - It has width and height set, so nothing shifts when it arrives.
- It has
srcsetand asizesvalue that matches the layout. - Everything below the fold is still lazy-loaded.
Speed isn’t about removing the good parts. It’s about serving them in the right order. The house rule