73% of mobile pages have an image as their LCP element. When that image is unoptimized, nothing else you do matters—you're bottlenecked by download time.
The median site keeps LCP images under 100KB, but 8% of sites serve LCP images over 1MB. That's 5-10 seconds of download time on 3G.
Large images slow LCP in a direct, linear way: bigger file = longer download. The resource load duration—how long the image takes to download—accounts for roughly 40% of typical LCP time.
Common causes:
Lighthouse flags this through multiple audits: "Properly size images," "Serve images in next-gen formats," "Efficiently encode images," and the combined "Improve image delivery" insight.
A 1MB hero image on a 3G connection (1.5 Mbps):
The same image optimized to 100KB:
Red flags:
Look for these audits:
Waterfall chart shows exact download time per image. Look for long bars on image requests.
WebP and AVIF are 30-50% smaller than JPEG at equivalent visual quality.
<!-- Before: JPEG only -->
<img src="hero.jpg" alt="Hero">
<!-- After: modern formats with fallback -->
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" width="1200" height="600">
</picture>
Format comparison at ~80 quality:
| Format | Typical size | Browser support |
|---|---|---|
| JPEG | 150KB | 100% |
| WebP | 90KB | 97%+ |
| AVIF | 60KB | 93%+ |
Use AVIF as primary, WebP as fallback, JPEG for ancient browsers.
Don't serve 2400px images to 400px mobile screens.
<img
src="hero.webp"
srcset="
hero-400.webp 400w,
hero-800.webp 800w,
hero-1200.webp 1200w,
hero-1600.webp 1600w
"
sizes="100vw"
width="1200"
height="600"
alt="Hero"
>
Understanding srcset and sizes:
srcset lists available images with their widths (400w = 400px wide)sizes tells browser how wide the image will displayCommon sizes patterns:
<!-- Full width hero -->
sizes="100vw"
<!-- Full width mobile, half width desktop -->
sizes="(max-width: 768px) 100vw, 50vw"
<!-- Fixed width sidebar image -->
sizes="300px"
<!-- Complex layout -->
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
Quality 80-85 is visually indistinguishable from 100 for most images.
npx sharp-cli hero.jpg -o hero.webp -- webp -q 80
convert hero.jpg -quality 80 hero.webp
# cwebp (Google)
cwebp -q 80 hero.jpg -o hero.webp
Quality guidelines:
| Image type | Quality | Rationale |
|---|---|---|
| Hero/LCP | 80-85 | Needs to look sharp |
| Product photos | 75-80 | Balance quality/size |
| Thumbnails | 70-75 | Small display size |
| Backgrounds | 60-70 | Often blurred/dimmed |
Always include width and height to prevent layout shifts and help browser allocate space.
<img
src="hero.webp"
width="1200"
height="600"
style="width: 100%; height: auto;"
alt="Hero"
>
Or use CSS aspect-ratio:
.hero-img {
width: 100%;
aspect-ratio: 2 / 1;
object-fit: cover;
}
Image CDNs (Cloudinary, imgix, Cloudflare Images) handle format selection, resizing, and compression automatically.
<!-- Cloudinary: automatic format, width, quality -->
<img
src="https://res.cloudinary.com/demo/image/upload/w_800,q_auto,f_auto/hero.jpg"
srcset="
https://res.cloudinary.com/demo/image/upload/w_400,q_auto,f_auto/hero.jpg 400w,
https://res.cloudinary.com/demo/image/upload/w_800,q_auto,f_auto/hero.jpg 800w,
https://res.cloudinary.com/demo/image/upload/w_1200,q_auto,f_auto/hero.jpg 1200w
"
sizes="100vw"
alt="Hero"
>
Benefits:
<picture>
<!-- AVIF for modern browsers -->
<source
type="image/avif"
srcset="
hero-400.avif 400w,
hero-800.avif 800w,
hero-1200.avif 1200w
"
sizes="100vw"
>
<!-- WebP fallback -->
<source
type="image/webp"
srcset="
hero-400.webp 400w,
hero-800.webp 800w,
hero-1200.webp 1200w
"
sizes="100vw"
>
<!-- JPEG for old browsers -->
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w
"
sizes="100vw"
width="1200"
height="600"
fetchpriority="high"
decoding="async"
alt="Descriptive alt text"
>
</picture>
Image download time is governed by simple physics: file size divided by bandwidth. You cannot make bytes download faster—you can only send fewer bytes.
Modern formats use better compression algorithms:
Responsive images ensure you're not sending 4x more pixels than needed:
Combined, format + responsive + compression can reduce image size by 80-90%.
next/image handles everything—format selection, responsive sizes, lazy loading:import Image from 'next/image'
<Image
src="/hero.jpg"
width={1200}
height={600}
priority // For LCP image
sizes="100vw"
alt="Hero"
/>
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
quality: 80,
},
}
@nuxt/image with a provider (ipx, cloudinary, etc.):<NuxtImg
src="/hero.jpg"
width="1200"
height="600"
sizes="100vw sm:100vw md:100vw lg:100vw"
format="webp"
quality="80"
preload
alt="Hero"
/>
export default defineNuxtConfig({
image: {
quality: 80,
format: ['webp', 'avif'],
screens: {
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
},
},
})
function ResponsiveImage({ src, alt, sizes = '100vw' }) {
const base = src.replace(/\.[^.]+$/, '')
const ext = src.match(/\.[^.]+$/)?.[0] || '.jpg'
return (
<picture>
<source
type="image/avif"
srcSet={`${base}-400.avif 400w, ${base}-800.avif 800w, ${base}-1200.avif 1200w`}
sizes={sizes}
/>
<source
type="image/webp"
srcSet={`${base}-400.webp 400w, ${base}-800.webp 800w, ${base}-1200.webp 1200w`}
sizes={sizes}
/>
<img
src={`${base}-800${ext}`}
srcSet={`${base}-400${ext} 400w, ${base}-800${ext} 800w, ${base}-1200${ext} 1200w`}
sizes={sizes}
alt={alt}
fetchPriority="high"
/>
</picture>
)
}
After implementing:
Expected improvement: Reducing a 500KB JPEG to 100KB WebP can save 400-800ms on typical connections.
If sizes doesn't match actual layout, browser downloads wrong image.
<!-- Wrong: says full width, but image is actually half width -->
<img sizes="100vw" src="..." style="width: 50%">
<!-- Right: matches layout -->
<img sizes="50vw" src="..." style="width: 50%">
Quality too low creates visible artifacts. Test visually.
cwebp -q 30 hero.jpg -o hero.webp # Will look bad
cwebp -q 80 hero.jpg -o hero.webp
2x and 3x displays need larger source images.
<!-- srcset should include sizes for high-DPR -->
srcset="
hero-400.webp 400w, /* 400px @ 1x */
hero-800.webp 800w, /* 400px @ 2x, 800px @ 1x */
hero-1200.webp 1200w /* 400px @ 3x, 600px @ 2x */
"
Mobile users on slow networks benefit most from image optimization. Test on mobile.
Often appears alongside:
Image optimization varies by page. Your homepage hero might be perfect while blog post images are unoptimized PNGs. Unlighthouse scans your entire site and identifies pages with oversized images.