Skip to content
Wadhah Belhassen
← All articlesWeb Performance

Image Optimization Guide: WebP, AVIF, Lazy Loading, and Beyond

A technical image optimization guide — format selection, compression, responsive images, lazy loading, fetch priority, and the patterns that cut page weight in half.

Wadhah Belhassen2026-11-1312 min read
Image Optimization Guide: WebP, AVIF, Lazy Loading, and Beyond

Image optimization is usually the single biggest performance win available on any site. Most sites we audit are shipping images 5 to 20 times larger than necessary — costing 1 to 4 seconds of LCP and 30 to 70 percent of page weight that could be free.

This guide covers image optimization end to end. Format selection, compression, responsive images, lazy loading, fetch priority, CDN delivery, and the specific patterns that consistently move LCP and reduce page weight on real production sites.

The work is concrete. Most sites can cut total image weight 60 to 80 percent in a week of focused work, with proportional improvements to LCP and Core Web Vitals.

Why images dominate page weight

The average web page in 2026 is around 2 MB total. Images typically account for 50 to 70 percent of that weight.

This is not because images are inherently large. It is because most sites ship images:

  • In old formats (JPEG, PNG) instead of modern (WebP, AVIF)
  • At display sizes 3 to 10 times larger than rendered
  • Without compression
  • Loading eagerly when they should lazy-load
  • Without proper responsive sizing

Each of these is a fix worth between 10 percent and 70 percent page weight reduction. Combined, they typically cut total weight 60 to 80 percent.

We covered the broader Core Web Vitals impact in our Core Web Vitals deep dive guide. Images are usually the biggest contributor to LCP issues.

Section 1 — Format selection

Picking the right format is the first decision, and the biggest single weight reduction.

Modern formats — AVIF and WebP

AVIF: 40 to 60 percent smaller than JPEG at equivalent quality. Best compression. Slowest to encode. Now supported in 95+ percent of browsers.

WebP: 25 to 35 percent smaller than JPEG at equivalent quality. Faster to encode. Supported in 97+ percent of browsers.

Recommendation: serve AVIF when supported, WebP as fallback, JPEG only for legacy browsers.

Modern hosting platforms (Vercel, Cloudflare Images, Imgix) handle the format negotiation automatically.

When to use JPEG vs PNG vs SVG

JPEG: photos, gradients, real-world images. Use for content with continuous tones.

PNG: graphics with sharp edges, transparency, logos with limited colors. PNG-8 is small. PNG-24 is large.

SVG: icons, logos, simple illustrations. Vector format, scales without quality loss, often the smallest format for icons.

For 2026, AVIF or WebP should be the default for photos. SVG for icons. PNG only when transparency is needed and SVG won't work.

GIF is dead

Animated GIFs should be replaced with MP4 or WebM video. The format is decades old and 5 to 10 times larger than modern alternatives.

For animated content, use <video autoplay loop muted playsinline> with WebM source. The file is smaller and the playback is smoother.

Section 2 — Compression

Compression is where most format gains come from. The format change without compression is half the win.

Quality settings

For WebP and AVIF photos:

  • Quality 80-85: visually indistinguishable from the original in 90+ percent of cases
  • Quality 70-75: slightly compressed but still good for most use
  • Quality 60 and below: visible artifacts on photos, acceptable for thumbnails

Default to quality 80 to 85. Drop lower only if you need extreme compression.

Tools for compression

For one-off images: Squoosh.app (browser-based, free, supports all modern formats).

For batch compression: ImageOptim (macOS), Compressor.io, TinyPNG (paid but excellent).

For build pipelines: Sharp (Node), imagemin plugins for various build tools.

For hosted optimization: Cloudflare Polish, Vercel Image Optimization, Imgix, Cloudinary.

Lossless vs lossy

Lossless compression preserves every pixel exactly. Useful for logos and graphics where artifacts would be visible.

Lossy compression discards information. Acceptable for photos where the reduction is invisible at normal viewing.

Default to lossy for content images. Lossless for logos and graphics with sharp edges.

Section 3 — Responsive images

Serving the right size for the right viewport is one of the biggest wins.

The problem

A 4,000 x 2,000 image served to a mobile phone displaying it at 400 x 200 wastes 95+ percent of the downloaded data.

The solution — srcset and sizes

<img
  src="hero-1200.webp"
  srcset="
    hero-400.webp 400w,
    hero-800.webp 800w,
    hero-1200.webp 1200w,
    hero-2400.webp 2400w
  "
  sizes="(max-width: 768px) 100vw, 1200px"
  alt="Hero"
  width="1200"
  height="630"
>

The browser picks the right size based on viewport and pixel density.

sizes syntax

sizes describes how the image is rendered, not its file dimensions. The browser uses it to pick the right srcset candidate.

Common patterns:

  • Full-width hero: sizes="100vw"
  • Half-width on desktop, full on mobile: sizes="(max-width: 768px) 100vw, 50vw"
  • Fixed width: sizes="800px"

Getting sizes right is critical. Wrong sizes means the browser downloads a larger image than needed.

Picture element for art direction

For different crops or compositions per breakpoint (not just different sizes), use <picture>:

<picture>
  <source media="(max-width: 768px)" srcset="hero-mobile.webp">
  <source media="(min-width: 769px)" srcset="hero-desktop.webp">
  <img src="hero-desktop.jpg" alt="Hero">
</picture>

Useful when you want a different image composition (close-up vs wide shot) for mobile vs desktop.

Section 4 — Lazy loading

Don't load images that are not in the viewport.

Native lazy loading

<img src="below-fold.webp" loading="lazy" alt="...">

Supported in all modern browsers. Defers loading until the image approaches the viewport.

When to lazy-load

  • Below-the-fold images (always)
  • Long-scroll content (image galleries, product grids, blog content)
  • Images in collapsed sections

When NOT to lazy-load

  • The LCP image (load eagerly, with priority)
  • Images visible above the fold on first paint
  • Images that are immediately critical to the page experience

loading="eager" and the LCP

The LCP image should be loading="eager" (the default) and ideally have fetchpriority="high".

<img
  src="hero.webp"
  loading="eager"
  fetchpriority="high"
  alt="Hero"
  width="1200"
  height="630"
>

This is the single highest-leverage image optimization. Free LCP improvement of 200 to 800 ms in most cases.

Section 5 — fetchpriority for the LCP image

The fetchpriority="high" attribute tells the browser to fetch this resource ahead of others.

How it works

Without fetchpriority, browsers use heuristics to pick which resources to fetch first. The LCP image often loses to less-important resources.

With fetchpriority="high", the browser knows to prioritise this image. The result is dramatically faster LCP.

When to use it

  • On the LCP image (and only the LCP image)
  • For above-the-fold hero images that are likely the LCP

When NOT to use it

  • On below-the-fold images
  • On multiple images per page (defeats the purpose)
  • On non-LCP images that happen to be important

The browser's prioritisation is "first among equals". Marking many things as high priority eliminates the signal.

Section 6 — Dimensions and aspect-ratio

Images without dimensions cause Cumulative Layout Shift. Always specify dimensions.

width and height attributes

<img src="hero.webp" width="1200" height="630" alt="...">

The browser uses these to reserve space immediately. When the image loads, no shift.

CSS aspect-ratio

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

For responsive images, aspect-ratio reserves space without committing to fixed dimensions.

Why this matters

A page with images missing dimensions can have a CLS score of 0.3 to 1.0. Adding dimensions to every image typically drops CLS to 0.05 or below.

We covered the CLS mechanics in our Core Web Vitals deep dive guide.

Section 7 — Background images

CSS background images are easy to misuse for performance.

The hidden cost of background images

A background image loaded via background-image: url(...) is treated like any other image by the browser — fetched, parsed, painted. But it lacks the optimization features available to <img>:

  • No native lazy loading
  • No fetchpriority hint
  • No srcset for responsive sizing
  • No automatic format negotiation (unless your CDN provides it)

When to use <img> over background image

For any content image (hero, product, illustration), prefer <img> over background-image. The optimization features are too valuable to forgo.

When background images make sense

  • Decorative backgrounds that are not content
  • CSS gradients (always background, never img)
  • Repeating textures or patterns
  • Images that need to be behind other content with overlap

For these cases, the lack of optimization features is acceptable.

Section 8 — CDN delivery

A CDN serves images closer to the user, reducing latency.

Built-in optimization CDNs

Modern image CDNs handle most optimization automatically:

  • Cloudflare Images: format conversion, resizing, compression
  • Vercel Image Optimization: handled automatically with next/image
  • Cloudinary: most comprehensive, expensive at scale
  • Imgix: powerful, mid-priced
  • Bunny Images: cheaper alternative

For most SMEs, Cloudflare Images or Vercel handles this without per-image effort.

Origin shielding

CDNs cache images at edge locations. For images that are not yet cached at a specific edge, the CDN fetches from your origin.

A "shield" layer between the edge and origin reduces origin requests dramatically — useful for sites with many edge locations and a single origin server.

HTTP/2 and HTTP/3

Modern protocols multiplex multiple image requests over a single connection. This eliminates the "6 concurrent connections per origin" limit that hurt HTTP/1.1 sites.

Most modern CDNs serve HTTP/2 or HTTP/3 by default. Verify with curl -I --http2 https://your-cdn.com/image.webp.

Section 9 — Image-heavy pages — gallery and product grids

Some pages have dozens of images. Optimization patterns specific to these.

Pagination over infinite scroll for SEO-critical pages

Infinite scroll loads more images as users scroll. SEO-wise, search engines often fail to crawl beyond the initial set.

For product catalog and gallery pages, paginate at 24 to 48 images per page. SEO-friendly, lower initial weight, fast load.

Use Intersection Observer for advanced lazy loading

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
});

For custom lazy-loading behaviour beyond loading="lazy", Intersection Observer gives you control.

Pre-load adjacent images

For galleries and carousels, pre-load the next and previous images so the transition feels instant.

<link rel="prefetch" href="next-image.webp">

A 14-day image optimization plan

If your site has unoptimised images, follow this sequence.

Days 1 to 3 — Audit. List every image on your top 10 pages. Note format, size, dimensions, lazy-loading status.

Days 4 to 6 — Format conversion. Convert all photos to WebP at quality 80-85. Use Squoosh or batch tool.

Days 7 to 9 — Sizing. Generate appropriate responsive sizes. Update srcset and sizes attributes.

Days 10 to 11 — Lazy loading. Add loading="lazy" to below-fold images. Add loading="eager" and fetchpriority="high" to LCP image.

Days 12 to 13 — Dimensions. Add width and height to every image. Set up aspect-ratio in CSS for responsive containers.

Day 14 — Measure. Re-run Lighthouse. Compare LCP, CLS, and total page weight to baseline.

Most sites see 50 to 80 percent total image weight reduction in this window, with LCP improvements of 1 to 3 seconds.

A real example — Marseille cosmetics image audit

We audited a Marseille cosmetics e-commerce site shipping 4.7 MB of images on the homepage. Format breakdown: JPEG and PNG only, no responsive sizes, no lazy loading, no dimensions specified.

After 10 days — WebP conversion, responsive sizes via srcset, lazy loading on below-fold images, fetchpriority="high" on hero, dimensions added everywhere — total image weight dropped to 680 KB. LCP improved from 4.2 seconds to 1.6 seconds. Conversion rate lifted 31 percent in the following 60 days. The full story is in our Marseille cosmetics case study.

Common image optimization mistakes

These are the patterns we see most often.

JPEG when WebP would work. Free 25 to 35 percent reduction. Almost universally skipped on older sites.

Single image size for all viewports. Mobile downloads desktop-sized images. Use responsive srcset.

Wrong sizes value. Causes browser to pick the wrong candidate from srcset.

Missing dimensions. Causes CLS spikes. Always specify width and height.

Lazy-loading the LCP image. Defers the most important image. Mark as eager and high priority.

Stock photo hero at 4 MB. Compress and resize before upload, not after.

Background images for content. Loses native optimization features. Use <img> for content images.

No CDN. Origin-served images are slower for geographically-distant users.

Frequently asked questions

Should I use AVIF or WebP?

Both. Serve AVIF when supported, WebP as fallback, JPEG only for legacy browsers. Modern image CDNs handle this automatically.

What quality setting for WebP?

80 to 85 is the sweet spot for photos. Visually indistinguishable from the original in most cases, with significant file size reduction.

How many responsive sizes should I generate?

Typically 3 to 5 widths spanning your common viewport sizes. For most sites: 400, 800, 1200, 2400 widths.

Is lazy loading still beneficial in 2026?

Yes. Native lazy loading is in every modern browser. Free LCP improvement for below-fold images.

Should I use next/image or custom image components?

For Next.js apps, next/image is the default and handles most optimization automatically. We covered the specifics in our Next.js performance best practices guide.

Do I need a paid image CDN?

For small sites, no. Vercel or Cloudflare's free tiers cover the essentials. For high-traffic sites or sites with many images, paid CDNs (Imgix, Cloudinary) deliver better optimization at scale.

Get an image optimization audit

We audit site images free of charge. Within 48 hours we deliver a per-image breakdown of waste and a prioritised optimization plan.

Book a free 30-minute audit. We screen-share, walk through your image footprint, and you leave with a clear action plan.

Or explore our Web Development service for the full system we run on performance-focused client accounts.

Want these strategies applied to your business?

30 minutes of free audit with concrete recommendations tailored to your business.