Article No. 53
Image Optimization: Formats, Compression, and Lazy-Loading Done Right
Abstract
Images are usually the single biggest page-weight lever on a typical page. Before touching JavaScript bundles, fonts, or third-party scripts, checking whether your images are the right format, correctly compressed,...
On this page
Images are usually the single biggest page-weight lever on a typical page. Before touching JavaScript bundles, fonts, or third-party scripts, checking whether your images are the right format, correctly compressed, and loading at the right time will usually produce the largest single improvement for the least effort.
Format decision framework
The common advice is “always use WebP or AVIF,” which is directionally right but skips the actual decision most people need to make. Here’s a more useful way to think about it:
| Format | When it makes sense | Tradeoff |
|---|---|---|
| AVIF | Photographic images where file size matters most and you can tolerate slower encoding | Best compression of the group, but encoding is meaningfully slower than WebP, which matters if you're generating images at request time rather than pre-processing them |
| WebP | The safe modern default for most images | Slightly larger than AVIF at equivalent quality, but faster to encode and has been broadly supported for longer |
| JPEG | Fallback for older browsers, or source material you're re-encoding from | No transparency, and lossy compression artifacts become visible at lower quality settings |
| PNG | Graphics needing exact color reproduction, transparency, or sharp edges (logos, icons, screenshots with text) | Much larger file sizes than any of the above for photographic content, since it's designed for lossless compression of flat-color graphics, not photos |
On compression figures specifically: AVIF is commonly cited as producing files roughly 50% smaller than an equivalent-quality JPEG, and WebP is commonly cited as producing files roughly 25-35% smaller than JPEG. Treat both as directional ranges, not guarantees. The actual savings depend heavily on image content, a busy photograph with lots of fine detail compresses differently than a flat-color graphic or a screenshot with large areas of solid color, so test on your own representative images rather than assuming a fixed percentage.
On browser support: both WebP and AVIF now have broad support across current browser versions, but exact figures shift as browser usage share changes and older versions age out of the data. Rather than hardcode a percentage here that will be stale within months, check current figures at caniuse.com’s WebP page and caniuse.com’s AVIF page at the time you’re making a format decision. If your audience skews toward older devices or specific regions with slower browser update cycles, verify support for your actual audience rather than the global average.
For most sites in 2026, a sensible default is serving AVIF where the encoding time isn’t a bottleneck, WebP as the next choice, and JPEG or PNG as the final fallback, using the <picture> element’s ability to let the browser pick the first supported format from an ordered list.
Compression and quality settings
“Quality 80-85” gets repeated as a rule of thumb for JPEG and WebP compression, and it’s a reasonable starting point, but it’s not a universal constant. What that setting actually trades off is the amount of detail discarded during lossy compression in exchange for a smaller file. Whether the loss is perceptible depends heavily on the image itself:
- A busy photograph with fine texture (foliage, fabric, skin) can often tolerate more aggressive compression before the loss becomes visible, because the eye has less to compare against.
- A flat-color graphic, a screenshot containing text, or an image with large smooth gradients shows compression artifacts (banding, blockiness) much more readily at the same quality setting.
- Images that will be displayed large or full-bleed show artifacts more readily than the same image displayed small.
The right approach is testing your own representative images at a few quality levels and comparing file size against visible quality loss, rather than applying one fixed number across an entire image library and assuming it’s universally safe.
Responsive images
Format and compression only address one dimension of image weight. The other is serving an image sized appropriately for the device displaying it, since a 2,000px-wide image doesn’t need to be served in full to a 400px-wide mobile viewport.
srcsetandsizeslet you provide multiple resolutions of the same image and tell the browser which one to pick based on viewport width and pixel density, so a high-DPI phone gets a sharp image without a desktop user downloading unnecessarily large files.- The
<picture>element extends this further, letting you serve entirely different image sources (not just different resolutions of the same image) based on viewport size or format support, which is how the AVIF-then-WebP-then-JPEG fallback pattern described above actually gets implemented in markup.
Skipping responsive images is a common way sites end up serving a single large desktop-sized image to every device, which quietly inflates page weight on mobile far more than a format or compression choice would.
A combined format-fallback and responsive-sizing example looks roughly like this:
<picture>
<source type="image/avif" srcset="hero-480.avif 480w, hero-960.avif 960w, hero-1600.avif 1600w" sizes="100vw">
<source type="image/webp" srcset="hero-480.webp 480w, hero-960.webp 960w, hero-1600.webp 1600w" sizes="100vw">
<img src="hero-960.jpg" srcset="hero-480.jpg 480w, hero-960.jpg 960w, hero-1600.jpg 1600w" sizes="100vw" alt="Descriptive alt text" fetchpriority="high">
</picture>
The browser picks the first <source> it supports, then the matching width based on viewport and pixel density, and falls back to the plain <img> (JPEG in this case) if neither AVIF nor WebP is supported. Note the fetchpriority="high" on the fallback <img> tag: if this is your LCP element, that attribute applies regardless of which source the browser ultimately selects.
Lazy-loading
Native lazy-loading, the loading="lazy" attribute on an <img> tag, tells the browser to defer fetching an image until it’s about to enter the viewport, rather than fetching every image on the page immediately regardless of whether the user will ever scroll to it. For a long page with many below-the-fold images, this meaningfully reduces the amount of data fetched on initial load.
The one place this backfires is the LCP image, or any above-the-fold image. Applying loading="lazy" to your hero image or any other image visible without scrolling tells the browser to deliberately deprioritize a resource the user is looking at immediately. Rather than a blanket “lazy loading adds 500ms+ to LCP” figure, the honest framing is narrower and more useful: never lazy-load your LCP image or any above-the-fold image, because deprioritizing the fetch of something the user needs to see immediately works directly against the goal. For that specific image, use fetchpriority="high" instead, which signals the opposite: fetch this with elevated priority. The full mechanics of diagnosing and fixing LCP live in the Largest Contentful Paint (LCP): Causes and Fixes guide.
For everything below the fold, native lazy-loading has broad current browser support and is the simplest way to implement this without a JavaScript library.
Delivery: CDN and image services
Serving images through a CDN or a dedicated image-optimization service (rather than directly from your origin server) adds two benefits worth knowing about even without a full vendor comparison: geographic edge caching, so images load from a server physically closer to the visitor, and, with many image services, on-the-fly format and size conversion, so you can upload one source image and let the service generate AVIF/WebP variants at multiple sizes automatically rather than pre-generating and storing every combination yourself. This isn’t a requirement for good image optimization, a well-configured origin server with proper caching headers can also perform well, but it removes a category of manual work for sites with a large or frequently changing image library.
One more format worth a mention for the graphics category specifically: SVG. For logos, icons, and simple illustrations made of shapes rather than photographic detail, SVG is a vector format that scales to any size without the file-size growth a raster format like PNG shows as dimensions increase, and it typically produces a smaller file than a PNG for that kind of content. It’s not a substitute for AVIF/WebP/JPEG on photographic images, but it’s frequently the better choice for the graphics that PNG gets reached for by default.
Build-time image optimization
Everything above covers optimizing images as assets: format, compression, sizing, and loading behavior. There’s a separate, related lever: optimizing images as part of your build process, using bundler loaders (for example, webpack’s image-handling plugins) to automate compression and format conversion at build time rather than doing it manually per image. That’s a build-tooling concern rather than an image-format concern, and it’s covered in the Webpack Optimization for SEO: Bundle Size, Code-Splitting, and Loading Performance guide rather than here.
Testing image weight impact
PageSpeed Insights and Lighthouse both surface image-specific opportunities directly: “Serve images in next-gen formats,” “Properly size images,” and “Efficiently encode images” are standard Lighthouse audits that flag specific images on a page along with an estimated savings figure. The Network panel in Chrome DevTools, sorted by transferred size, is a fast way to see exactly which images are the heaviest on a given page load, which is often a more useful starting point than trying to optimize every image in a library at once. Start with the largest few, since a handful of oversized images typically account for a disproportionate share of total page weight.
Checklist
- Pick a format per image type using the decision framework above, not a blanket “always use WebP” rule.
- Treat compression ratio claims and browser support percentages as ranges to verify, not fixed constants.
- Test quality settings against your own representative images rather than applying one fixed number everywhere.
- Implement
srcset/sizesor<picture>so devices get an appropriately sized image, not just an appropriately compressed one. - Lazy-load everything below the fold; never lazy-load the LCP image or other above-the-fold images.
- Use
fetchpriority="high"for the LCP image if it’s an image element. - Check the Network panel or Lighthouse’s image audits to find your heaviest images and start there.