View contents
Image Preloading Guide: Practical Implementation to Reduce LCP
Introduction
This guide covers how to identify your LCP image, correctly implement rel="preload" and fetchpriority="high", preload responsive images with srcset, and avoid the mistakes that cancel out these techniques' benefit. The UXR SEO Analyzer reports whether the detected LCP image lacks priority signals.
If you need the conceptual background first, see Image Preloading Explained.
Step 1: Identify the LCP Image
Use Chrome DevTools' Performance panel or a Lighthouse report to identify which element is flagged as Largest Contentful Paint. On most content pages it's the hero image or the article's featured image.
Step 2: Basic Preload for an HTML Image
<head>
<link rel="preload" as="image" href="/hero.jpg" fetchpriority="high">
</head>
<body>
<img src="/hero.jpg" fetchpriority="high" width="1200" height="600" alt="Image description">
</body>
Note that we declare fetchpriority="high" both on the <link rel="preload"> and on the <img> tag to maximize the priority signal at both points of the loading pipeline.
Step 3: Preloading Responsive Images
When the LCP image uses srcset/sizes, the preload must replicate those same attributes using imagesrcset and imagesizes so the browser preloads the correct variant:
<link
rel="preload"
as="image"
href="/hero-800.jpg"
imagesrcset="/hero-400.jpg 400w, /hero-800.jpg 800w, /hero-1200.jpg 1200w"
imagesizes="(max-width: 600px) 100vw, 800px"
fetchpriority="high">
Without imagesrcset/imagesizes, the browser could preload a different variant than the one the <img> ultimately uses, wasting the bandwidth of that early download.
Step 4: Preloading a CSS Background Image
If the LCP element is a background-image defined in CSS, the browser doesn't discover it until it processes that CSS rule—that's why a preload in the <head> is essential for early discovery:
<link rel="preload" as="image" href="/hero-bg.jpg" fetchpriority="high">
<style>
.hero { background-image: url('/hero-bg.jpg'); }
</style>
Step 5: Verify the Result
- Open DevTools → Network panel and confirm that
hero.jpgappears among the first requests, with "High" priority. - Run Lighthouse and compare the LCP timestamp before/after the change.
- Confirm there's no
loading="lazy"on the same image—that's the mistake that cancels out the preload's effect.
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
Preload href different from the actual src | The browser downloads a resource that's never used, speeding up nothing | Sync the preload URL exactly with the <img>'s |
| Preloading more than one image as "high" | Priority gets diluted and competes with the real LCP image | Reserve fetchpriority="high" for the LCP element only |
Combining preload with loading="lazy" | Contradictory signals; the browser may ignore the preload | Never use lazy loading on the LCP image |
Preload without the as="image" attribute | The browser doesn't assign the correct priority to the resource | Always declare as="image" |
Framework Implementation
Next.js
The next/image component exposes a priority prop that, when applied to the LCP image, automatically generates both the corresponding <link rel="preload"> in the <head> and the fetchpriority="high" attribute on the rendered element, and also disables lazy loading for that specific image:
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero.jpg"
width={1200}
height={600}
alt="Image description"
priority
/>
);
}
Nuxt / Vue
The <NuxtImg> component from @nuxt/image accepts an equivalent preload prop that generates the same pair of signals (preload link + fetchpriority) during server-side rendering.
Static HTML / Astro
On sites without a built-in image framework, the manual implementation shown in the earlier steps is the only option, so it's worth wrapping it in a reusable component or partial rather than repeating the pattern in every template.
Preloading Multiple LCP Candidates
On sites with dynamic content (for example, a feed where the first item changes per user), you may not know at build time exactly which image will be the LCP. A common strategy is to preload the first feed item's image server-side, since it's statistically the most likely LCP candidate, and accept that the benefit will be smaller in atypical cases.
Continuous Monitoring of LCP After Implementation
Once you've implemented the preload, validate the real-world impact (not just in the lab) by integrating the field metric with the web-vitals library:
import { onLCP } from 'web-vitals';
onLCP((metric) => {
console.log('This user\'s real LCP:', metric.value, 'ms');
// send the value to your analytics system alongside the page URL
});
Compare the distribution of field LCP before and after the change for at least a week, since variable network conditions across users can mask improvements that are consistent in aggregate.
Font Preloading vs. Image Preloading
It's common for a site to already use rel="preload" for critical web fonts, but not extend it to the LCP image. Both resources compete for the same initial bandwidth budget, so adding an image preload without reviewing how many preloads already exist can dilute the priority of all of them. As a general rule, limit critical preloads (main font + LCP image) to a maximum of 2-3 resources per page; adding more preloads than are actually critical to the initial render reduces the benefit of each one.
Conditional Preload by Viewport
In layouts where the LCP image changes between mobile and desktop (for example, a different hero per breakpoint), you can use the media attribute on <link rel="preload"> so the browser only downloads the relevant variant based on viewport width, avoiding an unnecessary download of both images:
<link rel="preload" as="image" href="/hero-mobile.jpg" media="(max-width: 600px)" fetchpriority="high">
<link rel="preload" as="image" href="/hero-desktop.jpg" media="(min-width: 601px)" fetchpriority="high">
Quick Decision Summary
| Question | Answer |
|---|---|
| Is this the page's LCP image? | Yes → apply preload + fetchpriority. No → leave it alone. |
| Is the LCP image in a CSS background? | Yes → preload is mandatory for early discovery. |
| Are there already 2-3 critical preloads on the page? | Yes → evaluate whether you really need to add another. |
Does the image have loading="lazy"? | Yes → remove it if it's the LCP image. |
Working Alongside a Content Security Policy
Sites with a strict Content Security Policy sometimes block preload hints unintentionally if the img-src directive doesn't cover the CDN domain the preloaded image lives on. If a preload silently has no effect, check the browser console for CSP violation warnings before assuming the technique itself failed—this is one of the more common reasons a correctly written preload tag doesn't actually speed anything up in production.
When Preloading Can Backfire
Preloading isn't free: every preloaded resource competes with everything else the page needs during the critical loading window, including render-blocking CSS and the main document itself. Preloading a large, unoptimized image (say, several megabytes because it skipped compression or a modern format) can starve other critical requests of bandwidth and make overall page load worse even as it speeds up that one image. Preload should always be paired with the other image performance basics—correct format, compression, and appropriate dimensions—rather than used as a substitute for them.
Related Articles
- Image Preloading Explained - Concepts and why it matters
- LCP Optimization Guide - Full Largest Contentful Paint strategy
- Fetchpriority Optimization Guide - Resource prioritization in depth
References
- web.dev - Preload critical assets to improve loading speed
- web.dev - Optimize resource loading with the Fetch Priority API
- web.dev - Preload responsive images
- web.dev - Optimize Largest Contentful Paint
- Chrome Developers - LCP request discovery
- Chrome Developers - Avoid chaining critical requests
