Image Optimization Guide: Compress, Resize & Serve Images for the Web

Images account for roughly 50% of the average webpage's total bytes. Unoptimized images are the single biggest cause of slow pages, poor Core Web Vitals scores, and high bounce rates. The good news: most image optimization is straightforward and can cut page weight by 40–80%.

Image Formats: Which to Use and When

Choosing the right format is the biggest win. Here's the modern decision matrix:

FormatBest ForCompressionBrowser Support
WebPPhotos, illustrations, icons25-35% smaller than JPEG97%+ (all modern browsers)
AVIFPhotos, HDR images50% smaller than JPEG92% (no IE, older Safari)
JPEG / JPGPhotos (fallback)Good lossy compression100%
PNGScreenshots, transparencyLossless, larger files100%
SVGIcons, logos, illustrationsVector, scales infinitely100%
GIFSimple animationsPoor (use video instead)100%
💡 Rule of Thumb: Use AVIF with WebP fallback for photos. Use SVG for icons and logos. Use PNG only when you need pixel-perfect transparency.

Compression Techniques

Lossy compression

Removes imperceptible visual data. A JPEG at quality 80 is nearly indistinguishable from quality 100 but is 50-70% smaller.

  • JPEG quality 75-85: The sweet spot for most photos
  • WebP quality 75-80: Even at lower quality, WebP artifacts are less noticeable than JPEG
  • AVIF quality 60-70: AVIF maintains quality at much lower settings

Lossless compression

Reduces file size without losing any data. Essential for screenshots, diagrams, and text-heavy images.

  • PNG optimization: Tools like pngquant and oxipng can reduce PNG size by 50-70%
  • SVG optimization: SVGO removes metadata, comments, and unnecessary attributes — typical 30-50% reduction

Compression tools

# Command line
npx sharp-cli input.jpg -o output.webp --webp-quality 80
cwebp -q 80 input.jpg -o output.webp
avifenc --min 20 --max 30 input.png output.avif

# Build tools
// vite.config.js
import viteImagemin from 'vite-plugin-imagemin';
plugins: [viteImagemin({
  webp: { quality: 80 },
  avif: { quality: 65 }
})]

Responsive Images

Don't serve a 2000px image to a 400px phone screen. Use the <picture> element and srcset:

The picture element (format negotiation)

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

Browsers pick the first format they support. AVIF-capable browsers get the smallest file; others fall back to WebP or JPEG.

Resolution switching with srcset

<img
  srcset="photo-400w.webp 400w,
          photo-800w.webp 800w,
          photo-1200w.webp 1200w"
  sizes="(max-width: 600px) 400px,
         (max-width: 1000px) 800px,
         1200px"
  src="photo-800w.webp"
  alt="Description"
  width="1200" height="800"
>

The browser picks the best size based on viewport width and device pixel ratio.

Lazy Loading

Images below the fold shouldn't load until the user scrolls near them. This improves initial page load dramatically.

Native lazy loading (recommended)

<!-- Below-fold images -->
<img src="photo.webp" loading="lazy" alt="Description"
     width="800" height="600">

<!-- Above-fold hero image — do NOT lazy load -->
<img src="hero.webp" loading="eager" alt="Hero"
     width="1200" height="600" fetchpriority="high">
⚠️ Important: Never lazy-load your LCP (Largest Contentful Paint) image. Add fetchpriority="high" to your hero/banner image to tell the browser to prioritize it.

Core Web Vitals Impact

Image optimization directly affects two of three Core Web Vitals:

  • LCP (Largest Contentful Paint): The hero image is often the LCP element. Optimize its size and preload it.
  • CLS (Cumulative Layout Shift): Always set width and height attributes (or use CSS aspect-ratio) to prevent layout shifts as images load.

Preloading the LCP image

<link rel="preload" as="image" href="hero.webp"
      type="image/webp" fetchpriority="high">

CDN & Caching

  • Use an image CDN: Services like Cloudflare Images, Imgix, or Cloudinary auto-resize, convert formats, and cache globally
  • Set long cache headers: Images rarely change — use Cache-Control: public, max-age=31536000, immutable
  • Content-based filenames: Use hashes in filenames (photo-a1b2c3.webp) for cache busting on updates

Quick Optimization Checklist

  • ✅ Serve AVIF/WebP with JPEG fallback using <picture>
  • ✅ Compress: JPEG quality 80, WebP quality 80, AVIF quality 65
  • ✅ Resize to the largest display size needed — never serve 4000px images
  • ✅ Use srcset and sizes for responsive images
  • ✅ Lazy-load below-fold images with loading="lazy"
  • ✅ Preload the LCP image with fetchpriority="high"
  • ✅ Always set width and height to prevent CLS
  • ✅ Use SVG for icons and logos (run through SVGO)
  • ✅ Replace animated GIFs with <video> — 80%+ smaller
  • ✅ Serve from a CDN with long cache headers

Need to compress images right now? Try our browser-based compressor.

Open Image to Base64 →