LCP is the Core Web Vital that most directly impacts your Google ranking — and a slow hero image is the #1 cause of a failing score. This guide covers every technique to get your LCP under 2.5 seconds: fetchpriority, preload, format conversion, responsive sizing, and CDN delivery.
Largest Contentful Paint (LCP) measures how long it takes for the largest visible element above the fold to fully render. For roughly 70% of web pages, that element is a hero image — a banner, product photo, or full-width background image near the top of the page.
LCP is one of Google's three Core Web Vitals, which directly affect your search rankings since the 2021 Page Experience update. A poor LCP score doesn't just hurt performance — it reduces your ranking potential and increases bounce rate.
LCP is a real-user metric. Google measures it across all your actual visitors in the Chrome User Experience Report (CrUX). Lab tools like Lighthouse give you a lab score; what counts for ranking is your field data — the 75th percentile of real user experiences.
<img> elements (most common)<image> inside SVG<video> poster imagesbackground-imageThis guide focuses on the most impactful case: optimizing an <img> LCP element.
Before you can optimize your LCP image, you need to know exactly which element it is.
F12 or Cmd+Option+I)Run PageSpeed Insights on your URL at pagespeed.web.dev. The "Largest Contentful Paint" section shows a screenshot of the LCP element alongside the timing breakdown.
// Log LCP element and timing in the browser console new PerformanceObserver((list) => { const entries = list.getEntries(); const last = entries[entries.length - 1]; console.log('LCP element:', last.element); console.log('LCP time:', last.startTime.toFixed(0) + 'ms'); }).observe({ type: 'largest-contentful-paint', buffered: true });
Your LCP element may differ between mobile and desktop layouts. If your hero image is hidden on mobile and replaced by a text heading, you have two different LCP elements to optimize. Always test both viewports.
This is the single highest-impact, lowest-effort change you can make. The fetchpriority attribute tells the browser to fetch this image at the highest priority, jumping it to the front of the download queue.
<!-- Before: normal priority, competes with CSS, fonts, scripts --> <img src="hero.webp" alt="Hero image" width="1200" height="600" /> <!-- After: browser fetches this image first --> <img src="hero.webp" alt="Hero image" width="1200" height="600" fetchpriority="high" />
Google's research shows fetchpriority="high" reduces LCP by 20–30ms on average on fast connections, and significantly more on slower mobile connections where bandwidth contention is severe. It's supported in all modern browsers since 2022.
Only add fetchpriority="high" to your single LCP image. If you apply it to multiple images, the browser treats them all as normal priority again and you lose the benefit entirely.
fetchpriority is supported in Chrome 101+, Edge 101+, Safari 17.2+, and Firefox 132+. Browsers that don't support it simply ignore the attribute — there's no downside to adding it now.
By default, the browser discovers your LCP image only after parsing the HTML and starting to render — which can be hundreds of milliseconds after the page starts loading. A <link rel="preload"> in the <head> tells the browser to start fetching the image immediately, before it reaches the <img> tag in the body.
<head> <!-- Preload the LCP image as early as possible in <head> --> <link rel="preload" as="image" href="hero.webp" fetchpriority="high" /> </head>
If your LCP image is responsive (uses srcset), you must also use imagesrcset and imagesizes on the preload link — otherwise the browser preloads the wrong image size.
<!-- Preload matches the responsive srcset of the <img> below --> <link rel="preload" as="image" imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" imagesizes="(max-width: 768px) 100vw, 1200px" fetchpriority="high" /> <!-- The matching <img> in the body --> <img src="hero-1200.webp" srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" sizes="(max-width: 768px) 100vw, 1200px" alt="Hero banner showing image compression tool" width="1200" height="600" fetchpriority="high" />
Put your <link rel="preload"> as high in <head> as possible — ideally before any stylesheets or scripts. Every element above it delays discovery of the preload hint.
loading="lazy" tells the browser to defer fetching an image until it's near the viewport. Applied to your LCP image — which is already in the viewport — this is catastrophic. You're explicitly telling the browser to delay the image that Google is measuring.
<!-- ✗ NEVER do this to your LCP image --> <img src="hero.webp" loading="lazy" <!-- kills your LCP score --> alt="Hero image" /> <!-- ✓ Correct: eager (default) + fetchpriority --> <img src="hero.webp" loading="eager" fetchpriority="high" alt="Hero image" />
This is a surprisingly common mistake, especially on sites that bulk-apply loading="lazy" to all images via a plugin or global CSS. Always audit your above-the-fold images specifically.
The format of your LCP image has a direct impact on file size, and therefore download time. JPEG and PNG — the legacy formats still used on most websites — are significantly larger than modern alternatives at the same visual quality.
| Format | Vs. JPEG | Browser Support | Best For |
|---|---|---|---|
| AVIF | 30–50% smaller | Chrome, Firefox, Safari 16+ | Photos, hero images |
| WebP | 25–35% smaller | All modern browsers | Photos, illustrations, UI |
| JPEG | Baseline | Universal | Fallback for older browsers |
| PNG | Often larger | Universal | Lossless only; avoid for photos |
Use <picture> to serve AVIF to browsers that support it, WebP as secondary, and JPEG as the universal fallback. Every browser only downloads the first format it supports.
<picture> <!-- AVIF: best compression, newest browsers --> <source type="image/avif" srcset="hero-400.avif 400w, hero-800.avif 800w, hero-1200.avif 1200w" sizes="(max-width: 768px) 100vw, 1200px" /> <!-- WebP: great compression, all modern browsers --> <source type="image/webp" srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" sizes="(max-width: 768px) 100vw, 1200px" /> <!-- JPEG fallback: older browsers --> <img src="hero-1200.jpg" srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w" sizes="(max-width: 768px) 100vw, 1200px" alt="Hero banner showing image optimization dashboard" width="1200" height="600" fetchpriority="high" /> </picture>
IMGVO converts JPEG and PNG to WebP and AVIF directly in your browser — free, no upload, no account needed. Use it to generate all the format variants for your LCP image before deploying.
Serving an image larger than its rendered size is wasted bandwidth — the browser downloads extra pixels it immediately discards. A 3000px-wide hero image displayed at 1200px wastes roughly 6× the bandwidth it should.
Including explicit width and height on your <img> tag lets the browser reserve space for the image before it loads, preventing Cumulative Layout Shift (CLS) — another Core Web Vital. It also helps the browser calculate resource priority earlier.
<!-- ✗ No dimensions: causes layout shift, slows priority calculation --> <img src="hero.webp" alt="Hero" /> <!-- ✓ Correct: width + height preserve aspect ratio, prevent CLS --> <img src="hero.webp" alt="Hero banner" width="1200" height="600" fetchpriority="high" />
Use srcset with width descriptors to let the browser pick the smallest appropriate image for each viewport:
| Breakpoint | Recommended Max Width | Example File |
|---|---|---|
| Mobile (< 480px) | 480px | hero-480.webp |
| Tablet (480–768px) | 768px | hero-768.webp |
| Laptop (768–1280px) | 1280px | hero-1280.webp |
| Desktop (> 1280px) | 1920px max | hero-1920.webp |
Without a sizes attribute, the browser assumes the image is 100vw and downloads a large variant even if the image is only displayed at 40% of viewport width. Always specify sizes accurately.
A CDN (Content Delivery Network) serves your images from edge servers geographically close to each visitor. For users far from your origin server, this alone can reduce LCP by 300–700ms.
max-age values (ideally 1 year) and content-addressable URLs (cache-busting via filename hash).Cloudflare's free tier acts as a CDN for static assets including images. Simply proxying your domain through Cloudflare typically improves TTFB by 50–200ms for international visitors, with zero code changes.
If your LCP element is a CSS background-image, browser discovery is significantly delayed. The browser must first download and parse the CSS, then apply it, before it knows to fetch the image. This adds 200–500ms compared to a native <img> tag.
Whenever possible, replace a CSS background hero with a native <img> element. This is the most impactful change — you gain fetchpriority, preload support, srcset, and browser-native optimization.
If you can't change the markup, preload the background image explicitly:
<!-- Preload the CSS background image in <head> --> <link rel="preload" as="image" href="hero-bg.webp" fetchpriority="high" /> <!-- The CSS that uses it --> /* styles.css */ .hero { background-image: url('hero-bg.webp'); }
CSS background-image with image-set() is still not preloadable with full responsive hints in all browsers. If your background image varies by breakpoint, this is another strong reason to migrate to a native <img> with srcset.
LCP (Largest Contentful Paint) measures how long it takes for the largest visible element in the viewport to fully render. For most pages, this is a hero image or large heading. Google's target is under 2.5 seconds for a "Good" score, and it's one of three Core Web Vitals that directly affect search rankings.
Google classifies LCP as Good if under 2.5s, Needs Improvement between 2.5s–4.0s, and Poor if over 4.0s. For passing Core Web Vitals, you need at least 75% of your real-user page loads to measure Good. This is measured using field data from the Chrome User Experience Report, not lab tools like Lighthouse.
The most common causes are: an unoptimized LCP image (wrong format, not compressed), render-blocking resources that delay image discovery, missing fetchpriority or preload hints, no CDN (high TTFB), server response times over 600ms, and the accidental application of loading="lazy" to the LCP element.
Yes. Google's own data shows fetchpriority="high" on the LCP image reduces LCP by 20–30ms on average, and sometimes much more on slower mobile connections where bandwidth contention is severe. It's a one-line change with no downside, and browsers that don't support it simply ignore it.
Never lazy load your LCP image. Adding loading="lazy" to your LCP image is one of the most damaging mistakes you can make — it explicitly tells the browser to delay fetching the exact element Google is measuring. Only use loading="lazy" on images that are below the fold (not visible on initial page load).
WebP or AVIF are best for LCP images. AVIF compresses 30–50% smaller than JPEG at the same visual quality; WebP is 25–35% smaller. Use a <picture> element with AVIF first, WebP second, and JPEG as fallback to cover all browsers. This way every browser gets the best format it supports without you having to detect it manually.
Yes, directly. LCP is one of the three Core Web Vitals that Google incorporated into its search ranking algorithm via the Page Experience update in 2021. Pages that fail Core Web Vitals (LCP, CLS, INP) receive a ranking penalty compared to otherwise-equal pages that pass. The impact is most visible in competitive niches where page experience is a tiebreaker.
Lighthouse measures LCP in a controlled lab environment (simulated throttled connection, no user cache, headless Chrome). Google Search Console reports field data — real LCP measurements from actual Chrome users visiting your site. Field data includes all network speeds, devices, and geographies, so it's typically worse than lab data. Optimize for field data by using a CDN, and always check the Core Web Vitals report in Search Console for your real-user score.
More resources to help you optimize images for performance and Core Web Vitals.