How to Improve Your Image Performance

Photo by Dan Gold on Unsplash

How to Improve Your Image Performance

How to choose and use the prettiest pictures

Featured on Hashnode

The Exposition (What?)

Boy, do I love websites with great imagery. Pictures of landscapes. Pictures of cats. Pictures of pictures. Pictures of graphs. Putting images on a site can make it miles more interesting.

That's not the only thing great about using images, though. People with low-vision or a cognitive disability may find it difficult to read or process text on its own. Supplementing text content with a picture or graphic is great for helping all users experience what your site has to offer. Plus, images let me insert pictures of my dog Mayari anywhere I want. Like so:

The author's beautiful red Irish-doodle dog, Mayari.

So, yeah, images are pretty dope. But with great imagery, as they say, comes great responsibility.

The Rising Action (Why?)

Images can cause pretty unfortunate performance issues when used without care (anyone who knows their way around Google Lighthouse knows what I'm talking about). Most notably, images can have a significant impact on a page's Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS).

When a webpage is loading, a single prominent element can cause users to believe the entire page is slow even if every other element is ready to rock. It doesn't matter if 90% of your site is lightning-fast if your hero photo takes two seconds to appear. It's not until they see the picture that users believe the page is just about ready to be useful. The measurement of how bad this is is called the Largest Contentful Paint or LCP. LCP is the amount of time that passes before the largest area of the screen is rendered. Since image elements are often used at the top of pages to quickly grab users' interest, not optimizing them for performance can cause users to believe the site is too slow and leave.

Have you ever tried to click on a link right after a page loads only for the button to jump down causing you to click on an ad for travel insurance instead? You just experienced a high Cumulative Layout Shift or CLS. CLS is the quantification of how often users experience unexpected shifts on the page. Poorly handled images can cause these infuriating gotchas by spontaneously appearing and taking up space.

Having high LCP and CLS scores can have a significant negative impact on user stickiness/session duration. Luckily, there are some things we can do to ensure our users don't have to wait for that sick banner photo of a dog catching a frisbee.

The Climax (How?)

Use the best format

I'm gonna make this easy for all of us. If your image names don't already end in .webp, then fixing that is the first step. WebP offers all the benefits of the classic formats we know and love (looking at you, PNG and JPEG), but they're much better at lossy and lossless compression. What that means for you is that they're generally harder, better, faster, and smaller.

There are a ton of super easy ways to convert your existing images to WebP. Google "convert image to WebP" and take your pick. If you have an absolute ton to convert or want more granular control though, you can do it scriptomatically. I made my tool for converting and optimizing images (check it out) in Node using the npm package sharp.


Believe it or not, images on a web page can be too good. If an image is so high-resolution that you can make out each grain of sand in the sandcastle you built with your niece, it's way too high. The bad news: you need to compress that image. The good news: no one will notice if you do it right.

An expertly constructed sandcastle in front of a picturesque body of water.

Compressing your images can be as easy or as hard as you want. If you have a small number of images to compress, find a compressor online and drop them in. Typically you'll need to designate what level of quality you want. In this case, I recommend some good old-fashioned trial and error to find how much you can compress your image without a noticeable loss in quality. You can also do it scriptomatically, like I did here.

If you're set on going deep into image compression, you'll find there are multiple different algorithms and philosophies on how best to do it. Delving into that is more than I've ever needed, but I'm sure some of you have or will get down and dirty with it.

Use responsive image tags

Go ahead and let those pants out an extra inch 'cause this part is a bit beefy.

It truly, truly is not necessary for a mobile user to see the same size picture as a desktop user. Mobile devices typically take much longer to load images, and I promise that no one is going to zoom in close so they can see how many freckles you have. For this reason, we might as well have users on mobile devices see a smaller version of the image so they don't have to wait for the bulky desktop version. Images that behave differently based on screen size are called "responsive images."

HTML offers everything we need to serve responsive images, with no fancy-pants JavaScript required. There are a few ways to do it though, and it's important to know when to use each method.

  1. When the image is small enough to not need responsiveness

    • Use this for stuff like logos or icons that are always the same size. This method is what most of us are used to. Nice and safe.

        <img src="/assets/sandcastles_in_the_sand.webp"
             alt="An impressive sandcastle the author built with his niece. Features battlements and a moat."
             width="200" height="150"
  2. When the image needs responsiveness, but always looks the same

    • Make use of the srcset and sizes attributes.

    • src points to the version of the image to use if the browser doesn't support srcset (pretty much just Internet Explorer).

    • srcset lists all the images the browser can choose from, paired with each image’s width.

    • sizes holds the rules for when to display the images listen in srcset. A media condition followed by a size that corresponds to one of the images in srcset.

            alt="An overwhelmingly cute Irish setter puppy with red hair."
            srcset="puppy-doggy-600w.webp 600w,
                    puppy-doggy-900w.webp 900w,
                    puppy-doggy-1200w.webp 1200w,
                    puppy-doggy-1200w.webp 1200w, 2400w"
            sizes="(max-width: 600px) 600px,
                   (max-width: 900px) 900px,
                   (max-width: 1200px) 1200px,
            width="900" height="900"
  3. When different images need to be shown based on screen size

    • Use the <picture>, <source>, and <img> tags, along with the media and srcset attributes.

    • The browser picks the image URL to use from srcset, based on the first <source> with a media attribute value that matches the user's viewport width.

    • <img> will be used as a fallback if <source> isn’t supported.

            <source media="(min-width: 45em)"
                    width="600" height="600"
            <source media="(min-width: 32em)"
                    width="400" height="400"
            <img src="small-wolf.jpg"
                alt="A wolf scampers menacingly through the snow."
                width="300" height="300"

Lazy loading

Ok, talking about responsive imagery was a whole meal. Luckily, this next part is a snack.

When you visit a webpage, the browser asks and (hopefully) receives instructions about what to put on the page and how to do it. Then, it asks the server if it would please send over those images mentioned in the instructions it kindly shared. Instead of waiting for an immediate response (sometimes servers, like people, take a bit to get back to you), the browser presses on. Once it's ready to start painting on the content, it starts from the top and moves its way down to the bottom.

As we've discussed already, sometimes images take a sec (hopefully less) to render in. If an image is so big (or a server is so slow) that it still hasn't arrived by the time the browser wants to paint it in, the entire show stops and waits for it. Then users and Google Lighthouse all get upset.

But, because the page renders top to bottom and screens are only so big, we don't need to make users wait for all of the images on a page. If there's an image of a logo in the footer, we can tell the browser to not hold everything up. I know, that logo designed by your sister-in-law's dog groomer looks so professional and perfectly represents your brand. Everyone will appreciate it more if they don't have to wait for it.

If an image doesn't need to be displayed right away, the solution is easy. Slap loading="lazy" on that bad boy. This tells the browser not to wait, to keep calm, and carry on serving up the page.

<img src="vape-store-logo.jpg" loading=“lazy” alt="Victors Vapes logo." width="150" height="100">

Give all images width and height

Now, back to those pages that jump up and down as they load. We chatted for a bit about Cumulative Layout Shift (CLS) already. CLS from images can easily be addressed by slapping width and height attributes on your images. This will tell the browser how much space to put aside so that the image has room to pop in when it's ready.

    alt="An impressive sandcastle the author built with his niece. Features battlements and a moat."
    width="200" height="150"

The Falling Action (Summary)

As we've discussed, images are magnificent in how they can make websites more interesting and accessible, but they can also cause performance issues. To ensure the best experience for your beloved users, make sure your images:

  1. are in WebP format.

  2. are compressed.

  3. are responsive.

  4. are lazy-loaded

  5. have width and height attributes.

The Denouement

There you have it! I hope this information was helpful for all y'all using images to enhance your site's experience for your users.

Have a question about how to implement any of the above recommendations? Or maybe you've caught something you think is missing or incorrect? If any of this sounds like you, comment down below so we can chat about it!

My Node image optimizer

Google Lighthouse