Home / Blog / Image optimization that actually moves LCP: WebP, AVIF, and responsive images

Image optimization that actually moves LCP: WebP, AVIF, and responsive images

On one e-commerce project, 70% of the page weight was images. Moving to WebP, trying AVIF, and tightening srcset discipline cut LCP in half. Notes from the work.

Most performance projects pick up half their wins from image optimization alone. Combine WebP, AVIF, responsive images, and lazy loading correctly and LCP improves dramatically.

Here’s what I learned from image optimization across the last 3 e-commerce projects.

The size of the problem

On a WooCommerce audit, the page weighed 4.2 MB. 3.1 MB of that was images, and 2.8 MB was a single banner JPEG. LCP was 4.8 seconds, and half the mobile traffic bounced before the page even rendered.

The problem sits in 3 layers:
1. Wrong format (JPEG/PNG aren’t the default for the 2020s)
2. Wrong size (a 3840px asset for a 2400px viewport)
3. Wrong load timing (everything below the fold is eager)

WebP: the new default

WebP was still “for modern browsers” up until 2024. In 2026 browser support is 98%+. Every engine except Internet Explorer renders it. Safari, Firefox, Chrome, Edge, all of them.

WebP wins:
– 25 to 35% smaller than JPEG at equivalent quality
– 50 to 80% smaller than PNG (with transparency support)
– Supports both lossless and lossy modes

The way to move to WebP isn’t a one-shot conversion, it’s auto-format delivery at the CDN layer. Cloudflare Polish, Imgix, Cloudinary, Bunny Optimizer, all of them look at the user’s browser and serve the right format.

On WordPress, plugins like EWWW, ShortPixel, and Smush convert every JPEG under wp-content/uploads/ to WebP. I use ShortPixel, CDN integration is solid.

AVIF: better, but not default yet

AVIF produces even smaller files than WebP (30 to 50% smaller), but:
– iOS Safari only supports AVIF from 16.0+
– Encoding is CPU-heavy, batch conversion is about 10x slower
– You can hit edge-case artifacts in image quality

Instead of making AVIF the default, I use it as progressive enhancement:

<picture>
    <source type="image/avif" srcset="hero.avif">
    <source type="image/webp" srcset="hero.webp">
    <img src="hero.jpg" alt="...">
</picture>

If the browser supports AVIF it uses that, otherwise WebP, otherwise JPEG. Best format wins.

Responsive images: srcset plus sizes

A single-size image is unforgivable in 2026. Serving the same asset to a 360px mobile and a 1920px desktop screen is wasted bandwidth.

The srcset plus sizes combo tells the browser: “Here are the variants I have, and here’s how big this image will be at each viewport, pick the right one.”

<img
    src="product-800.jpg"
    srcset="product-400.jpg 400w, product-800.jpg 800w, product-1200.jpg 1200w, product-1600.jpg 1600w"
    sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
    alt="Product"
    loading="lazy"
>

sizes tells the browser how many pixels this image will occupy in the layout. Browser picks the best srcset variant based on viewport and DPR.

WordPress does this automatically: call wp_get_attachment_image() or the_post_thumbnail() and srcset is generated for you. If you’re hand-writing <img> tags, you have to set sizes correctly yourself.

Image dimensions: always explicit

Writing width and height on the <img> tag is critical for CLS (Cumulative Layout Shift). Without dimensions the browser can’t reserve layout space, and the page shifts when the image lands.

<img src="..." width="800" height="600" alt="...">

What matters is the aspect ratio, not the literal pixel size. 800×600 and 400×300 both say “4:3”, the browser reserves a 4:3 slot, and CSS sets the actual rendered size.

The aspect-ratio CSS property does the same thing:

img { aspect-ratio: 16 / 9; width: 100%; height: auto; }

The first approach is native HTML support; the second is CSS-side control. I use both together when I can.

Lazy loading: native is enough

The native loading="lazy" attribute is supported in Chrome, Firefox, Safari, and Edge as of 2024. No need for custom IntersectionObserver lazy-loading JS.

One exception: don’t lazy-load the LCP element. Hero images and above-the-fold featured images have to be eager, otherwise LCP takes a hit.

<!-- Hero (LCP) -->
<img src="hero.jpg" fetchpriority="high" loading="eager">

<!-- Below the fold -->
<img src="gallery-2.jpg" loading="lazy">

fetchpriority="high" tells the browser “this image is high priority”. Always use it on the LCP image.

Blur placeholder or skeleton

Showing a blank box while the image loads is a bad experience. Two options:

LQIP (Low Quality Image Placeholder). A 20 to 30 pixel blurred base64 image inlined. As soon as loading starts, the blurry preview shows and the real image fades in once it arrives. Plaiceholder and sharp are libraries that generate these.

Skeleton. An animated gray rectangle. Not as pretty as LQIP but generic and quick to implement.

On a WooCommerce product listing, LQIP gives a much more premium hover feel. A day to implement and months of payoff.

Image CDN: don’t build your own pipeline

An image CDN (Cloudinary, Imgix, Cloudflare Images, Bunny CDN) solves:

  • On-the-fly format conversion (append a URL parameter for WebP/AVIF)
  • On-the-fly resize (400w, 800w generated dynamically)
  • On-the-fly quality adjustment
  • Global edge cache
  • Smart crop (ML-based focal point detection)

Fifty bucks a month buys you more savings than that. Standing up your own image processing pipeline, running ImageMagick servers, thinking through cache invalidation, coding a format matrix, all wasted time.

Performance impact: real numbers

Before and after image optimization on a WooCommerce project:

  • Average page weight: 4.2 MB to 1.1 MB (-74%)
  • LCP (mobile p75): 4.8s to 2.3s (-52%)
  • TTFB effect: none (CDN edge cache)
  • Mobile bounce rate: -18%
  • Conversion rate: +9%

A 9% lift in conversion rate makes image optimization a six-figure annual return. Cheap investment.

Audit checklist

When I audit a site:

  • [ ] Is WebP/AVIF being served? (check Accept header with curl)
  • [ ] Does srcset produce the right variants? (DevTools network panel)
  • [ ] Does the sizes attribute match the layout?
  • [ ] Are width and height explicit?
  • [ ] Are below-fold images lazy?
  • [ ] Is the LCP image eager with fetchpriority=high?
  • [ ] Is an image CDN in use?
  • [ ] Is there a placeholder strategy?

I run this list on every new project. The number of missing items usually correlates directly with the page speed score.

Have a project on this topic?

Leave a brief summary — I’ll get back to you within 24 hours.

Get in touch