Guide

Web image optimization explained: formats, responsive markup, and Core Web Vitals

Images are usually the heaviest assets on a page — often 50–80% of total transfer size on content sites. A full-resolution JPEG hero dumped into an article template can single-handedly fail Largest Contentful Paint (LCP) on mobile, while missing width and height attributes cause Cumulative Layout Shift (CLS) as the browser discovers intrinsic dimensions late. Modern web image optimization is not one trick; it is a stack of decisions: pick the right format, serve the right pixel dimensions for each viewport, defer off-screen bytes, and cache aggressively at the edge. This guide walks through each layer and how it connects to Core Web Vitals, HTTP caching, and CDN delivery.

Why images dominate performance budgets

Text compresses well; photographs do not. A 4000×3000 camera export might weigh 3–8 MB as JPEG. Downscaled to 1200 px wide at quality 80, the same photo often lands under 150 KB with no visible loss on a laptop screen. The gap between "what designers export" and "what the network needs" is where most image optimization wins live.

Browsers decode images on the main thread (unless you use decoding="async"). Large decode work competes with JavaScript hydration and input handlers, which hurts Interaction to Next Paint (INP) on image-heavy pages. Smaller files decode faster and arrive sooner — both help real-user metrics in Search Console.

For publishers monetizing with display ads, faster LCP means content and ad slots become visible sooner. Readers bounce less; ad viewability improves. Image optimization is a direct lever on organic traffic and revenue, not a cosmetic tweak.

Choosing a format: JPEG, PNG, WebP, AVIF, and SVG

Format choice is the first compression layer. There is no universal winner — match the format to content type and browser support.

JPEG

Best for photographs and complex gradients. Lossy compression; no transparency. Universal support. Use progressive JPEG or mozjpeg-tuned encoders for slightly better perceptual quality at a given file size. Quality 75–85 is a common starting range — always eyeball at target display size, not 100% zoom.

PNG

Lossless; supports alpha transparency. Appropriate for logos, UI chrome, and screenshots with sharp text. Avoid PNG for large photos — file sizes explode. For simple flat graphics, consider SVG instead.

WebP

Google's format offering both lossy and lossless modes plus transparency. Typically 25–35% smaller than JPEG/PNG at equivalent visual quality. Supported in all major browsers today. A safe default for new photographic content when you can serve a JPEG fallback via <picture>.

AVIF

Based on AV1 video codec intra frames. Often beats WebP by another 20–50% on photos, especially at lower bitrates. Encode is slower and CPU-heavy in build pipelines. Safari added support in recent versions; use progressive enhancement with WebP and JPEG fallbacks. Worth it for hero images and large article thumbnails where every kilobyte counts.

SVG

Vector format for icons, diagrams, and simple illustrations. Infinitely scalable; tiny for line art. Inline SVG avoids an extra HTTP request but adds to HTML parse weight — external SVG files cache independently. Sanitize third-party SVGs; they can embed scripts.

A practical cascade in <picture>: AVIF source, WebP source, JPEG or PNG fallback <img>. The browser picks the first format it understands.

Responsive images: srcset, sizes, and the picture element

Serving a 2400 px image to a 390 px phone wastes bandwidth and decode time. Responsive images let the browser download an appropriately sized file.

srcset with width descriptors

List multiple image URLs with their intrinsic widths:

<img
  src="/img/hero-800.jpg"
  srcset="/img/hero-400.jpg 400w,
          /img/hero-800.jpg 800w,
          /img/hero-1200.jpg 1200w"
  sizes="(max-width: 600px) 100vw, 800px"
  width="800"
  height="450"
  alt="Description of the image"
>

The sizes attribute tells the browser how wide the image will render at each breakpoint. Without accurate sizes, the browser assumes 100vw and may over-download. Audit your CSS: if the image sits in a 720 px column inside a max-width container, reflect that in sizes.

Device pixel ratio (DPR)

Retina screens benefit from 2× assets, but not every image needs 3× variants. Hero and product shots: yes. Decorative 16 px icons inlined as SVG: no. A common pattern is 1×, 1.5×, and 2× widths in srcset for key visuals only.

Art direction with picture

Sometimes crop matters, not just scale — a wide banner on desktop becomes a tight portrait on mobile. <picture> supports media queries on <source> elements so you can swap entirely different crops or aspect ratios per breakpoint.

Lazy loading, fetchpriority, and the LCP hero exception

Native lazy loading is one attribute:

<img src="..." loading="lazy" alt="...">

Off-screen images defer their network request until the user scrolls near them. That saves bandwidth on long articles and category pages. The critical rule: never lazy-load your LCP element. If the hero image above the fold is the largest paint, loading="lazy" delays it and wrecks LCP. Instead, mark the hero with high priority:

<img src="..." fetchpriority="high" alt="...">

Pair fetchpriority="high" with a preload link for the exact hero URL when it is discovered late in HTML (below many stylesheets and scripts):

<link rel="preload" as="image" href="/img/hero-800.webp"
      imagesrcset="..." imagesizes="...">

Below-the-fold images, author avatars in comment threads, and gallery thumbnails are ideal lazy-load candidates. Test with Chrome DevTools Performance — confirm the LCP image request starts early in the waterfall.

Preventing layout shift: dimensions, aspect-ratio, and placeholders

CLS spikes when the browser reserves zero space for an image, then reflows the page when pixels arrive. Always set explicit width and height on <img> matching the intrinsic aspect ratio of the file you serve. CSS can scale the rendered size:

img { max-width: 100%; height: auto; }

Modern CSS aspect-ratio works on containers when dimensions vary across srcset candidates with the same ratio. For unknown third-party embeds, use a fixed-ratio wrapper or a low-quality image placeholder (LQIP) — a blurred 20 px-wide data URI or dominant-color block — so space is reserved before the full asset loads.

Accessible alt text is not optional for SEO and accessibility. Describe the image's purpose, not "image of..." filler. Decorative images use alt="".

Build-time compression and CDN image pipelines

Manual export from design tools does not scale. Automate in CI:

  • Sharp, Squoosh, or imagemin — batch resize and re-encode to WebP/AVIF at defined quality tiers.
  • Content-defined widths — generate 400, 800, 1200, 1600 px variants; store hashed filenames for long cache lifetimes per HTTP caching guidance.
  • CDN image transforms — Cloudflare, Fastly, imgix, and Cloudinary resize and format-negotiate at the edge using Accept headers (image/avif, image/webp). One origin master; infinite derivatives without bloating your repo.

When using a CDN, set cache keys to include format variant. Purge carefully after re-encoding campaigns — stale AVIF at the edge while origin serves new JPEG causes confusing A/B visual diffs.

decoding="async" hints that decode can happen off the critical path. It helps on image galleries; less impact on single-hero article layouts. Combine with lazy loading for below-fold grids.

Measuring impact: lab vs field

Lighthouse flags "properly size images" and "serve images in next-gen formats." Treat Lighthouse as a checklist, not the score that matters for ranking. Field data in Search Console and Chrome UX Report (CrUX) reflects what real users on slow networks experience after your CDN and caching layers apply.

Before/after workflow:

  1. Identify the LCP element in PageSpeed Insights — usually a hero <img> or a CSS background (prefer real <img> for discoverability and priority hints).
  2. Check transfer size in Network panel; compare WebP/AVIF savings.
  3. Verify CLS with and without width/height attributes.
  4. Re-check CrUX after 28 days for durable field movement.

Background images set in CSS bypass srcset, lazy loading, and fetch priority. For performance-critical heroes, use semantic markup with an <img> and style the container.

Practical checklist

  1. Resize to max display width — never ship 4K pixels to a 800 px column.
  2. Encode AVIF/WebP with JPEG fallback via <picture>.
  3. Write accurate sizes — audit against real CSS layout.
  4. Set width and height — kill CLS on every content image.
  5. fetchpriority="high" on LCP hero — never lazy-load it.
  6. Lazy-load below-fold — galleries, related posts, comments.
  7. Cache immutable hashed URLs — pair with CDN long TTLs.
  8. Meaningful alt text — accessibility and image search.

Image optimization is one of the highest-ROI performance tasks on content sites. The markup is boring; the metrics move. Get formats and dimensions right once in your templates, automate variants in the build, and let the CDN handle the long tail of device sizes.

Related reading