Performance · Loading performance

Image optimization (formats, responsive)

Medium ⏱ 1h #images

← Performance

TL;DR — Images are usually the heaviest bytes on the page and the most common LCP element, so this is the highest-leverage loading work. Win on four axes: right format (AVIF/WebP over JPEG/PNG), right dimensions (srcset/sizes so phones don't download desktop images), explicit size (kill CLS), and priority (preload/fetchpriority the LCP image, lazy-load the rest).


🧠 Mental model

Never ship a bigger image than the device will display. That one rule generates most of the technique:

Wrong: one 2400px, 800KB JPEG served to every device
        → phone downloads 800KB to paint it at 375px  (≈ 6× waste)

Right:  AVIF, multiple widths, browser picks the smallest that fits
        srcset 480w/960w/1440w + sizes → phone grabs the 480w at ~30KB

Two independent questions the browser must answer before layout: which file (format + resolution — solved by srcset/sizes and `


```html
<!-- ✅ Preload the LCP hero so it isn't discovered late & loaded at low priority. -->
<link rel="preload" as="image" href="hero-960.avif" type="image/avif"
      imagesrcset="hero-480.avif 480w, hero-960.avif 960w" imagesizes="50vw" />

<!-- ✅ Below-the-fold images: lazy, and STILL sized to avoid CLS. -->

⚖️ Trade-offs

  • AVIF is smallest but not free. It encodes much slower than JPEG/WebP (a build-time cost) and can be slower to decode on low-end devices — occasionally hurting the paint it was meant to speed up. Measure on real hardware; WebP is often the pragmatic default with AVIF as the top <source>.
  • <picture> art direction vs srcset resolution switching solve different problems. Don't reach for <picture> when all you need is different sizes of the same image — plain srcset/sizes on `` is simpler and sufficient.
  • Quality vs bytes is perceptual, not linear. Dropping JPEG quality 90 → 75 often halves the file with no visible difference; below ~60 artefacts show. Tune per-image, and prefer an automated image CDN over hand-exported assets.
  • Don't over-engineer tiny images. A 3 KB icon doesn't need three formats and five widths. Spend the effort on the hero and the gallery.

💣 Gotchas interviewers probe

  • The image is usually the LCP element — so image optimisation is LCP optimisation. Candidates who treat images as a side concern miss where the metric actually lives.
  • Never lazy-load the LCP image. loading="lazy" on the hero delays your largest paint. It's the most common regression in this whole area.
  • Missing width/height → CLS. Even with lazy loading, always reserve space. Aspect-ratio boxes are non-negotiable.
  • sizes is what makes srcset work. Without it the browser assumes 100vw and downloads the largest candidate — people add srcset and wonder why mobile still over-fetches.
  • Images are discovered late and loaded at low priority by default. fetchpriority="high" + preload for the LCP; the browser won't prioritise it for you.
  • decoding="async" keeps image decode off the main thread so it doesn't block other rendering — cheap win on image-dense pages.
  • CDN/Accept negotiation — a good image CDN serves AVIF/WebP based on the Accept header automatically, so you don't hand-maintain <picture> fallbacks. Know that this exists.

🎯 Say this in the interview

"Images are usually the heaviest bytes and the most common LCP element, so this is the highest-leverage loading work. I optimise on four axes. Format: AVIF or WebP over JPEG/PNG via <picture> with fallbacks — AVIF is roughly half the size of JPEG. Dimensions: srcset with sizes so a phone downloads a 480-wide image, not the desktop 1440 — and sizes is the part people forget, without it the browser assumes 100vw and over-fetches. Stability: always set width and height or an aspect-ratio so the box is reserved before load, which kills CLS. And priority: the LCP hero gets preloaded with fetchpriority=\"high\" because images are discovered late and loaded at low priority by default, while everything below the fold gets loading=\"lazy\" — but never the LCP image itself, that's the classic own-goal. In production I'd lean on an image CDN doing Accept-based format negotiation rather than hand-maintaining all of this."

🔗 Go deeper

Primary source — the single best place to go deeper web.dev: images
ESC

Type to search across every question bank.

navigate open 3,610 questions indexed