Modern Image Optimization: Stop Hurting Your Site Speed

By LearnWebCraft Team11 min readIntermediate
modern image optimizationhtmlresponsive imageswebpaviflazy loading

Let’s be real for a second. We’ve all been there. You click a link, and the page just… hangs. The text loads, but you’re staring at that dreaded, giant white box where an image is supposed to be. You wait. You tap your fingers. You start to wonder if your internet is broken.

Nope. Your internet is fine. It's just a glorious 5MB hero image of a cat, lovingly uploaded straight from a DSLR without a second thought.

Listen, I’ve made this mistake. You’ve probably made this mistake. And honestly, it’s costing us. Slow sites frustrate users, they absolutely tank SEO rankings, and they just feel unprofessional. But here's the good news: the days of just dragging your JPG through some crusty old compression website are over. We have powerful, modern image optimization tools baked right into HTML itself.

It's high time we started using them.

Why We Can't Ignore This Anymore

Before we dive into all the cool HTML tags, let's just take a moment to talk about the why. Because, let's face it, heavy images are the number one cause of slow websites. Period. End of story.

And it’s not just about a user's patience, either. Google cares—a lot. Your site’s Largest Contentful Paint (LCP), one of the big Core Web Vitals, is almost always your main banner image. A slow-loading image right there at the top means Google sees your site as slow, and your ranking is going to suffer for it.

So, yeah, better images mean happier users and better SEO. It’s a total win-win.

Meet the New Kids on the Block: WebP & AVIF

For years, our world was pretty simple: JPG for photos, PNG for anything with transparency, and GIF for... well, for memes, obviously. But these formats are getting old. They’re like flip phones in an iPhone world—they still work, but we can do so much better.

Enter the next-generation formats: WebP and AVIF.

  • WebP: This one's from Google. It offers significantly smaller file sizes than JPG and PNG for basically the same quality. Plus, it supports transparency and animation. Browser support is fantastic these days, as you can see over on CanIUse.
  • AVIF: This is the new challenger on the block. It often provides even better compression than WebP, especially for those really high-detail images. Support is growing like crazy and it's already in all the major browsers.

"But wait, what if a user has some ancient browser from 2010?" I can almost hear you asking. That, my friend, is where the magic of the <picture> element comes in.

The <picture> tag is brilliant. It lets you provide a whole list of different sources for an image, and the browser simply picks the very first one on the list it supports. It's such a beautiful, elegant solution to an old problem.

<picture>
  <!-- The browser will try this first. If it supports AVIF, it stops here. -->
  <source srcset="image.avif" type="image/avif">

  <!-- If it doesn't know AVIF, it'll try WebP next. -->
  <source srcset="image.webp" type="image/webp">

  <!-- The fallback for old browsers that support neither. This is required! -->
  <img src="image.jpg" alt="A descriptive alt text is crucial for accessibility." width="800" height="600">
</picture>

Just look at that. You’re serving the tiniest, most modern file format to browsers that can handle it, while still providing a safe, reliable fallback for everyone else. This is progressive enhancement at its absolute finest.

Responsive Images: Stop Sending a Truck When a Bike Will Do

Okay, this is the big one. And it's a mistake I still see all the time. A developer uploads a massive, 2000px-wide image and just shrinks it down with CSS for mobile screens.

That’s basically like making someone on a tiny phone download a billboard. The browser still has to download the entire giant file, even if it's only going to display it at 300 pixels wide. It's just a massive waste of data and time.

The real solution here is srcset and sizes. I know, I know, they look a little intimidating at first glance, but the concept is actually pretty simple: you just give the browser a menu of different-sized images and tell it how to choose the right one.

  • srcset: This is your menu. It's a list of the image files you have available and their actual widths (we use the w descriptor for that).
  • sizes: This is like a little hint for the browser. It says, "Hey browser, at this screen size, the image is going to take up this much of the screen."

Let's break it down.

<img 
  srcset="image-small.jpg 480w,
          image-medium.jpg 800w,
          image-large.jpg 1200w"
  sizes="(max-width: 600px) 100vw, 
         (max-width: 900px) 50vw, 
         800px"
  src="image-medium.jpg" 
  alt="A beautiful landscape showing the power of responsive images."
  width="800" 
  height="600">

I know, I know. It can look like a bit of gibberish at first. But let's translate it into plain English, shall we?

  1. srcset is basically saying: "Hey, I've got three versions of this image for you: one is 480px wide, one is 800px wide, and a big one that's 1200px wide."
  2. sizes is telling the browser:
    • "So, if the screen is 600px wide or less, this image is going to fill the full width of the screen (100vw)."
    • "Then, if the screen is between 601px and 900px, this image will only take up half the width of the screen (50vw)."
    • "And for any screen wider than 900px, the image will just be a fixed 800px wide."

The browser then does all the heavy lifting. It checks the device's screen size, looks at your sizes instructions to figure out how big the image slot is going to be, and then picks the smallest image from the srcset list that's big enough to fill that slot beautifully, without looking all pixelated.

It’s brilliant, isn't it? Yes, it's a little extra work up front to create those different image sizes, but the performance payoff is just enormous.

The Easiest Performance Wins You'll Ever Get

Okay, so you've mastered next-gen formats and responsive sizes. You're already in the top tier of web developers, seriously. But hang on, there are two more attributes you can add for some ridiculously easy performance wins.

1. Lazy Loading (loading="lazy")

This one feels like cheating, it's so easy. By just adding loading="lazy" to your <img> tag, you're telling the browser: "Hey, don't even bother downloading this image until it's about to scroll into view."

<img src="footer-logo.png" alt="Company logo" loading="lazy" width="200" height="50">

Just think about that for a second. Why should a user on a spotty connection have to download all 20 images on a long blog post if they only end up reading the first two paragraphs? With this simple attribute, images way down the page are only fetched as the user actually scrolls towards them.

It's an absolute game-changer for pages with lots of images. But, there's one important caveat: please, never use it on images that are "above the fold" (the stuff visible without scrolling), especially not on your main LCP image. That would actually slow down the perceived load time, which is the exact opposite of what we want.

2. Asynchronous Decoding (decoding="async")

Alright, this one's a bit more subtle, but it's still a neat trick to have in your back pocket. "Decoding" is the process the browser goes through to "unpack" an image file so it can actually be painted on the screen. For really large or complex images, this can sometimes cause the main thread to stutter for a split second, making the page feel a bit janky.

Adding decoding="async" gives the browser permission to do this decoding work off the main thread, so it doesn't interrupt anything more important.

<img src="huge-background.jpg" alt="A complex background pattern" decoding="async">

Now, this doesn't make the download itself any faster, but it can make the page feel smoother and more responsive while the image is loading in. It’s one of those small touches that contributes to a much better overall user experience.

Don't Forget Your SVGs!

We've been talking a lot about photos, but what about logos, icons, and simple illustrations? Using a raster format like PNG for these is often total overkill. You should be reaching for SVG (Scalable Vector Graphics). They aren't pixels, they're just code, which means they're usually incredibly small and can be scaled to any size—from a tiny favicon to a giant billboard—without ever losing a drop of quality.

But here's a pro-tip: even SVGs can be optimized. Before you drop an SVG into your project that you got from a design tool, do yourself a favor and run it through an online optimizer like the amazing SVGOMG. It strips out all the editor metadata and junk code, and can often slash the file size by 50% or more. It's a no-brainer.

Putting It All Together: The Ultimate Image Tag

So, after all that, what does a truly modern, fully-optimized image setup actually look like? Well, it combines everything we’ve talked about into one glorious chunk of HTML. It's a bit of a beast, I'll admit, but it's a beautiful one.

<picture>
  <source 
    type="image/avif" 
    srcset="photo-sm.avif 480w, photo-lg.avif 800w">
  <source 
    type="image/webp" 
    srcset="photo-sm.webp 480w, photo-lg.webp 800w">
  <img 
    srcset="photo-sm.jpg 480w, photo-lg.jpg 800w"
    sizes="(max-width: 600px) 100vw, 800px"
    src="photo-lg.jpg"
    alt="An incredibly well-optimized photograph of a soaring eagle."
    loading="lazy"
    decoding="async"
    width="800"
    height="600"
  >
</picture>

I mean, just look at it. This code is doing so much heavy lifting for us:

  • It serves the most modern format the browser can handle (AVIF first, then WebP, then good old JPG).
  • It serves the most appropriately-sized image for the user's viewport, saving tons of data.
  • It will lazy-load the image if it's offscreen.
  • It will decode the image without blocking the main thread, keeping things smooth.

It's perfect.

I know this might seem like a lot to write by hand every time, but the great news is that modern frameworks and build tools can automate most of the image generation for you. The really important part is understanding what you're asking the browser to do, and why.

You don't have to become an expert overnight. Just start small. On your next project, try adding loading="lazy" to the images below the fold. The time after that, maybe try using the <picture> tag for a new hero image. These modern image optimization techniques are here to help us all build a faster, better web. So let's get to it.


Frequently Asked Questions

What's the best image format to use in 2025?

Honestly, there's no single "best" format for every situation. A fantastic strategy, though, is to use AVIF with a WebP fallback, and then a final JPG or PNG fallback inside a <picture> element. This approach covers all your bases, making sure you deliver the smallest possible file to the widest range of users. And for logos and icons, your default should always be an optimized SVG.

Do I need both the <picture> element and the srcset attribute?

Yep, you often do, because they solve two different problems! Think of it this way: the <picture> element is for format switching (like choosing between AVIF and JPG) or for art direction (showing a different crop on mobile). The srcset and sizes attributes, on the other hand, are for resolution switching (serving a small image to a small screen and a large image to a large screen). You'll frequently use them together, just like in that "ultimate" example we went over.

Is loading="lazy" always a good idea?

It's a great idea for most images, but definitely not for everything. The golden rule is to avoid using loading="lazy" on any images that are immediately visible when the page first loads (your "above-the-fold" content). This is especially true for your main hero image or banner. Lazy-loading these critical images can actually delay when they appear on screen, which can hurt your LCP score. But for everything else that a user has to scroll to see? It's a fantastic and incredibly easy performance boost.

Related Articles