Images & Video

Image optimisation for web performance: the four things that matter

Most pages ship images several times larger than they display, in a format from 1992, with no dimensions set. Fixing those three things is most of the work.

3 minute read

On a typical content page, images account for the majority of transferred bytes. They are also the element most likely to be the Largest Contentful Paint, the metric that decides whether a page feels fast. Four decisions cover almost all of the available improvement.

1. Serve the right dimensions

The most common waste in web images is enormous by percentage and trivial to fix: shipping a 3000-pixel-wide image into a 700-pixel-wide slot.

The browser has to download all of it, decode all of it, and then throw most of it away. On a phone connection that is seconds of waiting for pixels that are never displayed.

Target roughly twice the CSS display width to look sharp on high-density screens, and no more. An image displayed at 700 px should be around 1400 px wide. Beyond 2× the returns are effectively zero and the cost is linear.

For images that display at different sizes on different screens, srcset and sizes let the browser choose:

<img
  src="photo-800.jpg"
  srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
  sizes="(max-width: 700px) 100vw, 700px"
  width="800" height="533"
  alt="Description of the image"
>

2. Serve a modern format

At equivalent perceptual quality, WebP is typically 25–35% smaller than JPEG, and AVIF is smaller still. On an image-heavy page this is the difference between a 4 MB and a 1.5 MB page.

Serve modern formats with a fallback so nothing breaks:

<picture>
  <source srcset="photo.avif" type="image/avif">
  <source srcset="photo.webp" type="image/webp">
  <img src="photo.jpg" width="800" height="533" alt="Description">
</picture>

The browser takes the first format it understands. Older clients get the JPEG.

And for anything that is a logo, icon, chart or diagram: use SVG. It is usually a couple of kilobytes, stays sharp at every zoom level, and can be styled with CSS.

3. Always set width and height

An image without dimensions has no reserved space in the layout. When it loads, everything below it jumps down. That is Cumulative Layout Shift, it is measurably annoying, and it is the reason you sometimes tap the wrong link on a page that is still loading.

Setting the width and height attributes lets the browser compute the aspect ratio and reserve the correct box before a single byte of image data arrives. Your CSS can still size the element responsively, the attributes are used for the ratio, not as a fixed size.

This costs nothing and is one of the highest-value one-line fixes available.

4. Load the right images at the right time

  • Lazy-load below-the-fold images with loading="lazy". The browser defers them until the user scrolls near them.
  • Do not lazy-load the hero image. It is the LCP element; deferring it directly delays the metric you are trying to improve. Mark it fetchpriority="high" instead.
  • Preload the LCP image if it is discovered late, for example, if it is set as a CSS background.
  • Avoid CSS background images for meaningful content. They are discovered later in the parse, they cannot be lazy-loaded intelligently, and they carry no alt text.

The things that are not worth the effort

  • Shaving the last 5% with an exotic encoder while shipping a 3000 px image into a 700 px slot. Fix the big thing first.
  • Quality 95. Nobody can see it, and it can double the file size versus quality 80.
  • Base64-inlining large images. It adds roughly 33% overhead, cannot be cached separately, and blocks the document. Reasonable for a 200-byte icon, harmful for a photograph.

A workable pipeline

  1. Start from the highest-quality original.
  2. Resize to the largest size you will actually serve, and generate two or three smaller variants for srcset.
  3. Export AVIF or WebP, plus a JPEG fallback, at quality ~80.
  4. Strip metadata, EXIF blocks and embedded colour profiles can be hundreds of kilobytes.
  5. Set width, height and alt on every image.
  6. Lazy-load everything except the hero.

Steps 2 to 4 are batch work. Bulk resizing, format conversion and compression all run locally in the browser, which for unreleased design work or client photography is the difference between a routine task and a data-handling decision.

Further reading

Do it hereImage CompressorCompress JPEG, PNG, WebP & AVIF images with quality control, no upload neededOpen

Written for NextTool. Every statement about a tool is checked against that tool's code, and corrections are dated above. How guides are written

performancewebcore-web-vitals