What you'll learn
Quick Answer
Use srcset to offer several sizes and let the browser choose, sizes to tell it how large the image will display, picture for format fallbacks, and always set width and height to prevent layout shift.
The problem, in numbers
A photo straight from a camera might be 4000 pixels wide and several megabytes. Displayed in a 400-pixel column on a phone, the browser downloads all of it and throws most away.
The cost lands hardest exactly where it hurts most — on mobile data, on a slower connection, on the users least likely to wait. Images are typically the largest contributor to page weight, and the hero image is frequently the LCP element, so this is a performance problem and a Core Web Vitals problem simultaneously.
Three separable decisions: serving the right size, the right format, and loading at the right time.
srcset and sizes: right size
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
width="800" height="600"
alt="Students working in a computer lab">
srcset lists the available files with their actual widths in pixels, using the w descriptor.
sizes tells the browser how wide the image will be displayed: full viewport width below 600px, half above. Without it the browser assumes full width and over-downloads on desktop.
The browser then picks, accounting for screen density — a phone with a 2× display and a 400px slot needs the 800px file.
src remains as a fallback for very old browsers. And width and height are not about sizing here — they let the browser reserve space and prevent layout shift, which is the single highest-return CLS fix.
picture: right format and art direction
srcset chooses between sizes of the same image. <picture> chooses between genuinely different sources.
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" width="800" height="600" alt="...">
</picture>
The browser takes the first source whose type it supports, falling back to the img. Order matters — most efficient format first.
Modern formats are a substantial win: WebP is typically much smaller than an equivalent JPEG at similar quality, and AVIF smaller again. The fallback chain means you can use them without excluding anyone.
The other use is art direction — a wide landscape crop on desktop and a tighter portrait crop on mobile, where simply scaling the wide image would make the subject unrecognisably small:
<picture>
<source media="(max-width: 600px)" srcset="hero-portrait.jpg">
<img src="hero-wide.jpg" width="1600" height="600" alt="...">
</picture>
Right time: lazy loading and priority
<img src="below-fold.jpg" loading="lazy" width="400" height="300" alt="...">
loading="lazy" defers loading until the image approaches the viewport. One attribute, no JavaScript, supported everywhere that matters.
Do not lazy-load your hero image. This is the most common mistake with the attribute. Lazy loading the LCP element delays the very thing being measured, making the score worse. Lazy-load below-the-fold images only.
For the hero, do the opposite and raise its priority:
<link rel="preload" as="image" href="hero-800.webp">
<img src="hero-800.webp" fetchpriority="high" ...>
A simple rule: everything visible on arrival loads eagerly, everything below lazily.
Practical workflow
- Generate sizes automatically. Producing four variants by hand for every image does not survive contact with a real project. Build tools and image CDNs do this on demand.
- Compress properly. Quality around 80 is usually indistinguishable from 100 at a fraction of the size. Strip metadata.
- Use SVG for logos, icons and diagrams. Scales perfectly at any size and is usually tiny.
- Write real alt text. It matters for screen readers and for search. Describe the content; leave
alt=""for purely decorative images so they are skipped rather than announced. - Check on a real phone with throttling enabled. Desktop broadband hides every one of these problems.
If you do only one thing: set width and height on every image. It costs nothing and fixes the most common layout-shift problem on the web.
