Product images: AVIF, WebP and the bytes that decide your LCP
Format wars are the least interesting part of image performance. Why your thumbnail generator matters more than AVIF, how to do srcset properly, and why lazy-loading the hero image makes everything worse.
Photo: free stock photography (Unsplash licence) — see imprint
LCP is almost always an image, and almost always your fault
On a product detail page, the Largest Contentful Paint element is the product photo roughly every time. That makes image delivery the single biggest lever on the one Core Web Vital your customers actually feel. Since March this year INP has replaced FID in the set, and it is worth knowing about — but INP is about how fast your page responds to a tap, and it will not move a millimetre no matter what you do to your JPEGs. Images are an LCP conversation. Keep the two apart or you will optimise the wrong thing for a quarter.
The uncomfortable part is that the damage is almost never done by the format. It is done by a 3,000-pixel-wide original shipped to a phone that will display it at 390, by a hero image the browser only discovers after parsing 200 kilobytes of JavaScript, and by a lazy-loading attribute somebody applied to every image on the page including the one above the fold. Those three cost more milliseconds than any codec choice you will ever argue about.
WebP is the safe default; AVIF is the better format with a bill attached
WebP is the boring correct answer for most shops in 2024. Every browser your customers use supports it, the encode is fast enough to run on upload without anybody noticing, and against a well-tuned JPEG it saves a meaningful share of the bytes at the same perceived quality. If your shop still serves JPEG-only, moving to WebP is the highest-return afternoon of work available in the image layer, and it needs no fallback gymnastics worth worrying about.
AVIF is genuinely better, especially on large flat areas of colour and on the kind of studio product photo with a white background that fills most catalogues. The catch is encode cost: AVIF encoding is dramatically slower than WebP, and that is not an abstraction when you have 40,000 images and a supplier who re-delivers the whole catalogue every quarter. Budget it as a batch job on real hardware, not as something your shop does synchronously on import. Serve it through a picture element with WebP behind it, and you get the wins where they are cheap without betting the catalogue on them.
Your thumbnail generator matters more than your codec
Here is the arithmetic nobody runs. Switching a 3,000-pixel image from JPEG to AVIF might save you half the bytes. Resizing that same image to the 400 pixels the phone will actually paint saves you around ninety-five percent of them, in any format. Resolution beats format, every time, by an order of magnitude — and yet the format debate gets the meeting while the thumbnail configuration gets whatever defaults shipped with the system.
So go and look at your thumbnail sizes. In Shopware they live on the media folder, and the standard set is a reasonable start that almost nobody revisits. Check that the sizes you generate correspond to the sizes your layout actually renders at real breakpoints — measure them in a browser, do not guess from the design file. Check that a new upload actually produces the whole set. Then check the worst case: the product where somebody uploaded a 12-megapixel original and the listing is quietly serving it because a thumbnail was missing and the fallback is the source file.
srcset done properly, and the sizes attribute everybody gets wrong
srcset lists the candidates; sizes tells the browser how wide the image will be, and sizes is where shops break their own responsive images. The default when you omit it is 100vw — the browser assumes your image fills the viewport, picks the largest candidate, and every byte you saved by generating thumbnails is handed straight back. A four-column product grid on a desktop needs something like sizes='(min-width: 1200px) 25vw, (min-width: 768px) 50vw, 100vw', and that string has to match the CSS. When the designer changes the grid, it silently stops being true.
Two more things belong in the same breath. Always set width and height attributes on the img — the browser reserves the space, the page does not jump, and your CLS stays where it belongs. And keep the candidate list sane: five widths spanning the range your layout really uses beats twelve, because every extra size is storage, a slower import and another cache entry to warm.
- Never lazy-load the LCP image — loading='eager' plus fetchpriority='high' on the hero.
- Lazy-load everything below the fold, including gallery images two through eight.
- sizes must match your real CSS breakpoints — omitting it means 100vw and full-size downloads.
- width and height on every img, always — free CLS protection.
- Preload the hero with a matching imagesrcset, or the preload just downloads a second file.
Lazy-loading the hero image is the most common self-inflicted wound
loading='lazy' arrived, it was easy, and somebody put it on every image in the template because that felt like the thorough thing to do. The result is that the browser now refuses to start fetching your main product photo until it has done layout and worked out that the image is visible. You have taken the one image on the page that defines LCP and deliberately moved it to the back of the queue. We have seen this single attribute cost a second on mobile, and it is a one-line fix.
The rule is simple and admits no exceptions: the image that is the LCP element loads eagerly, gets fetchpriority='high', and ideally is discoverable in the initial HTML rather than inserted by a slider script after JavaScript boots. Everything else on the page lazy-loads. If your gallery is a JavaScript carousel that builds all its slides on init, the first slide is not lazy and the other seven are — and if the carousel cannot express that, the carousel is your performance problem, not your image format.
| Format | Size vs tuned JPEG | Encode cost | Use it for |
|---|---|---|---|
| JPEG | Baseline | Negligible | Last-resort fallback only |
| WebP | Meaningfully smaller | Fast — fine on upload | The default for the whole catalogue |
| AVIF | Smaller again, best on flat colour | Slow — batch job, real hardware | Hero and top sellers first |
| PNG | Far larger for photos | Negligible | Logos and line art, never product photos |
- Resizing beats re-encoding by an order of magnitude — fix your thumbnails before you argue about AVIF.
- A missing sizes attribute means 100vw, and that hands back every byte your thumbnails saved.
- The LCP image must never be lazy-loaded; everything below the fold must be.
- INP joined Core Web Vitals in March 2024, but images move LCP — do not confuse the two budgets.
Frequently asked questions
Only if you have already fixed resolution, srcset and lazy-loading, and want the last slice. AVIF is smaller but the encoding is far more expensive, which is a real operational cost at catalogue scale. A sensible middle path is AVIF for hero images and top sellers, WebP for the long tail, delivered through a picture element so the browser picks.
Usually because the image is small but late. Check three things: is it lazy-loaded, is it inserted by JavaScript after boot rather than sitting in the initial HTML, and is your server slow to send the document in the first place. A 40-kilobyte hero discovered at second two is worse than a 200-kilobyte hero discovered immediately. LCP measures when it paints, not how big it was.
Both, depending on where you apply it. Below the fold it is nearly free bandwidth and helps everything. On the LCP image it is actively harmful, because the browser deliberately delays the fetch until layout proves the image is visible. The blanket loading='lazy' on every img in a template is one of the most common performance regressions we find.
For a German shop selling mainly to German customers, the latency win from a CDN is modest — your server is already close. The real reason to use an image service is that it does the resizing, format negotiation and cache-busting correctly so your team stops getting it wrong. If your thumbnail pipeline already works, a CDN is a nice-to-have, not a fix.
We do this for a living — Shopware, Node.js, React, ERP integration and automation for B2B.
Talk to an engineer