< Core Web Vitals | images />

How to optimize images for the web?

Eroan Boyer

July 27, 2026

20 minutes

A user looks at tables with very heavy frames, symbolizing unnecessarily heavy web images.

In almost all the sites we audit, images represent the largest downloaded weight, far ahead of JavaScript and stylesheets. The HTTP Archive's Web Almanac, which analyzes millions of real pages each year, confirms this observation on a web scale: images remain the heaviest resource on a median page. And it's not just a matter of weight: in most cases, the largest element displayed on the screen, the one measured by LCP, is precisely an image.

The good news is that image optimization is one of the most profitable areas of web performance: the gains are massive, measurable, and the techniques are now standardized in browsers, without relying on third-party libraries. The less good news is that each lever has its pitfalls, and poorly applied optimization can degrade what it claims to improve. A classic example: lazy loading applied to the wrong image can cost several hundred milliseconds of LCP.

In this article, we review the entire chain: format selection, responsive sizing, loading prioritization, lazy loading, layout stability, the specific case of SVGs, hosting, and automation. As with web fonts, the goal is to give you a complete and prioritized view of optimization levers, the kind we apply in our web performance audits.

Why do images weigh so much on performance?

Images represent the largest part of a web page's weight, far ahead of JavaScript and stylesheets. They impact all three Core Web Vitals: LCP, because the main element is almost always an image; CLS, when their dimensions are not reserved; and INP, when their decoding occupies the main thread.

Before diving into the techniques, let's precisely position the impact. Images affect, directly or indirectly, the three Core Web Vitals metrics that Google uses to evaluate the loading experience, and on which our article what is web performance and how is it measured lays the foundation.

LCP: when the image dictates perceived speed

LCP (Largest Contentful Paint) measures the rendering time of the largest visible element in the initial viewport, with a threshold of 2.5 seconds to respect according to web.dev. However, this largest element is, in the majority of cases measured across the web, an image: a homepage banner, a product photo, an article visual.

Every link in the chain counts: the discovery of the image in the HTML, its network priority, its weight, and then its decoding. An LCP image that is too heavy, discovered late, or loaded with low priority can, on its own, cause the page to exceed the 2.5-second threshold. The format, size, and prioritization levers detailed below each address a specific link in this chain.

CLS: when the image causes layout shifts

CLS (Cumulative Layout Shift) quantifies unexpected layout shifts during loading, with a threshold of 0.1 defined on web.dev. An image delivered without known proportions forces the browser to reorganize the page upon its arrival: the paragraph the visitor was starting to read moves down one screen, their finger lands next to the intended button. On the web, images without dimensions are the primary cause of CLS, and it's a completely avoidable problem, as we'll see with explicit dimension attributes.

INP: when decoding blocks interactivity

INP (Interaction to Next Paint) assesses the page's responsiveness to clicks, typing, and taps, with a threshold of 200 milliseconds documented on web.dev. The link with images is more indirect, but very real: decoding a multi-megapixel image consumes CPU time, and on an entry-level mobile, this decoding can delay the response to an interaction if it occurs at the wrong time. Properly sized and deferred images free up the main thread when it's most needed.

Beyond the Core Web Vitals

There's also a pure bandwidth issue. An e-commerce product page commonly embeds 1.5 to 3 MB of images. On an average mobile connection, this is several seconds of download, data charged to the user, and energy consumed unnecessarily when three-quarters of these images will never be seen. Image optimization is thus the rare convergence point between performance, SEO, user experience, and efficiency.

The rest of this article follows the logical order of well-executed optimization: first reduce what is transferred (format, size), then order what is loaded (prioritization, lazy loading), and finally stabilize what is displayed (dimensions, layout).

Which image format to choose between AVIF, WebP, and JPEG?

Serve AVIF first, with WebP as a fallback and JPEG as a last resort. At equivalent visual quality, AVIF produces the lightest files, WebP is in between, and JPEG remains the universal safety net. The picture tag allows serving all three without breaking any browser.

FormatRelative weightCompatibilityUse case
AVIFLightest94.7% of browsersPhotos, rich visuals, LCP image
WebPIntermediate96.2% of browsersUniversal fallback for photos
JPEGHeaviestUniversalLast resort, maximum compatibility
PNGHeavyUniversalTransparency without modern alternative
SVGVariable, often minimalUniversalLogos, icons, vector diagrams

Compatibility rates are from Caniuse and are based on actual usage shares recorded in July 2026. The gap between AVIF and WebP is mainly due to Opera Mini and a few marginal browsers: for the French traffic of a mainstream site, both formats are served to almost all visitors.

The format influences the transferred weight, the CPU decoding cost, and the perceived quality. For raster images (photos, complex visuals), three formats currently cover most relevant use cases.

AVIF, the best quality-to-weight ratio

Derived from the AV1 video codec, AVIF offers the best compromise currently available: at equivalent visual quality, we commonly observe a gain of around 50% compared to JPEG and 20% compared to WebP. Its support is now universal on modern browsers, as confirmed by caniuse.com: Chrome since version 85, Firefox since 93, Safari since 16.4. AVIF supports transparency and wide color gamuts (HDR), making it a replacement for PNG in most cases. Its only drawback is slightly more CPU-intensive decoding, which should be monitored on very long mobile galleries.

WebP, the modern fallback

WebP has been universally supported since 2020 (including Safari 14) and remains an excellent fallback format. However, be aware of a common misconception: WebP is not systematically lighter than JPEG. For an already well-compressed image, or one with low visual complexity, conversion can produce a heavier file. A robust conversion process always compares the result to the original file and keeps the lighter of the two, as recommended by web.dev.

JPEG and PNG, still useful as a last resort

JPEG retains two roles: serving as a universal fallback solution in a <picture> element, and remaining the reference format for certain images where it performs equally to WebP. PNG is now only justified for very specific cases of lossless transparency when compatibility with older environments is required. As for TIFF and BMP, they have no reason to appear in production.

Bar chart comparing the weight of the same image in JPEG (100%), WebP (approx. 70%), and AVIF (approx. 50%)
Typical order of magnitude for file size at equivalent visual quality: AVIF roughly halves the size of a JPEG.

In practice, format fallbacks are declared using the <picture> element: the browser iterates through the sources in order and selects the first one whose MIME type it supports, as documented on MDN.

<picture>
  <source srcset="visuel.avif" type="image/avif">
  <source srcset="visuel.webp" type="image/webp">
  <img src="visuel.jpg" width="1200" height="800" alt="Description du visuel">
</picture>

The case of animated GIFs

Animated GIF is probably the most costly format still in circulation: a limited palette of 256 colors, no efficient inter-frame compression, and files that easily reach several megabytes for a few seconds of animation. Converting to a silent, looping MP4 or WebM video typically reduces file size by over 90% according to web.dev, for a strictly identical on-screen rendering.

How to serve the right image size with srcset?

The srcset attribute declares multiple versions of the same image with their width in pixels, and sizes indicates the space the image will occupy in the layout. The browser then chooses the most suitable file for the screen size and device density, without any JavaScript intervention.

Changing format saves 30% to 50%. Serving an image tailored to the visitor's screen can save much more: sending a 1600px wide image in an area displayed at 400px on mobile means transferring up to 7 times too much data. This is the most powerful lever in the entire chain, and paradoxically one of the most neglected.

The standard mechanism relies on two complementary attributes of the <img> tag, described in detail in web.dev's responsive images guide. The srcset attribute declares the available variants with their actual width in pixels. The sizes attribute tells the browser the intended display width according to the viewport. With these two pieces of information and the screen's pixel density, the browser alone chooses the most economical variant.

<img
  src="hero-1200.avif"
  srcset="hero-600.avif 600w,
          hero-1200.avif 1200w,
          hero-2000.avif 2000w"
  sizes="(min-width: 1024px) 50vw, 100vw"
  width="1200" height="800"
  alt="Description du visuel">

Two errors consistently appear in our audits. The first: a srcset with only one variant, which is the same as not having one. You need at least two to three variants per image, excluding logos and icons. The second: a srcset with width descriptors without a sizes attribute. In this case, the browser assumes the image occupies 100% of the viewport and often downloads a variant that is too large. You also need to remember to keep the sizes values up to date: after a graphic redesign, a sizes attribute that no longer reflects the actual layout will silently cause the wrong variants to be downloaded.

The <picture> element complements this setup when the selection depends on something other than width: the format cascade seen earlier, art direction (serving a different crop on mobile), or dark mode via a prefers-color-scheme media query. The width and height dimensions are always set on the final <img> tag, never on the <source> elements.

How to prioritize the LCP image?

Add fetchpriority="high" to the page's main image and remove any loading="lazy" from it. Without this attribute, the browser downloads images with low priority by default and discovers which one matters late. Only one image per page deserves this priority, the one that triggers the LCP.

Not all images on a page are equal. There is one that deserves special treatment: the one that constitutes the LCP element, generally the large visual at the top of the page. However, by default, browsers load images at low priority until they have calculated the layout and determined which ones are visible. This delay of several hundred milliseconds directly impacts the LCP.

fetchpriority high on the LCP image

The fetchpriority="high" attribute corrects this behavior by promoting the image to a high network priority as soon as it is discovered in the HTML, as explained by web.dev. Measurements published by Addy Osmani (Chrome team) show gains of 5% to 30% on LCP depending on the stacks, with an average around 15%. For an attribute that takes one word, the effort-to-benefit ratio is unbeatable.

<img src="hero.avif"
     srcset="hero-800.avif 800w, hero-1600.avif 1600w"
     sizes="100vw"
     width="1600" height="900"
     fetchpriority="high"
     alt="Visuel principal de la page">

The golden rule: only one image per page should have fetchpriority="high". If five images claim it, priority becomes meaningless and behavior reverts to default. Conversely, footer images can be assigned fetchpriority="low" to free up bandwidth for critical resources.

The CSS background-image trap

A recurring trap deserves to be highlighted: when the main visual is served as a CSS background-image, it escapes the browser's preloader, which only discovers it after downloading and parsing the stylesheet. This is one of the most frequent causes of degraded LCP that we encounter. The clean solution is to convert the element into a real <img> or <picture> tag. Failing that, a <link rel="preload" as="image"> in the <head> can trigger early download, a technique detailed in web.dev's LCP optimization guide and in our article dedicated to LCP.

<link rel="preload" as="image"
      href="hero.avif"
      imagesrcset="hero-800.avif 800w, hero-1600.avif 1600w"
      imagesizes="100vw"
      fetchpriority="high">

Is your site as fast as your visitors expect?

Discover how we can help you

How to defer image loading?

The loading="lazy" attribute is sufficient: it tells the browser to only download an image when it's close to the viewport, without JavaScript libraries. It should never be applied to images visible in the initial view. Lazy loading applied to the LCP image degrades performance instead of improving it.

The exact counterpart to prioritization, lazy loading consists of not downloading images located below the fold until the user gets close to them. Since 2020, this feature is native in all browsers via the loading="lazy" attribute, documented on MDN: no JavaScript, no library, no observer to write.

<img src="illustration-section-basse.avif"
     loading="lazy"
     width="800" height="533"
     alt="Illustration de la section">

<iframe src="https://www.youtube.com/embed/xyz"
        loading="lazy"
        width="560" height="315"
        title="Titre de la vidéo"></iframe>

The browser triggers the download when the image approaches the viewport, with an anticipation margin that depends on connection speed, as described by web.dev. The attribute also applies to iframes, which is valuable for embedded maps and video players. On this last point, we go further by recommending clickable facades, a topic related to third-party scripts.

Costly errors

The first, by far the most serious error: applying loading="lazy" to the LCP image. The browser then waits for confirmation that the image is visible before downloading it, which delays rendering by 200 to 800 milliseconds in the cases we measure. This error is all the more insidious as it is often produced by a plugin or theme that applies the attribute en masse. And the combination of loading="lazy" with fetchpriority="high" fixes nothing: lazy loading always takes precedence.

Second mistake: JavaScript lazy loading systems based on data-src, inherited from before 2020. They break the browser's preloader and make images invisible if the script fails, while offering no advantage over the native solution. Third mistake, more subtle: applying lazy loading to small, repeated images (avatars, icons), for which the mechanism's latency exceeds the benefit of deferring the download.

Web page schema: LCP image above the fold loading immediately with fetchpriority high, images below the fold loading lazy
The strategy boils down to one question: is the image in the initial viewport? Above the fold, immediate and prioritized loading; below the fold, deferred loading.

And async decoding?

We often encounter the decoding="async" attribute presented as an essential optimization. The reality is more nuanced: modern browsers already decode most images off the main thread, and the MDN documentation describes an attribute with limited scope. It is harmless and can be set by default, but its effect is only truly observable on very large images under heavy CPU pressure. In other words: worth adding, but certainly not a priority for audits.

How to prevent images from causing page jumps?

Always provide the width and height attributes on each img tag, with the intrinsic dimensions of the file. The browser then reserves the space even before the download. This is the most cost-effective fix against CLS, and it only costs two attributes per image.

An image without width and height attributes is an image whose proportions the browser ignores before downloading it. Consequence: it reserves zero height for it, displays the text, then redraws the entire layout when the image arrives. This visual shift is counted by CLS, and images without dimensions have historically been the primary cause of this on the web, as the web.dev CLS optimization guide reminds us.

Since late 2019, all browsers calculate the aspect ratio from these two attributes and reserve the exact space even before the first byte of the image. The key point, often misunderstood: HTML values do not dictate display size. CSS remains in control, and the classic combination of max-width: 100% with height: auto maintains a perfectly responsive rendering while preserving the reserved ratio.

<img src="photo.avif" width="1600" height="900" alt="Description">

/* côté CSS : responsive sans casser le ratio réservé */
img {
  max-width: 100%;
  height: auto;
}
Before and after comparison: without width and height the text jumps when the image loads, with width and height the space is reserved and the layout remains stable
Without dimensions, content jumps upon arrival of each image. With dimensions, space is reserved from the first render.

For CSS background images, which lack an equivalent to these attributes, the aspect-ratio property allows reserving space directly in the stylesheet. And in a <picture> element, let's recall, dimensions are declared on the final <img> tag. It's a reflex to systematize: on most sites we optimize, simply generalizing explicit dimensions is enough to bring CLS below the 0.1 threshold targeted by Google. Our guide on Core Web Vitals with WordPress details this task on the CMS side.

When should you use the SVG format?

SVG is suitable for logos, icons, pictograms, and diagrams, i.e., any vector drawing that needs to remain sharp at all sizes. It is not suitable for photographs. Its weight depends on the complexity of the path, not the display dimensions, making it unbeatable for simple shapes.

For logos, icons, pictograms, and diagrams, the vector format SVG is unrivaled: it displays sharply at all resolutions, often weighs less than a kilobyte when optimized, and compresses remarkably well in Brotli or gzip since it's XML. A logo in PNG or, worse, JPEG, is an immediate red flag in an audit.

Three practices make a difference. First, systematically clean exported files from creation tools: metadata from Illustrator, Sketch, or Inkscape unnecessarily bloat the weight, and a pass through SVGO on the command line, or its web interface SVGOMG by Jake Archibald, removes them without affecting the rendering. Second, inline critical small SVGs like the header logo: this saves a network request. Finally, for icon sets, prefer a sprite declared once via <defs> and reused everywhere via <use>, rather than thirty identical SVGs repeated in the DOM.

Last point, often forgotten: an SVG without a viewBox or explicit dimensions causes the same layout shifts as a raster image without width or height. The stability rules seen above apply identically.

How to host and deliver images efficiently?

Serve your images from a CDN, with long browser caching and a versioned filename. The distance to the visitor and the cache policy weigh as much as the file size itself. A well-compressed but poorly served image remains slow, especially on repeat visits.

The best format in the world cannot compensate for poor delivery. First principle: host images on your own domain. Each third-party domain imposes an additional DNS resolution and TLS negotiation, typically 100 to 300 milliseconds of overhead on mobile. Since Chrome partitioned its HTTP cache, the argument of a shared cache between sites has actually disappeared entirely: a third-party resource is re-downloaded for each site that uses it.

If an image CDN is justified (on-the-fly resizing, automatic format conversion based on the browser), the best practice is to serve it behind an alias CNAME of the main domain rather than from the provider's domain. We have dedicated a full article to the pros and cons of CDNs to help you decide. Second principle: an aggressive browser caching policy. Images versioned in their filename can have a Cache-Control of one year with the immutable directive, which eliminates any revalidation for subsequent visits.

Finally, the quality of the origin infrastructure remains crucial: Brotli compression enabled, HTTP/2 or HTTP/3 for parallel downloads, and controlled server response time. This is precisely what we guarantee with our WordPress hosting optimized for performance.

How to automate image optimization?

Optimization should happen when the file is uploaded, not manually. An automated pipeline generates modern formats, produces intermediate sizes, and applies compression, without contributor intervention. Any step left manual eventually gets forgotten, and the site degrades over time with new publications.

All of the above is only worthwhile if it's applied to 100% of images, including those that contributors will upload next year. Manual optimization does not scale: an automated pipeline is necessary.

On WordPress, some of the work is natively supported: the CMS automatically generates multiple sizes on upload and builds the corresponding srcset, applies loading="lazy" to images outside the initial viewport, and knows how to set fetchpriority="high" on the main image, a continuous effort by the core performance team documented on make.wordpress.org. Support for the AVIF format in the media library has been native since WordPress 6.5. Automatic conversion of uploads to AVIF can be controlled with a filter:

<?php
// Générer les tailles intermédiaires en AVIF à l'upload
add_filter( 'image_editor_output_format', function ( $formats ) {
    $formats['image/jpeg'] = 'image/avif';
    $formats['image/png']  = 'image/avif';
    return $formats;
} );

This native foundation has its limits, however: no comparison of weight before and after conversion, global quality settings, and no handling of existing images without dedicated processing. This is where specialized plugins or a custom build pipeline come in, to be chosen based on the context. This fine-tuning, which also includes excluding the LCP image from lazy loading, is part of the standard interventions in our WordPress optimization service. Outside of the CMS, on modern JavaScript stacks, frameworks provide image components that industrialize these same principles; our SPA audits verify that they are correctly configured.

How to optimize images on WordPress?

WordPress already does some of the work without configuration: it generates intermediate sizes with each upload, automatically fills srcset and sizes in the content, and applies loading="lazy" to images outside the initial view. Three important levers remain your responsibility, and they are precisely the ones that have the most impact.

The first is format. The WordPress core does not convert uploads to AVIF or WebP: without a dedicated extension or processing on upload, your visitors receive the original JPEG. This is the most significant weight saving left on the table for the majority of installations we audit.

The second is LCP image prioritization. WordPress applies a heuristic to remove lazy loading from the first few images, but it does not reliably set fetchpriority="high" on all templates, especially with a page builder. Check the attribute on the final render, not in the editor.

The third is the proliferation of intermediate sizes. Each theme and each plugin declares its own: a loaded installation commonly produces fifteen variations per uploaded image, half of which will never be served. The disk and backups pay the price, with no benefit for the visitor.

How to measure the effect of image optimization?

Track three indicators before and after: the total weight of the page's images, the field LCP at the 75th percentile, and the format actually served to your visitors. The first two indicate whether the gain exists, the third whether your chain is working. Unmeasured optimization is just a hypothesis.

As always in performance, we only steer what we measure. Synthetic audit tools identify most of the issues mentioned here: Lighthouse and PageSpeed Insights flag oversized images, outdated formats, images without dimensions, and candidates for lazy loading, with an estimate of the byte savings for each. We compared these two tools in a dedicated article.

The DevTools network panel complements the analysis: filter by images, sort by weight, and check the effective request priority for the LCP image. However, be careful not to conclude based solely on lab tests: only field data, from the CrUX report or a RUM tool, reflects what your visitors actually experience, on their devices and connections. This is the whole purpose of our web performance monitoring offer: to track these metrics continuously and detect regressions before they cost positions or conversions.

What to remember about image optimization

If you were to remember only one method, it would be this one, in order. Serve modern formats, AVIF as the first choice with WebP as a fallback then JPEG, verifying that each conversion actually reduces the file size. Provide each image in multiple sizes with srcset and sizes. Give network priority to the LCP image with fetchpriority="high", and to it alone. Defer all images below the fold with loading="lazy", taking care never to apply it to the LCP image. Declare width and height on each image for stable CLS. Process SVGs with SVGO, host your images on your domain with a long cache policy, and automate everything so that the discipline survives future uploads.

Each site nevertheless has its specificities: theme, plugins, CDN, editorial habits. To know precisely where your room for improvement lies and to quantify it, a web performance audit provides a prioritized and quantified action plan; and if you prefer to delegate the entire project, our team will handle it from start to finish as part of an optimization service.

Continue reading