DEV Community

Cover image for A Few Things I’ve Learned About Images and Performance
Jessica aristimuño
Jessica aristimuño

Posted on

A Few Things I’ve Learned About Images and Performance

Sometimes I forget how much the browser can do for us

When I think about web performance, I usually think about things like bundle sizes, caching and rendering strategies. All important, and all topics that could easily turn into articles of their own.

But sometimes I go back to something much simpler: HTML.

A few attributes can help the browser decide when to load an image, how much space to reserve for it, and which file to choose. Less custom logic for me, more work for the browser. Seems fair.

I’ve been thinking about these details more since I started using AI tools. I can ask an agent to build a component and get something that looks good in minutes. But did it add image dimensions? Is the hero image being lazy-loaded? Is that tiny card downloading a massive image?

The agent might have thought about all of this. Or maybe not. Either way, I still need to check.

So this article is partly a reminder for myself, and maybe for you too: getting a component to look right is only part of the job.


Not every image needs to load right now

Imagine a page with a hero image and a long list of cards. When someone opens it, they can see the hero and maybe the first few cards. The images near the bottom can wait a little.

It’s a bit like going to the gym. I usually start with some mobility and a warm-up before getting into the workout. I don’t put on my lifting belt during the warm-up if I’m not lifting heavy yet. I grab it later, when I need it. 🏋️‍♀️

workout

For images further down the page, we can give the browser a similar hint:

<img
  src="/images/card.webp"
  alt="A person working on a laptop"
  loading="lazy"
  width="800"
  height="600"
/>
Enter fullscreen mode Exit fullscreen mode

With loading="lazy", the browser can wait until the image gets closer to the viewport before loading it.

I find this especially useful for longer pages with lots of images. But the word closer matters here: lazy loading doesn’t necessarily wait until the exact moment an image appears on screen. The browser decides when to start fetching it.

Of course, I wouldn’t put this on every image and call it a day. The hero image is right there when the page opens, so delaying it could make the page feel slower.

If the hero image is the page’s Largest Contentful Paint element, or LCP, we want it to load early. LCP measures when the largest visible piece of content finishes rendering.

<img
  src="/images/hero.webp"
  alt="A person learning at their desk"
  width="1600"
  height="900"
  fetchpriority="high"
/>
Enter fullscreen mode Exit fullscreen mode

The browser already has its own way of prioritising resources. fetchpriority just lets us give it some extra context.

But I’d keep that hint for the images that really need it. If everything is a priority, we’re back where we started.


The little jump nobody asked for 👀

Something else I always notice is when I’m reading a page and the content suddenly moves because an image has loaded above it.

I was reading that. Where did it go?

Looking around

Adding width and height to an image helps the browser understand its aspect ratio before the file loads. That way, it can reserve the space it needs:

<img
  src="/images/card.webp"
  alt="A person working on a laptop"
  width="800"
  height="600"
  loading="lazy"
/>
Enter fullscreen mode Exit fullscreen mode

The image can still be responsive:

img {
  max-width: 100%;
  height: auto;
}
Enter fullscreen mode Exit fullscreen mode

This is one of those small things that’s easy to miss because the page might look completely fine once everything has loaded. But for the person using it, that little jump can be annoying, especially if it moves a button just as they’re about to click it.


One image, different screens

What happens when the same image is available in different sizes?

Let’s say we have the same image in three sizes:

<img
  src="/images/card-800.webp"
  srcset="
    /images/card-400.webp 400w,
    /images/card-800.webp 800w,
    /images/card-1200.webp 1200w
  "
  sizes="(max-width: 600px) 100vw, 800px"
  alt="A person working on a laptop"
  width="1200"
  height="900"
  loading="lazy"
/>
Enter fullscreen mode Exit fullscreen mode

In this example, I’m assuming the image takes the full screen width on smaller screens and is displayed at around 800px on larger ones.

Here’s what’s happening:

  • srcset tells the browser which image files are available.

  • sizes gives it an idea of how wide the image will appear.

  • Your sizes value should match your actual layout as closely as possible.

The browser can then choose a suitable file using that information, along with things like the screen’s pixel density.

The part that took me a moment to understand is that sizes doesn’t actually resize the image on the page. It’s a hint for choosing which file to download. CSS still controls how the image is displayed.

We give the browser the options and some context. It decides which file to use.


I recently came across sizes="auto"

When I first saw sizes="auto", I had to stop and look at what it actually does.

For lazy-loaded images, it lets the browser use the image’s layout width to help choose a file from srcset. This can be useful when an image sits inside a layout where its width is difficult to describe with a simple media query.

<img
  class="card-image"
  src="/images/card-800.webp"
  srcset="
    /images/card-400.webp 400w,
    /images/card-800.webp 800w,
    /images/card-1200.webp 1200w
  "
  sizes="auto, (max-width: 600px) 100vw, 800px"
  alt="A person working on a laptop"
  width="1200"
  height="900"
  loading="lazy"
/>
Enter fullscreen mode Exit fullscreen mode
.card-image {
  display: block;
  width: 100%;
  height: auto;
}
Enter fullscreen mode Exit fullscreen mode

The values after auto provide a fallback. They’re just an example here and should reflect how wide the image appears in your own layout.

We also still need to think about the image’s dimensions or aspect ratio so the browser can reserve space for it.

I like this because responsive layouts can get complicated. A card might have one width on mobile, another on desktop, and a completely different width when it lives inside a sidebar. Letting the browser use the actual layout width feels like a nice way to avoid some of that guessing.


What I’m taking away from this 💭

None of these attributes will magically fix a slow website. We still need to look at the page, understand what’s causing the delay, and measure whether a change actually helps.

But I like that a lot of this comes down to giving the browser better context.

Which images are actually needed now? How much space will they take? What sizes are available? Is there a resource that deserves a little extra attention?

Whether I’m writing the code myself or using an AI agent to help, these are things I still want to think about. It’s easy to focus on getting a component working and forget to check some of the smaller details.

And sometimes those details are just a few HTML attributes. Easy to add, and also easy to forget.

Is there an image attribute or performance detail you often find yourself checking? I’d love to know what’s on your list.


Further reading 📚

Top comments (0)