All posts
PerformanceImagesLCP

How to Optimize Images for Web Performance

Images are the biggest contributor to page weight and slow LCP. Learn how to choose the right format, compress effectively, serve responsively, and use lazy loading.

debugutils.dev

Images account for more than 50% of the average page’s total weight. They’re also the #1 cause of slow LCP scores — the Largest Contentful Paint is almost always an image. Optimizing images is one of the highest-ROI performance improvements you can make.

Choose the Right Format

WebP — The best general-purpose format for the web today. Typically 25–35% smaller than JPEG at equivalent quality. Supported by all modern browsers.

AVIF — Even better compression than WebP (often 40–50% smaller than JPEG), but encoding is slower and browser support, while good, is slightly less universal.

JPEG — Use for photographs when you can’t use WebP/AVIF. Still widely supported.

PNG — Use for images with transparency, logos, and illustrations. For photos, it’s almost always much larger than JPEG.

SVG — For logos, icons, and illustrations that need to scale. Infinitely scalable, tiny file size for simple graphics.

GIF — Avoid for animation. Use <video> with autoplay and loop instead — video codecs compress animated content far better.

How to serve WebP with a JPEG fallback

<picture>
  <source srcset="hero.avif" type="image/avif" />
  <source srcset="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="..." width="1200" height="600" />
</picture>

The browser uses the first <source> it supports, falling back to the <img> tag.

Compress Images

Even with modern formats, compression settings matter. Most image editors default to higher quality than necessary for web use.

For JPEG: 75–85% quality is indistinguishable from 100% at normal viewing sizes. For WebP: Quality 80 is a good starting point. For AVIF: Quality 60–70 often looks excellent.

Tools:

  • Squoosh — browser-based, excellent quality comparison
  • ImageOptim — Mac desktop app for batch optimization
  • sharp (Node.js) — for build pipeline automation
  • Cloudflare Images / Cloudinary — CDN with automatic optimization

Serve Responsive Images

Don’t serve a 2400px image to a 600px mobile viewport. Use srcset and sizes to let the browser pick the right size:

<img
  src="hero-800.webp"
  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w"
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 1200px"
  alt="..."
  width="1200"
  height="600"
/>

The browser uses sizes to understand the display width and srcset to pick the right source file.

Always Set Width and Height

This prevents layout shifts (CLS) by reserving the correct space before the image loads:

<!-- Good -->
<img src="photo.webp" width="800" height="600" alt="..." />

<!-- Or use aspect-ratio in CSS -->
<style>
  .hero-img { aspect-ratio: 4 / 3; width: 100%; }
</style>

Lazy Load Below-Fold Images

Images below the fold don’t need to load immediately. Use the native loading="lazy" attribute:

<img src="gallery-photo.webp" loading="lazy" alt="..." width="400" height="300" />

Never use loading="lazy" on your LCP image. The browser needs to fetch it immediately.

Prioritize the LCP Image

Your LCP image should be the highest-priority resource on the page:

<!-- In the <head> -->
<link rel="preload" as="image" href="hero.webp" fetchpriority="high" />

<!-- On the img tag -->
<img src="hero.webp" fetchpriority="high" alt="..." />

This tells the browser to start fetching the image before it finishes parsing the document.

Use a CDN

Serving images from a CDN reduces latency through edge caching and geographic distribution. Many CDNs (Cloudflare Images, Imgix, Cloudinary) also offer on-the-fly format conversion and resizing.

Measuring Image Performance

Run a test on debugutils.dev to see your total image weight, format breakdown, and which images are the heaviest. The LCP analysis identifies your LCP element and how much time its download is contributing to your score.