Image Optimization for the Web: Formats, Sizing, and Loading

Images are often the biggest bytes on a page and one of the easiest wins for performance. This guide explains which formats to use, how to size images correctly, how to implement responsive images (srcset/sizes), when to lazy-load, and how to avoid CLS.

By Olamisan~12 min readPerformance

Fast wins (do these first)

If you want results quickly, start here:

  1. Stop overserving: deliver images close to their displayed size.
  2. Use modern formats: AVIF/WebP where possible.
  3. Don’t lazy-load the hero image (often your LCP element).
  4. Reserve space for every image to prevent CLS.
  5. Cache aggressively for static images.

Related: Core Web Vitals quick wins

Choosing formats: AVIF, WebP, JPEG, PNG, SVG

AVIF

Best compression for many photos and gradients. Great when your pipeline supports it. Use WebP/JPEG as fallback if needed.

WebP

Excellent default for modern browsers, often a strong improvement over JPEG/PNG.

JPEG

Still useful as a safe fallback for photos. Use progressive JPEG if your pipeline supports it.

PNG

Use when you need transparency and lossless quality (icons or UI assets). Otherwise, prefer WebP/AVIF.

SVG

Best for logos, icons, and simple illustrations. Keep SVG clean (avoid huge embedded data).

Practical default

  • Photos: AVIF → WebP → JPEG
  • Icons/illustrations: SVG
  • Transparency-heavy UI: WebP/PNG

Sizing correctly (the #1 mistake)

The most common performance mistake: serving a massive image for a small display slot. Fixing sizing usually gives the biggest win.

Rules of thumb

  • Never ship a 2000px image to render at 320px.
  • For retina screens, target ~2× display size (not 4×).
  • Keep hero images tight: large enough to look good, not larger than needed.

Example

If your image renders at 360px wide on mobile, typical srcset widths might include:

360w, 720w, 1080w

Responsive images: srcset + sizes

Responsive images let the browser pick the best file for the user’s screen. Use srcset to provide multiple sizes and sizes to describe layout.

Example: responsive <img>

<img
  src="/images/hero-720.webp"
  srcset="
    /images/hero-360.webp 360w,
    /images/hero-720.webp 720w,
    /images/hero-1080.webp 1080w
  "
  sizes="(max-width: 640px) 360px, (max-width: 1024px) 720px, 1080px"
  width="1080"
  height="720"
  alt="A descriptive hero image"
  loading="eager"
  decoding="async"
/>

Tip: if you’re using Next.js, next/image can handle responsive sizes for you.

Loading strategy: priority, preload, lazy-load

1) Don’t lazy-load the hero image

If the hero image is your LCP element, lazy-loading delays it and hurts LCP. Load it eagerly and consider preloading.

2) Lazy-load below-the-fold images

Use loading="lazy" for images outside the initial viewport.

3) Use decoding async

decoding="async" can reduce main-thread work while decoding images.

Practical loading rules

  • Above the fold: eager (and maybe preload)
  • Below the fold: lazy
  • All images: decoding async + correct dimensions

Avoid CLS: reserve space

Images cause CLS when the browser doesn’t know their dimensions and the layout shifts as they load. Always reserve space with width/height or a stable aspect ratio.

CLS fixes that work

  • Always set width and height on images (or use CSS aspect-ratio).
  • Reserve space for embeds/iframes and ads.
  • Avoid injecting UI above existing content after render.

Related: Core Web Vitals: CLS section

Caching & delivery

After sizing and formats, caching is the next big win. Static images should be cached long-term.

Practical delivery checklist

  • Use fingerprinted filenames for assets you can cache long-term.
  • Serve images from a fast origin or CDN when possible.
  • Enable compression where relevant (CDNs usually handle this).

Before-you-ship checklist

  • ✅ Correct format (AVIF/WebP preferred for photos)
  • ✅ Correct size (no overserving)
  • ✅ Responsive images (srcset + sizes) for layout-based sizing
  • ✅ Hero image not lazy-loaded (LCP-safe)
  • ✅ Below-the-fold images lazy-loaded
  • ✅ Dimensions reserved (avoid CLS)
  • ✅ Caching headers/CDN where appropriate

Related: Core Web Vitals quick winsNext.js metadata guide

FAQ

Should I use AVIF or WebP?

Use AVIF when supported in your pipeline, with WebP as fallback. Keep JPEG/PNG for compatibility and special cases.

What’s the biggest image performance mistake?

Overserving: sending images far larger than the display size. Responsive images usually fix this.

Should I lazy-load all images?

Lazy-load below-the-fold images, but do not lazy-load the hero image if it’s the LCP element.

How do images cause CLS?

CLS happens when layout shifts after images load. Reserve space with width/height or a stable aspect ratio.

Related reading