Practical image loading improvements for the web
Images are often the heaviest part of a page. The HTTP Archive image report showed them accounting for more than 60% of transferred page data when this article was written.
There is no single image optimisation switch. These are the changes I would check first, from loading behaviour to the file that eventually reaches the browser.
Lazy-load images below the fold
The loading="lazy" attribute tells the browser that an image or iframe may wait until the visitor approaches it. Chrome and Firefox supported native lazy loading in 2020, which made it a good replacement for a JavaScript library in many cases.
<img src="cat.jpg" loading="lazy"/>
Do not add it blindly to every image. A hero image visible on the first screen should normally load immediately.
Asynchronous decoding
decoding="async" asks the browser to decode an image without holding up other rendering work. It is a hint, not a guarantee, and the browser can still choose the behaviour it considers appropriate.
<img src="cat.jpg" decoding="async"/>
I treat this as a small optional hint, not as the main performance fix. File size and image dimensions usually matter more.
Cumulative Layout Shift (CLS)
Cumulative Layout Shift (CLS) measures unexpected movement of visible content. Images without known dimensions are a common cause: the browser lays out the page, the image arrives, and everything below it moves.
Include width and height attributes so the browser can reserve the correct aspect ratio before the file loads. CSS can still make the image responsive:
<style>
img {
max-width: 100%;
height: auto;
}
</style>
<!-- 16:9 aspect ratio -->
<img src="cat.jpg" width="640" height="360"/>
Responsive images
srcset gives the browser several versions of the same image and lets it choose the most suitable one. Density descriptors help with fixed display sizes; width descriptors describe files with different intrinsic widths.
<!-- density descriptor -->
<img src="cat.jpg"
srcset="cat.jpg,
cat_2x.jpg 2x"
/>
<!-- or width descriptor -->
<img src="cat.jpg"
srcset="cat-small.jpg 300w,
cat-medium.jpg 600w,
cat-large.jpg 900w"
/>
WebP images
Google first released WebP in 2010. Compared with equivalent JPEG or PNG files, it could often reduce file size by roughly 25–35%, although the real result depends on the image and encoder settings.
The cwebp command-line tool works well for a one-off conversion. For projects that already had a build pipeline, the Imagemin WebP plugin was easier to automate.
<img src="cat.webp" />
WebP support was not universal in 2020, so a <picture> element could provide a JPEG fallback (browser support):
<picture>
<source srcset="cat.webp" type="image/webp">
<source srcset="cat.jpg" type="image/jpeg">
<img src="cat.jpg">
</picture>
CDN for image delivery
An image CDN can resize, compress, and reformat an image for the requesting device, then serve it from a nearby edge location. That is useful when a site has many image sizes or user-uploaded files and generating every variant yourself becomes awkward.
Options available at the time included Imgix, Cloudinary, Akamai Image Manager, and ImageEngine. Image optimisation APIs included Kraken.io, Imagify, ShortPixel, Fastly Image Optimizer, and TinyPNG.
For a small site, I would start with correct dimensions, sensible compression, and srcset. A CDN is useful, but it is not required to serve images well.