Image optimization for the web: a practical guide
Images are usually the heaviest part of a page. Resize them, compress them, serve modern formats and the right size per screen, and load them in the right order.
On most websites, images account for more bytes than everything else together. A single photo straight from a camera or a design tool can outweigh the HTML, CSS and JavaScript of the whole page. That makes images the first place to look when a site is slow, and the place where a small effort pays the most.
Work through these steps in order. Each builds on the one before.
1. Resize to the size you display
A 4000 pixel photo shown in an 800 pixel slot wastes more than 90 percent of its pixels. The browser downloads all of them and then throws most away.
Export images at the largest size they will be displayed, allowing for high density screens. For a slot that is 800 CSS pixels wide, 1600 pixels is plenty.
2. Compress
After resizing, reduce the quality setting until you can see a difference, then go back one step. For photos, a quality of 75 to 85 is usually indistinguishable from the original. Squoosh and ImageOptim do this by hand, and sharp does it in a build script.
Remove metadata as well. Camera details and embedded thumbnails add weight and can reveal a location.
3. Use modern formats
WebP and AVIF give much smaller files than JPEG and PNG at the same quality. Serve them with a fallback for older browsers. The details are in WebP and AVIF: modern image formats with a fallback.
4. Send each screen the right size
A phone does not need the image a desktop monitor needs. With srcset and sizes, the browser chooses.
<img
src="/img/room-800.webp"
srcset="/img/room-480.webp 480w, /img/room-800.webp 800w, /img/room-1600.webp 1600w"
sizes="(min-width: 60rem) 50vw, 100vw"
alt="A bright living room with a grey sofa"
width="1600"
height="1067"
loading="lazy"
/>
srcsetlists the files and their widths.sizessays how wide the image is displayed: half the viewport on large screens, the full width otherwise.
The browser combines the two with the screen's pixel density and downloads one file, the smallest that still looks sharp.
5. Reserve the space
Always set width and height. The browser then knows the shape of the image before it loads, and the page does not jump when it arrives.
6. Load in the right order
- The main image at the top loads first. Give it
fetchpriority="high"and do not lazy load it. - Everything below the first screen gets
loading="lazy".
More on this in native image lazy loading.
7. Cache and deliver
- Put a content hash in file names and cache them for a year. A changed image gets a new name, so nobody sees a stale copy.
- Serve images from a CDN, close to the visitor.
Automate it
Nobody keeps doing steps 1 to 4 by hand. Choose one of two approaches:
- A build step. A script generates the sizes and formats from the originals on every build. This site works that way.
- An image CDN. You upload one original, and the service resizes, converts and caches each variant on request.
Either way, an editor uploads one good image and the pipeline handles the rest.
What to expect
| Step | Typical effect |
|---|---|
| Resizing oversized images | The largest single saving |
| Compression | 30 to 60 percent smaller files |
| Modern formats | A further 25 to 50 percent |
| Responsive sizes | Phones download a fraction of the desktop bytes |
| Lazy loading | Far fewer requests on first load |
Together these often cut image weight by 70 percent or more and bring a clear improvement in Largest Contentful Paint. To see where your own page stands, measure it with Chrome DevTools.