Image & Video Optimization: Modern Formats & Techniques

Easy•

Media optimization is critical - images are the largest resource on most pages. Master these techniques for dramatic performance gains.

Quick Decision Guide

Quick Wins:

Format: Use modern formats such as WebP or AVIF when they produce smaller files for the asset Responsive: Use srcset - serve appropriate size for each device Lazy load: loading="lazy" for non-critical below-fold images; keep likely LCP media eager Dimensions: Always set width and height to prevent layout shift CDN: Use image CDN (Cloudinary, Imgix) for automatic optimization

Result: Smaller transfers and better visual quality when format, dimensions, and compression are chosen per asset. Always measure output quality and bytes.

Modern Image Formats

WebP

Often smaller than JPEG or PNG for photographic and transparent assets, depending on image content and encoder settings.

<picture>
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Fallback">
</picture>

Browser support: broadly supported in modern browsers; keep fallbacks when your audience requires them

AVIF

Often compresses better than WebP for photographs, but encoding cost, decode support, and quality settings matter.

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Fallback">
</picture>

Browser support: supported in modern evergreen browsers; keep WebP/JPEG fallbacks for compatibility

Format Comparison

Original JPEG: 1MB
WebP: smaller if the asset compresses well
AVIF: often smaller still, with quality and decode trade-offs

When to Use Each

•WebP: Universal modern format
•AVIF: Best compression, use with fallback
•JPEG: Legacy browsers only
•PNG: Icons, logos (or use SVG)

Responsive Images

srcset - Multiple Sizes

Serve different sizes for different screens:

<img 
  srcset="
    image-400w.jpg 400w,
    image-800w.jpg 800w,
    image-1200w.jpg 1200w
  "
  sizes="(max-width: 600px) 400px, 800px"
  src="image-800w.jpg"
  alt="Responsive image"
  loading="lazy"
/>

How it works:

•Browser picks appropriate size based on screen width
•sizes tells browser the display width
•w descriptor specifies image width in pixels

Picture Element - Art Direction

Different images for different sizes:

<picture>
  <!-- Mobile: Square crop -->
  <source 
    media="(max-width: 600px)" 
    srcset="mobile-square.jpg"
  />
  
  <!-- Desktop: Wide crop -->
  <source 
    media="(min-width: 601px)" 
    srcset="desktop-wide.jpg"
  />
  
  <img src="default.jpg" alt="Responsive art direction" />
</picture>

Density Descriptors

Serve higher resolution for retina displays:

<img 
  srcset="image.jpg 1x, image@2x.jpg 2x, image@3x.jpg 3x"
  src="image.jpg"
  alt="Hi-DPI image"
/>

Lazy Loading

Native Lazy Loading

<!-- Lazy load (loads when near viewport) -->
<img src="image.jpg" loading="lazy" alt="Lazy loaded">

<!-- Eager load (default, loads immediately) -->
<img src="hero.jpg" loading="eager" alt="Hero image">

Browser support: broadly supported in modern browsers; keep fallbacks when your audience requires them

Implementation Strategy

<!-- First 2-3 images: eager -->
<img src="hero.jpg" loading="eager" alt="Hero">
<img src="feature1.jpg" loading="eager" alt="Feature 1">

<!-- Rest: lazy -->
<img src="feature2.jpg" loading="lazy" alt="Feature 2">
<img src="feature3.jpg" loading="lazy" alt="Feature 3">

Prevent Layout Shift

Always specify dimensions:

<img 
  src="image.jpg" 
  alt="Description"
  width="800" 
  height="600"
  loading="lazy"
/>

Or use aspect ratio:

img {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
}

Video Optimization

Video Formats

<video controls preload="metadata">
  <source src="video.webm" type="video/webm">
  <source src="video.mp4" type="video/mp4">
</video>

Preload options:

•none: Don't preload anything
•metadata: Preload metadata only (duration, dimensions)
•auto: Browser decides

Lazy Load Videos

<!-- Don't autoplay, load poster only -->
<video 
  poster="thumbnail.jpg" 
  preload="none"
  controls
>
  <source src="video.mp4" type="video/mp4">
</video>

YouTube/Vimeo Optimization

Use facade (covered in Import on Interaction):

function VideoEmbed({ videoId }) {
  const [play, setPlay] = useState(false);
  
  if (play) {
    return (
      <iframe 
        src={`https://youtube.com/embed/${videoId}?autoplay=1`}
        allow="autoplay"
      />
    );
  }
  
  return (
    <div 
      onClick={() => setPlay(true)}
      style={{
        backgroundImage: `url(https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg)`
      }}
    >
      <button>ā–¶ļø Play</button>
    </div>
  );
}

Saves: ~600KB per embed until user clicks

Image CDN & Automation

Next.js Image Component

Automatic optimization:

import Image from 'next/image';

<Image 
  src="/photo.jpg"
  width={800}
  height={600}
  alt="Auto-optimized"
  loading="lazy"
  quality={85}
/>

Features:

•Automatic WebP/AVIF conversion
•Responsive images (srcset)
•Lazy loading
•Blur placeholder

Cloudinary

<!-- Original: 1MB JPEG -->
<img src="https://res.cloudinary.com/demo/image/upload/photo.jpg">

<!-- Optimized: Auto format, quality, size -->
<img src="https://res.cloudinary.com/demo/image/upload/f_auto,q_auto,w_800/photo.jpg">

URL transformations:

•f_auto: Auto format (WebP/AVIF)
•q_auto: Auto quality
•w_800: Resize to 800px width

Imgix

<img src="https://demo.imgix.net/photo.jpg?auto=format,compress&w=800">

Benefits: No build step, on-the-fly optimization, global CDN

Best Practices

Checklist

āœ… Use modern formats (WebP minimum, AVIF when possible)

āœ… Implement srcset for responsive images

āœ… Lazy load non-critical below-fold images

āœ… Set dimensions (width/height) to prevent CLS

āœ… Tune quality per asset; 80-85% is a common starting point, not a universal rule

āœ… Use CDN for automatic optimization

āœ… Optimize early - avoid shipping oversized source images to the browser

Common Mistakes

<!-- āŒ Bad: No dimensions (causes CLS) -->
<img src="image.jpg" alt="Image">

<!-- āœ… Good: With dimensions -->
<img src="image.jpg" width="800" height="600" alt="Image">

<!-- āŒ Bad: No lazy loading -->
<img src="below-fold.jpg" alt="Not visible">

<!-- āœ… Good: Lazy loaded -->
<img src="below-fold.jpg" loading="lazy" alt="Lazy">

<!-- āŒ Bad: Single size for all devices -->
<img src="huge-4k.jpg" alt="Oversized">

<!-- āœ… Good: Responsive sizes -->
<img srcset="small.jpg 400w, large.jpg 1200w" alt="Responsive">

Performance Impact

Before optimization:
- 10 images, all JPEG
- Total: 8MB
- LCP: 4.5s

After optimization:
- 10 images, WebP with srcset
- Lazy loading enabled
- Total: 1.2MB (85% reduction)
- LCP: 1.2s (73% faster)