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
Acceptheaders (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:
- Identify the LCP element in PageSpeed Insights — usually a hero
<img>or a CSS background (prefer real<img>for discoverability and priority hints). - Check transfer size in Network panel; compare WebP/AVIF savings.
- Verify CLS with and without width/height attributes.
- 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
- Resize to max display width — never ship 4K pixels to a 800 px column.
- Encode AVIF/WebP with JPEG fallback via
<picture>. - Write accurate
sizes— audit against real CSS layout. - Set width and height — kill CLS on every content image.
- fetchpriority="high" on LCP hero — never lazy-load it.
- Lazy-load below-fold — galleries, related posts, comments.
- Cache immutable hashed URLs — pair with CDN long TTLs.
- 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
- Core Web Vitals explained — LCP, INP, and CLS thresholds that image choices directly affect
- HTTP caching explained — Cache-Control for fingerprinted image assets vs HTML
- CDN explained — edge delivery and format negotiation at scale
- Service workers and PWA — offline image caching strategies in installable apps