Hero Images and LCP in Core Web Vitals
Largest Contentful Paint (LCP) is a key performance metric in Core Web Vitals, measuring how quickly the largest element (often a hero image or banner) becomes visible to users. A common culprit for slow LCP on WordPress sites is a large above-the-fold background image (such as a hero section backdrop) that loads too late. This post explores how to preload hero background images for faster LCP.
Important note, results vary by site setup
Preloading the hero background image is a quick win when that image is the LCP element, but the improvement you see will vary by WordPress setup. LCP is affected by how your hero section is implemented (CSS background vs <img>, sliders, dynamic elements), the theme assets that must load and run (render-blocking CSS, large JS bundles, critical CSS coverage, fonts), the plugins installed (page builders, lazy-load features including background lazy-load, optimization plugins, script loaders), third-party scripts (tag managers, analytics, ads, chat widgets), and even server and caching factors (TTFB, CDN, image delivery). Treat this as a strong contributor to better LCP, not a guaranteed single-step fix.
Why You Shouldn’t Lazy-Load Hero Background Images
Lazy-loading is a great technique for deferring offscreen images, but above-the-fold hero images should always load eagerly. If a hero image is lazy-loaded, the browser delays fetching it, directly hurting your LCP timing. In fact, WordPress core developers discovered that automatically lazy-loading all images (including the first viewport image) regressed LCP by about 7% in testing. That’s why WordPress now avoids adding loading="lazy" to the first content image by default. For background images, you should take similar care: do not apply any lazy-loading mechanism to a hero background, and ensure it’s fetched as early as possible.
Early discovery is critical. Unlike an <img> tag in HTML, which the browser sees immediately and can start downloading, a CSS background image is typically discovered late in the page load process. The browser must first load and parse the CSS before it even knows about a background image URL. This creates an extra round-trip and delay. By the time the CSS is processed and the background image request is made, precious seconds may have passed. In contrast, an inline image in HTML is recognized and queued for download during HTML parsing. In short, hero background images that are only referenced in CSS wait behind the CSS and risk becoming the last thing to load in the render path.
Key takeaway: Don’t hide your LCP image behind slow CSS or lazy-load scripts. Make sure the hero image is included or hinted at in the initial HTML so the browser can fetch it immediately. This might mean outputting an <img> in your hero section or using a preload hint (discussed below) for background images. The goal is to treat that hero image as a first-class, high-priority resource.
Best Practices for Hero Background Images in WordPress
Implementing a hero background image efficiently in WordPress requires both proper HTML/CSS structure and leveraging WordPress features:
- Use Standard HTML for Critical Images Whenever Possible: If your design allows, consider using an actual
<img>element for the hero image (with CSS positioning or a covering container) rather than purely a CSS background. An<img>can benefit from native responsive image attributes (srcset, sizes) and priority hints. Browsers naturally prioritize images in the viewport, and modern WordPress versions automatically avoid lazy-loading the first image, which helps LCP. If using an<img>for the hero, mark it as high priority (e.g., addfetchpriority="high"in WordPress 6.1+ or use a plugin) to further boost its loading priority. - Inline Critical CSS or Use Early Loading: If you must use a CSS background (for example, for a decorative effect with overlay text), ensure the CSS that references this image is loaded as early as possible. This could mean inlining the CSS for the hero in the
<head>of your HTML. However, be cautious: even an inline<style>withbackground-image: url("hero.jpg")will only let the browser discover the image once that HTML is parsed. It’s better than a separate CSS file, but you can do even better by explicitly preloading the image (next section). Do NOT rely on JavaScript to set the background after page load for a hero image, as that defers loading until scripts run, which is even later (and will almost guarantee a poor LCP). - Exclude Hero Images from Lazy-Loading Plugins: Many performance plugins (or WordPress itself) lazy-load images by adding attributes or using JavaScript observers. Ensure your hero image is excluded. For example, WP Rocket’s “Lazy load CSS background images” feature should be turned off or configured to skip the hero section. If you are using WordPress’s native lazy loading, no special markup is needed for backgrounds (since it only targets
<img>tags), but if you use a plugin that also defers background images, make sure the hero section is marked to not lazy-load. The hero image needs to load immediately, not on scroll. - Plan for Responsive Design: Often, you’ll want different hero images or sizes for desktop and mobile (for example, a wide image for desktop vs. a taller cropped image for mobile). In WordPress, this can be handled by CSS media queries or using the
<picture>element if you opt for an<img>. Plan your theme or page builder setup so that the mobile view isn’t downloading a huge desktop background image unnecessarily. (We’ll cover how to preload separate images per device in a moment.)
With these general best practices in mind, let’s dive into the specific technique of preloading to ensure the hero background image downloads as early as possible.
Preloading Hero Background Images for Faster LCP
One of the most effective ways to speed up your hero background image is to use an HTML preload hint. Preloading tells the browser in advance that a certain resource is high priority, allowing it to start fetching that resource immediately, even before it encounters it in CSS or HTML content. This is perfect for background images, which otherwise would only load late during CSS processing.
How to preload a background image: In your page’s <head>, add a <link rel="preload" as="image"> tag pointing to your hero image’s URL. For example:
<link rel="preload" as="image" href="/path/to/hero-background.jpg" fetchpriority="high">This single line ensures the browser starts downloading hero-background.jpg right away with high priority. The fetchpriority="high" attribute is recommended for LCP images – without it, some browsers treat image preloads with a default “low” priority, which could negate the benefit. By explicitly setting it to high, you’re telling the browser this image is critical to render upfront.
WordPress implementation: You can add this preload tag via your theme. For instance, in a custom theme’s header.php, output the link tag (making sure to reference the correct image URL). If your hero image is a featured image or a custom header, you might retrieve it dynamically. Here’s a simplified example in a WordPress theme context:
<?php
// If on front page, preload the hero image (featured image).
if( is_front_page() && has_post_thumbnail() ):
$hero_url = get_the_post_thumbnail_url(null, 'full'); ?>
<link rel="preload" as="image" href="<?php echo esc_url($hero_url); ?>" fetchpriority="high">
<?php endif; ?>This PHP snippet (to be placed in the <head> section of your theme) checks for a featured image on the front page and emits a preload hint for it. You could adapt the logic to your specific case (perhaps using a theme option or custom field for the hero background URL). The key is that this <link> tag should appear early, ideally before your main stylesheet link, so that the browser learns about the image immediately. When done correctly, preloading can cut the LCP image load time dramatically – DebugBear reported cutting an LCP in half (3.4s to 1.7s) by preloading the background hero image.
Example: If your hero section uses a CSS class .hero with a background, and the image is hero.jpg, you would preload hero.jpg. The browser will fetch it, and by the time your CSS applies the background style, the image is likely already in cache or in progress. This means it can paint almost instantly once the CSS is parsed. Just remember that preloading is most effective when you’re fairly certain the image will indeed be needed (which is true for a hero image visible on page load).
Also read: Why is WordPress Maintenance Crucial for Your Business Operation?
Responsive Images: Preloading Desktop and Mobile Hero Images
For responsive sites, you may have separate hero images optimized for mobile and desktop. You don’t want to preload an image that the user won’t actually need (e.g., preloading a huge desktop image for a mobile user). Thankfully, the <link> tag supports a media attribute, allowing us to conditionally preload resources based on media queries.
Technique: Add two preload tags with different media conditions. For example:
<link rel="preload" as="image" href="/path/to/hero-desktop.jpg" media="(min-width: 768px)" fetchpriority="high">
<link rel="preload" as="image" href="/path/to/hero-mobile.jpg" media="(max-width: 767px)" fetchpriority="high">In this code, devices with a viewport width of 767px or less will preload the mobile hero image, while larger viewports will preload the desktop hero image. Only the matching <link> will be honored by the browser, so you won’t waste bandwidth preloading the wrong asset. This mirrors how you might use CSS media queries or the <picture> element to serve different images. The browser essentially uses the media attribute to decide which preload to execute, just as it would for a <link rel="stylesheet" media="...">.
Make sure that the same media query logic is used in your CSS or page code to display the corresponding image. For instance, your CSS might look like:
// for desktop
@media (min-width: 768px) {
.hero {
background-image: url("/path/to/hero-desktop.jpg");
}
}
// for mobile
@media (max-width: 767px) {
.hero {
background-image: url("/path/to/hero-mobile.jpg");
}
}This way, the preload hint aligns perfectly with the actual usage: the mobile browser preloads and uses the mobile image, and the desktop does the same with the desktop image. The result is faster LCP on both device types, without any one device having to download an unnecessarily large image.
Tip: Alternatively, for more complex responsive scenarios (e.g., multiple resolutions or DPR differences), you can use the imagesrcset and imagesizes attributes on the <link rel="preload"> tag, similar to how you would on an <img> tag. This is useful if you have an array of srcset candidates. But for most hero images where you have one image per device layout, the media attribute approach is simpler and effective.
Image Format, Size, and Compression Considerations
Optimizing the delivery of your hero image isn’t just about when it loads, but also how. Even if you preload an image, if the file is enormous or inefficient, it can still slow down LCP. Follow these best practices for the image itself:
- Use Modern Image Formats: Serve the hero image in next-gen formats like WebP or AVIF for significantly smaller file sizes. These formats offer superior compression over old JPEG/PNG without visible quality loss. For instance, WebP can often cut file size by ~30% compared to JPEG for the same quality level. AVIF often compresses even further (though with potentially higher decoding cost). By reducing bytes, you speed up downloads and improve LCP. WordPress supports WebP natively (and with plugins, can serve AVIF or WebP to browsers that support them). If you use a plugin like Imagify or Smush, you can automatically convert and serve WebP/AVIF images on your site. Always provide a fallback (or let the plugin handle it) for browsers that might not support the newer format, though by 2025, support is very broad.
- Proper Dimensions for Each Device: Don’t ship a 2000px wide image to a 360px wide screen. Ensure your hero image’s pixel dimensions are suited to the device. This may mean creating a mobile-specific crop or just using WordPress’s built-in image sizes. If your theme uses
the_post_thumbnail()or responsive image functions, leverage the srcset it outputs. This way, mobile users get a smaller image file, while desktop gets a higher resolution – improving load times without sacrificing quality. In our preload example above, we split completely different images for mobile/desktop for art direction reasons. Even if you use one image with srcset, just make sure the largest size isn’t unnecessarily huge. As a guideline, aim for the hero image to be just a bit larger than the container’s display size (to account for high-DPR screens) but not wildly beyond. Also specify width and height (or use CSS aspect-ratio) for the hero container to avoid layout shifts. - Compress and Optimize: Export your hero images with an appropriate compression level. High-quality photographs can usually be compressed quite a bit (e.g., 75% quality JPEG or equivalent WebP) before artifacts become noticeable. Tools or plugins can automate this. The goal is to keep the hero image file size as small as possible – under 200 KB is a good target for a full-screen image, if achievable, without visible quality loss. Test different compression settings; an over-compressed image might degrade user perception, but an uncompressed one can drastically slow down LCP. Find a balance and consider the content of the image (photographs tolerate lossy compression; graphics or text images might need lossless or a higher quality setting).
- Caching and CDN: Though not specific to the image itself, remember that serving the hero image from a fast source helps. Host your images on a CDN or use a service that delivers them quickly worldwide. This reduces latency, which is especially important since we are making the hero image load early – we want that early load to be as fast as possible.
By choosing the right format and optimizing size, you complement the preload strategy. A preloaded image that is half the size will obviously load faster than a larger one, directly benefiting LCP . Every kilobyte counts, especially on mobile connections.
It’s worth reiterating a few pitfalls to avoid when dealing with above-the-fold background images:
- Inline CSS Backgrounds vs. Preload: Some developers inline critical CSS (including background images) to speed up rendering. While inlining can help eliminate an HTTP request for CSS, it does not fully solve the issue of late image discovery. The browser still has to parse that inline CSS to find the url() reference. If the inline CSS is at the top of the HTML, that’s fairly quick – but if you have a lot of HTML or if the style is further down, it can still introduce a delay. The safest approach is still to use a preload hint for that image, which essentially shortcuts the process by telling the browser about the image even before it encounters the CSS rule . In short, don’t assume that just because you used a style attribute or an inline <style> block for your hero image that it’s as optimized as it can be. Preload if LCP is a concern.
- Don’t Use JavaScript to Load Hero Images on Page Load: Sometimes, background images are set via JavaScript (for example, some sliders or hero animations might do this). That is detrimental to LCP. If your hero image is injected or swapped in by JS after the page loads, the image fetch is delayed until scripts run and execute, which is far along in the loading timeline . If you have such a setup (perhaps for an image carousel or header), consider refactoring so that the first image is in the HTML/CSS from the start (you can still lazy-load subsequent slider images). At minimum, use a preload to get that first image loading early. The principle is: the primary LCP image should require no JavaScript to appear.
- Avoid CSS background-imagewith data URIs or overly complex values for LCP images: While not common, if someone inline-embeds a huge image as a Base64 data URI in CSS, it can bloat your HTML/CSS and possibly delay rendering or parsing. It’s generally better to keep the hero image as a real resource that can be cached and requested independently (and preloaded). Data URIs also can’t be selectively preloaded or prioritized easily.
- Test and Monitor: After implementing these optimizations, test your site with tools like Google PageSpeed Insights or WebPageTest to see the effect on LCP. Verify in Chrome DevTools network panel that your hero image is indeed loading early (you should see the request for it start almost immediately after the HTML, marked as high priority). If it’s still starting late, double-check that the preload tag is correctly placed and not blocked by anything. Tools like DebugBear even highlight if the LCP element was discovered via HTML or CSS . Monitoring will ensure your changes are effective.
The Bigger Picture: Hero Images and Core Web Vitals
Optimizing your above-the-fold image is a big win for LCP, but remember that LCP is one piece of the puzzle. A fast LCP contributes to good Core Web Vitals, but you should also keep an eye on First Input Delay (or INP, its successor) and Cumulative Layout Shift (CLS). Fortunately, image optimization can help with those too: a properly sized hero image with defined dimensions prevents layout shifts (improving CLS), and efficient loading can indirectly aid INP by freeing up main-thread time.
It’s also important to ensure other factors aren’t holding back your LCP:
- Server response time – The faster your HTML is delivered, the sooner everything else can start. If your Time to First Byte (TTFB) is slow, even a preloaded image can’t show up until the HTML arrives.
- Render-blocking resources – As the DebugBear example noted, even if the image is downloaded early, the browser won’t display it until CSS (and potentially JS) that affect render are done . Make sure your CSS is optimized (minimize large CSS, use critical CSS techniques if necessary) so it doesn’t introduce a long render delay after the image is ready.
- Other large elements – If your hero image is optimized but you have a giant heading or video that’s larger in render size, that other element might become the LCP element. Always identify what your LCP element is (Chrome DevTools or Lighthouse will tell you) and focus efforts there.
By preloading and properly handling your hero background image, you’re tackling one of the most impactful optimizations for visual load speed. Many WordPress sites see immediate LCP improvement from this step alone, as the hero image often is the Largest Contentful Paint element. For example, not lazy-loading and preloading a hero image can turn a sluggish loading banner into a near-instant one . That improvement directly translates into a better user experience – the site feels like it’s loading faster because the most significant above-the-fold element is quickly in place.
Wrapping Up
Above-the-fold background images in hero sections make a strong visual statement, but they shouldn’t come at the cost of poor performance. By loading your hero image early and efficiently – avoiding lazy-load on it, preloading it in the HTML, using appropriate formats and sizes – you significantly improve LCP, which is a core metric for both user experience and SEO.
Remember, optimizing the hero background image is not the only step for great performance, but it’s often a high-impact win. Coupled with other best practices (caching, minimizing third-party scripts, optimizing fonts, etc.), it helps ensure your site passes Core Web Vitals with flying colors. A fast-loading hero section sets the tone for the entire visit – giving your users a visually engaging experience that arrives almost instantly. By following these guidelines, WordPress developers and site owners can have striking hero designs and superb LCP scores – truly the best of both worlds for web performance.
Sources:
- web.dev, Optimize Largest Contentful Paint (LCP)
- web.dev, Preload responsive images
- web.dev, Optimize resource loading with the Fetch Priority API
- web.dev Learn, Responsive images (preload hints)
- WordPress Core, Refining WordPress core’s lazy-loading implementation
- WordPress Trac, Ticket #53675 (lazy-loading and LCP)
- DebugBear, Fix LCP for background images
- DebugBear, Preload your LCP image
- MDN, Fix image LCP
- WP Rocket Docs, LazyLoad for CSS background images
- WP Rocket Docs, Disable LazyLoad on specific images
