What you'll learn
Quick Answer
Modern image formats like WebP and AVIF typically compress photos smaller than JPEG or PNG at the same visual quality, but the gap depends heavily on the image itself and the quality setting you choose, so treat any single percentage figure as a starting estimate rather than a guarantee. Serve the right size for each screen with srcset and sizes, wrap modern formats in a picture element with an older-format fallback, and add loading="lazy" to offscreen images so the browser skips downloading them until needed.
JPEG, PNG, WebP, AVIF: What Each Is For
Four formats cover almost every image on the web, and each has a job:
- JPEG, lossy, built for photos with smooth gradients and lots of color variation. No transparency support.
- PNG, lossless, built for flat colors, sharp edges, text, and transparency. Wastes space on photographic detail because it isn't throwing anything away.
- WebP, supports both lossy and lossless compression in one format, plus transparency and animation, effectively covering what JPEG and PNG each do separately. WebP files are commonly smaller than equivalent JPEG or PNG files, though the exact gap depends on the image.
- AVIF, newer, generally the smallest of the four at matching visual quality on photographic content, at the cost of slower encoding and less universal decode support in older software.
The mistake to avoid is treating this as "AVIF always wins, use it everywhere." What actually wins depends on what's in the image, which is worth testing rather than assuming.
What We Actually Measured
Rather than repeat a vendor percentage, we generated two test images and compressed each with Sharp, the image library behind most Node-based build pipelines, to see actual numbers.
A synthetic photo-like image (1200×800, gradient plus shapes plus film-grain-style noise) at matching quality-80 settings:
JPEG quality 80 146.9 KB
WebP quality 80 182.9 KB (24.5% LARGER than JPEG)
AVIF quality 50 96.2 KB (34.5% smaller than JPEG)
WebP came out larger than JPEG here, the opposite of the usual claim, because "quality 80" isn't a comparable number across formats; each codec's quality scale is its own scale. AVIF at a lower nominal quality number produced the smallest file at comparable visual quality.
A flat-color UI graphic (800×400, solid shapes and text, transparent background), the case PNG exists for, told a different story:
PNG lossless 5.7 KB
WebP lossless 2.2 KB (60.8% smaller than PNG)
JPEG quality 90 10.3 KB (larger, AND loses transparency)
Same four formats, opposite winner, because the content is different. That's the actual lesson: run your own images through your own pipeline before trusting a blanket number, including ours.
Serving the Right Size with srcset and sizes
srcset gives the browser several versions of an image and lets it pick, instead of everyone downloading one size meant for the largest screen:
<img
srcset="photo-480.jpg 480w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 800px"
src="photo-800.jpg"
alt="Product photo"
>
The 480w, 800w, and 1200w are width descriptors, each image's actual pixel width, not a display size. sizes tells the browser how wide the image will actually be rendered at different viewport widths, so it can match a source to the real display size and device pixel ratio instead of guessing. Below a 600px viewport, the image fills the viewport (100vw); above that, it renders at a fixed 800px.
The plain src stays as a fallback for browsers that don't understand srcset, which by now is close to none, but costs nothing to include. Skipping sizes and providing only srcset is a common mistake: without it, the browser has to guess the display size before it has finished laying out the page, and that guess is frequently wrong on anything other than a full-width image.
The picture Element and Fallback Order
<picture> lets you offer a modern format first and fall back gracefully, rather than choosing one format for every browser:
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="Product photo" loading="lazy">
</picture>
The browser checks each <source> in order and uses the first one it supports, AVIF, then WebP, then falling through to the mandatory <img> at the end if neither modern format is understood. Order matters: put your smallest, newest format first, since that's the one browsers reach for whenever they can.
Every attribute that works on a plain <img>, alt, loading, width, height, belongs on that final fallback <img>, since it's the element that actually renders in every case, including the one where none of the <source> elements match.
Lazy Loading, and Its One Big Caveat
loading="lazy" on an <img> tells the browser to skip downloading that image until it's close to entering the viewport, instead of fetching every image on the page immediately regardless of whether the visitor ever scrolls that far:
<img src="footer-banner.jpg" alt="Seasonal offer" loading="lazy" width="1200" height="400">
It's a single attribute, needs no JavaScript, and is supported in every current browser. The one real caveat: it should never go on an image that's visible immediately when the page loads. The largest such image is very often the page's Largest Contentful Paint element, and lazy-loading it means the browser doesn't even start fetching it until after layout, which makes LCP worse, not better. Lazy loading is for images below the fold, not the hero image at the top.
The other easy win in that same snippet: explicit width and height let the browser reserve the right space before the image arrives, preventing the layout shift that shows up as a Cumulative Layout Shift penalty.
