Images

Ship the smallest image that still looks right on the screen it lands on — and stop images from moving the page.

06of 16
≈ 13Min read
5Parts

Images are most of the weight

On a median page, images are the largest share of the bytes and usually the LCP element too. They are also the easiest win in performance, because almost every page ships them two or three times larger than the screen can show.

Three decisions do nearly all the work: the format, the size, and whether the browser knew the shape before the bytes arrived.

Format

offer the new formats, keep the old one
<picture>
  <source srcset="/hero.avif" type="image/avif">
  <source srcset="/hero.webp" type="image/webp">
  <img src="/hero.jpg" width="1200" height="600" alt="…" fetchpriority="high">
</picture>
FormatTypical size vs JPEGUse it for
AVIF≈ 50–65 % smallerphotographs — best compression, slower to encode
WebP≈ 30–40 % smallerthe safe default, supported everywhere
JPEGbaselinethe fallback
PNGmuch largeronly when you need lossless or real transparency at sharp edges
SVGtinylogos, icons, anything geometric — and it never blurs

Size — let the browser choose

srcset lists what exists and how wide each one is. sizes tells the browser how wide the image will be in CSS pixels at this viewport — it has to know that before layout, which is why you have to say it. The browser then multiplies by the device pixel ratio and takes the smallest candidate that is big enough.

Beyond about 2× the return is invisible. A phone reporting DPR 3 does not need a 1170-wide image for a 390 px slot; cap at 2 and nobody will ever see the difference.

srcset and sizes
<img
  srcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w, /hero-2000.avif 2000w"
  sizes="(max-width: 700px) 100vw, 60vw"
  src="/hero-800.avif" width="1200" height="600" alt="…">

Shape — before the bytes

width and height attributes are not sizing; they are an aspect ratio, and CSS height: auto keeps them from fighting your layout. With them the browser reserves the right box immediately and nothing jumps when the image arrives. Without them you have the layout shift from the previous lesson.

css
img { max-width: 100%; height: auto; }   /* the two lines every stylesheet needs */

The rest of the attributes

And one warning: preload every image and you have preloaded nothing. Priority is relative; marking everything high is the same as marking nothing.

AttributeMeaning
loading="lazy"Do not fetch until it is near the viewport. Below the fold only.
fetchpriority="high"Jump the queue. For the LCP image, and for nothing else.
decoding="async"Do not block the next paint while decoding
sizes="auto"On a lazy image, let the browser use the real laid-out width — it already knows it by then

Practice