DEV Community

Cover image for Optimizing images for the web - an in-depth guide
Adrian Bece for PROTOTYP

Posted on • Edited on • Originally published at blog.prototyp.digital

Optimizing images for the web - an in-depth guide

Table Of Contents

Unoptimized (non-minified) images are one of the main causes of poor website performance, mainly on the initial (first) load. Depending on the resolution and image quality, you might end up with images that take up more than 70% of your total website size.

It's very easy for unoptimized images to end up on a production site and slow down its initial load considerably. Inexperienced devs usually aren't aware of this potential problem. They also aren't aware of a wide range of tools and approaches for optimizing images.

This article aims to cover most of the tools and approaches for optimizing images for the web.

Calculating JPG image file size

The uncompressed image size can be easily calculated by multiplying image width px value with image height px value and multiply the result by 3 bytes which is equivalent to 24 bits (RGB color system). We divide the result by 1,048,576 (1024 \* 1024) to convert the value from bytes to megabytes.

image_size = (image_width * image_height * 3) / 1048576
Enter fullscreen mode Exit fullscreen mode

For example, let's calculate file size for an uncompressed image that has 1366px x 768px dimensions.

1366 * 768 * 3 / 1048576 = 3Mb
Enter fullscreen mode Exit fullscreen mode

Considering that average website size today is between 2Mb and 3Mb, imagine having an image on your site that takes more than 80% the size of your site. 3Mb takes ages to load on slower mobile networks, so you might lose some traffic on your website if the user is waiting for your website to load and most time is spent on loading a single image. Scary thought, isn't it?

So what we can do to avoid having optimized images on the web but preserve the acceptable quality and resolution?

Online image optimization

If you are working on a simple static website that only has a handful of images that won't change often or won't change at all, you can just drag and drop your images in one of the numerous online tools. They do an amazing job at compressing images using various algorithms and are more than enough for simple projects.

Alt Text

Most notable websites, in my opinion, are:

Automated solutions

However, if you are working on more complex projects with multiple people and using a lot of images, optimizing each one as it is added to the project can become tedious. Also, there is a risk that some images may end up not optimized due to human error or some other factor.

On complex projects, it's common to use an equally complex build system like Gulp, Webpack, Parcel, etc. Image optimization plugins can be easily added to those build configs and fully automate the image optimization process. Images can be optimized as soon as they are added to the project.

Most notable plugin, in my opinion, is imagemin which can be easily integrated with any CLI or build tools:

Image loading optimization

We've looked at the image optimization strategies that reduce the file size by compressing the image without changing the image resolution and affecting image quality too much. Although optimizing image file reduces the file size of images considerably, having multiple optimized images (on the webshop catalog page for example) loaded all at once can have a poor effect on performance.

Lazy Loading

Lazy loading is a concept of only loading assets that are needed. In our case, only images that are currently within the user's viewport (screen) are loaded. Other images are not loaded until they appear within the user's viewport.

Although native Lazy loading has just been recently introduced to browsers, there have been many JavaScript-based solutions available.

Alt Text

Native Lazy Loading

<img src="image.jpg" loading="lazy" alt="Sample image" />
Enter fullscreen mode Exit fullscreen mode

JavaScript-based solutions

Most notable JavaScript-based solutions, in my opinion, are:

Progressive images

Although lazy loading does a great job performance-wise, looking at the problem from UX perspective we can see that the user is waiting for the image to load and looking at the blank space. On slow connections, downloading images can take ages. This is where progressive images come into play.

Basically, having a progressive image means that a low-quality image will be displayed to the user until a high-quality image has finished loading. A low-quality image has a considerably smaller file size due to the low quality and high compression rate, so this image will be loaded very fast. In between the low quality and high-quality image we can have as many images with varying quality as we need and we can load the higher quality image on each download.

Alt Text

Similarly to the article on Skeleton loading I've written, this technique gives the user an illusion of speed. User is looking at an image that is loading and becoming more clearer as it loads higher and higher quality image, instead of looking at the empty space waiting for something to happen.

This is an JavaScript implementation of progressive images: progressive-image

Responsive images

We also need to be careful of using properly-sized images.

For example, let's say we have an image that is 1920px maximum width on desktop , 1024px maximum width on tablet devices and 568px maximum width on mobile devices. Simplest solution would be to just use the 1920px image and cover all the cases, right? In that case, an user on a smartphone with slow and unreliable connection would have to wait ages for the massive image to download and we'd be back at the square one of the problem.

Luckily for us, we can use picture element to tell the browser which image to dowload, depending on the media query. Although this element is supported by more than 93% of globally used browsers, it has a pretty simple fallback with img element already inside it.

<picture>
  <source media="(min-width: 1025px)" srcset="image_desktop.jpg">
  <source media="(min-width: 769px)" srcset="image_tablet.jpg">
  <img src="image_mobile.jpg" alt="Sample image">
</picture> 
Enter fullscreen mode Exit fullscreen mode

Using CDN

CDN services like Cloudinary and Cloudflare can perform image optimization on the server and serve the optimized images to the user. If your website uses a CDN, it's worth looking into asset optimization options. This allows us not to worry about image quality optimization at all, and have all optimizations done server-side. We only need to look into optimizing image loading by either using lazy loading or progressive images.

WebP image format

WebP image format is developed by Google and is an image format specifically optimized for the web. According to the canIUse data, current browser support for WebP image format is at around 80% which is great. Luckily, implementing a fallback to standard jpg image with img element inside picture element is easy.

<picture>
  <source type="image/webp" srcset="image.webp" />
  <source srcset="image.jpg" />
  <img src="image.jpg" alt="Sample image" />
</picture>
Enter fullscreen mode Exit fullscreen mode

Although there are numerous online file format converters that can convert images to WebP format, CDN services can easily perform format conversion server-side.

Optimization for high pixel density screens

This is more UX improvement rather than performance, but it's also important to take into account devices that have higher pixel density.

For example, let's assume that we are displaying an 768px x 320px banner image on 768px screen. But the screen has 2x density and the px width is actally: 2 x 768 = 1536px. Basically, we are stretching 768px over 1536px and this leads to blurry image on high pixel density devices.

In order to fix that, we need to serve an image optimized for high pixel density screens. We need to create separate images that are 2 times or 3 times the resolution of regular screens and use the srcset attribute with 2x tag marking for higher resolution image.

<img src="image-1x.jpg" srcset="image-2x.jpg 2x" alt="Sample image" />
Enter fullscreen mode Exit fullscreen mode

Example - Responsive WebP/PNG images with high-density screen support

<picture>
    <source srcset="./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x" type="image/webp" media="(max-width: 440px)">
    <source srcset="./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x" media="(max-width: 440px)">
    <source srcset="./images/webp/hero-image-550-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x" type="image/webp" media="(max-width: 767px)">
    <source srcset="./images/minified/hero-image-550-min.png 1x, ./images/minified/hero-image-960-min.png 2x" media="(max-width: 767px)">
    <source srcset="./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x" type="image/webp" media="(max-width: 1023px)">
    <source srcset="./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x" media="(max-width: 1023px)">
    <source srcset="./images/webp/hero-image-760-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x" type="image/webp" media="(max-width: 1919px)">
    <source srcset="./images/minified/hero-image-760-min.png 1x, ./images/minified/hero-image-960-min.png 2x" media="(max-width: 1919px)">
    <source srcset="./images/webp/hero-image-960-min.webp" type="image/webp">
    <source srcset="./images/minified/hero-image-960-min.png">
    <img  src="./images/minified/hero-image-960-min.png" alt="Example">
</picture>
Enter fullscreen mode Exit fullscreen mode

Conclusion - Optimization priority

  1. Use optimized images (optimized by automated build tools, online services or CDN)
  2. Use lazy loading (JS solution until native becomes more supported)
  3. Optimize images for high pixel density screens
  4. Use WebP image format
  5. Use progressive images

Optional: Remember to serve images (and other static assets) over CDN if you are able to.

Alt Text


These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

Buy Me A Coffee

Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.

Top comments (45)

Collapse
 
gtb3nw profile image
GTB3NW

Hey Adrian, have a look at conditional webp returning. You can see browser headers for Accept and if webp appears there when the request for an image is made, you can conditionally return the webp to the client. Browsers will render the webp instead of a jpeg for example. No code changes required, just pipeline changes to create a webp alongside your jpegs, then add the condition to your webserver.

Collapse
 
adrianbdesigns profile image
Adrian Bece • Edited

I had no idea it can be implemented server-side as well. Thank you very much for your suggestion.

I found this guide, looks simple and straightforward:

alexey.detr.us/en/posts/2018/2018-...
github.com/uhop/grunt-tight-sprite...

Collapse
 
eionrobb profile image
Eion Robb

This can cause issues when saving images offline (eg for building offline-capable PWA's) unless you're careful to change the filename extension when you store it offline, eg storing a .jpg file with webp contents can trigger security checks since the mime-type of the contents of the file doesn't match with the mime-type of the file extension (extension sniffing has to be used since there's no mime-type headers for file://)

Collapse
 
moviezdotbiz profile image
Moviez.biz

Hey Adrian in my case i am running an blogger blog where it's full of YouTube videos, what I am doing is using staticaly cdn where I can insert there cdn.statically.io/img/:image_url to optimize images

Collapse
 
adrianbdesigns profile image
Adrian Bece

CDNs that optimize images right out of the box like Cloudinary and Statically are awesome. In cases like yours, CDNs really take a weight off our shoulders.

Are you doing any loading optimization like lazy loading the images?

Collapse
 
moviezdotbiz profile image
Moviez.biz

No because i am using this blogger platform with a amp theme,still google is not officially enabled amp for blogger platform,struggling a bit to implement things for a fully video blog like mine in i think you know if i add a video from YouTube as blog post it will have 2 URLs one is for image and the second one for YouTube video, in my case image URL is used for thumbnails in homepage and when sharing in social media it will be used

I have spoke with the lots of amp theme creators still they don't know how to make valid YouTube amp post.

Collapse
 
rnanwani profile image
Rahul Nanwani

CDNs that optimize images on-the-fly really ease the burden, and no doubt Cloudinary and Statically do a great job there, but have you tried ImageKit.io? It's easy to use and offers a more cost-effective solution.

Collapse
 
moviezdotbiz profile image
Moviez.biz

If you know how to use lazy loading in blogger please tell me how to implement it

Collapse
 
jannikwempe profile image
Jannik Wempe

Thanks for that awesome article giving a nice overview.

Would appreciate a full example combining a picture element for different screensizes, formats (WebP) and pixel densities. As far as i understood this article, that should be the best solution (adding CDN, lazy loading and so on...).

Collapse
 
adrianbdesigns profile image
Adrian Bece • Edited

Thank you very much.

I've copied this directly from one of my projects. Hopefully, it will be helpful to you. In the article, I didn't want to overwhelm the users with the full example, but I might include it as a bonus.

<picture>
    <source srcset="./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x" type="image/webp" media="(max-width: 440px)">
    <source srcset="./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x" media="(max-width: 440px)">
    <source srcset="./images/webp/hero-image-550-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x" type="image/webp" media="(max-width: 767px)">
    <source srcset="./images/minified/hero-image-550-min.png 1x, ./images/minified/hero-image-960-min.png 2x" media="(max-width: 767px)">
    <source srcset="./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x" type="image/webp" media="(max-width: 1023px)">
    <source srcset="./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x" media="(max-width: 1023px)">
    <source srcset="./images/webp/hero-image-760-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x" type="image/webp" media="(max-width: 1919px)">
    <source srcset="./images/minified/hero-image-760-min.png 1x, ./images/minified/hero-image-960-min.png 2x" media="(max-width: 1919px)">
    <source srcset="./images/webp/hero-image-960-min.webp" type="image/webp">
    <source srcset="./images/minified/hero-image-960-min.png">
    <img  src="./images/minified/hero-image-960-min.png" alt="Example">
</picture>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
malvoz profile image
Robert Linder

Although this post mentions native lazy-loading, seeing as it is already available in Chrome for desktop and Android (that's over 50% browser support! caniuse.com/#feat=loading-lazy-attr) - I think it'd be wise to conditionally load JS library for lazy-loading only when native isn't available, more info on that here: web.dev/native-lazy-loading

Collapse
 
malvoz profile image
Robert Linder • Edited

Also, to improve performance and avoid content shifts - remember to set the width and height attributes for <img>, more info in this video from Jen Simmons of the CSS Working Group: youtube.com/watch?v=4-d_SoCHeWE

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you very much for the info on implementing the native lazy loading with fallback.

Collapse
 
allison profile image
Allison Walker

Interesting list!

Do you know of an image compressor that also converts .jpg into webp? You can also install ImageOptim on your computer and compress images before uploading them.

I'm also curious why you only include the picture element and not img srcset? From my reading of the MDN spec, picture is for the art direction problem, and img srcset is for image resolutions.
developer.mozilla.org/en-US/docs/L...

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you very much.

I've used picture element because it offers me more control over which image is being loaded. I guess I prefer explicitly telling the browser what to load. I've found it to work great with both art direction and performance. Also, picture element allowed me to support WebP with a really nice fallback.

I think it's just a matter of use-cases and personal preference.

Collapse
 
allison profile image
Allison Walker

What is your thought about adding images via background in CSS?

background-image:url(img/myimage.jpg);

Can images added via this method be optimized using picture or srcset, or do you just have to optimize them by resizing and compressing? thx

Thread Thread
 
adrianbdesigns profile image
Adrian Bece

For this method, my primary concern is accessibility. Without the alt tag and any HTML, a simple div with background-image doesn't have good accessibility.

Another issue is that you'd have to use an image aspect ratio to calculate the padding (spacing) needed for the background image which may lead to code bloat if you have lots of images with various aspect ratios.

As for optimization, we need to optimize them by compression and resizing (if needed).

Collapse
 
ut4utc profile image
ut4utc

I've been wondering - how much time would love to confess about the fact that lazy loading is cool? Well, because it is very shabby looking, permanent screen blinking, the gray squares, which is 2000 on dialup modem sit and wait for the download. Well, that's pathetic! Did the developers do not have own opinion and remains only an opinion which they impose on internet giants?

Collapse
 
pxnji profile image
Panji Lesmana

I'm using this service statically.io/imgpx, It has a lot of features I need. And now it supports custom domain.

Collapse
 
adrianbdesigns profile image
Adrian Bece

Someone else also mentioned statically. I might use it on my personal projects if needed. Are there any downsides to using that particular service? Any issues that you ran into during development?

Collapse
 
pxnji profile image
Panji Lesmana

Sometimes there's an error on some files that are cache from Github. So I must rename it on Github and recache it again. I still don't know why.

Collapse
 
weakish profile image
Jang Rush

Hi Adrian, I'd like to translate this excellent guide to Chinese. The translated text will be published at nextfe.com
Can you give me the permission?

Collapse
 
adrianbdesigns profile image
Adrian Bece

Hello Jang. Can you please contact me via email? My email is adrianbece@live.com

I would like to know more about your website and what is your publishing policy. Do you link to the original article and is the author credited?

Thank you

Collapse
 
weakish profile image
Jang Rush

Oh, I forgot to inform you that I recently finished the translation and published it at: nextfe.com/optimize-image/

Thanks again for sharing these excellent guide.

Thread Thread
 
adrianbdesigns profile image
Adrian Bece

Awesome, thank you really much

Collapse
 
weakish profile image
Jang Rush

The author is credited at the beginning of the translation, and is linked back to the original article. I've sent you an email with details. :-)

Collapse
 
hte305 profile image
Ha Tuan Em

Thanks for that article. I hope you will post more new idea for old solution !
Thanks you so much !

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you very much, hope you'll find it useful in your projects

Collapse
 
siddharthvenkatesh profile image
Siddharth Venkatesh

Excellent article. Thank you for writing this.

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you very much