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:
| Format | Best For | Compression | Browser Support |
|---|---|---|---|
| WebP | Photos, illustrations, icons | 25-35% smaller than JPEG | 97%+ (all modern browsers) |
| AVIF | Photos, HDR images | 50% smaller than JPEG | 92% (no IE, older Safari) |
| JPEG / JPG | Photos (fallback) | Good lossy compression | 100% |
| PNG | Screenshots, transparency | Lossless, larger files | 100% |
| SVG | Icons, logos, illustrations | Vector, scales infinitely | 100% |
| GIF | Simple animations | Poor (use video instead) | 100% |
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
pngquantandoxipngcan 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">
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
widthandheightattributes (or use CSSaspect-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
srcsetandsizesfor responsive images - ✅ Lazy-load below-fold images with
loading="lazy" - ✅ Preload the LCP image with
fetchpriority="high" - ✅ Always set
widthandheightto 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