Frontend Engineering/
Lesson

Image optimization is the single most impactful performance work you can do. Images are typically 50-70% of total page weight, and the fix is straightforward: serve the right format, at the right size, at the right time.

Choosing the right format

FormatBest forSize vs JPEGBrowser support
AVIFPhotos, complex images~50% smallerChrome, Firefox, Safari 16.4+
WebPPhotos, complex images~30% smallerAll modern browsers
JPEGPhotos (legacy fallback)BaselineUniversal
PNGImages with transparency2-10x largerUniversal
SVGIcons, logos, illustrationsTiny (vector)Universal

The <picture> element lets you offer modern formats with automatic fallbacks:

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

The browser picks the first format it supports. Modern browsers get AVIF (smallest), older browsers get WebP, and ancient browsers get JPEG.

AI pitfall
AI always generates plain <img src="photo.jpg"> tags. It never uses <picture>, srcset, or width/height attributes. Every image tag AI produces needs manual optimization.

Format decision table

Image typeRecommended formatWhy
Hero photoAVIF with WebP and JPEG fallbackMaximum compression for the largest asset
Product photoWebP with JPEG fallbackGood balance of quality and compression
Icon or logoSVGScales perfectly, tiny file size
Screenshot with textWebP or PNGSharp text rendering
Transparent imageWebP (supports alpha) or PNGJPEG does not support transparency
02

Responsive images

Sending a 4000x3000 image to a 400px phone wastes ~90% of bandwidthWhat is bandwidth?How much data can flow through a connection at once - like the number of lanes on a highway rather than the speed limit.. The srcset attribute tells the browser which sizes are available:

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

The browser combines sizes with the device pixel ratio to pick the optimal file from srcset. On a 2x display with a 400px viewportWhat is viewport?The visible area of a web page in the browser window. Its size changes depending on the device and whether the user has resized the window., it fetches the 800w image.

03

Loading strategies for images

<!-- LCP image: load immediately with highest priority -->
<img src="hero.webp" loading="eager" fetchpriority="high"
     width="1200" height="600" alt="Hero" />

<!-- Above-fold but not LCP: normal priority -->
<img src="logo.svg" width="200" height="50" alt="Logo" />

<!-- Below-fold: lazy load -->
<img src="product.webp" loading="lazy"
     width="400" height="300" alt="Product" />
Image typeloadingfetchpriorityWhy
LCP / HeroeagerhighDirectly affects your LCP score
Above-fold secondarydefaultdefaultImportant but not critical
Below-fold contentlazydefaultUser has not scrolled here yet
Decorative / backgroundlazylowLeast important
04

Image compression

ToolTypeNotes
Squoosh (squoosh.app)Manual, browser-basedBest for one-off optimization
Sharp (npm package)ProgrammaticBuild-time image processing
Cloudflare ImagesCDN serviceAutomatic format/size on the edge
Imgix / CloudinaryCDN serviceURL-based transformations
// scripts/optimize-images.js
import sharp from 'sharp';

async function optimizeImage(input, output) {
  await sharp(input)
    .resize(1200, 600, { fit: 'cover' })
    .webp({ quality: 80 })
    .toFile(output);
}

// Generates a 1200x600 WebP at 80% quality
// A 5MB JPEG becomes ~100KB WebP

Compression quality guidelines

Use caseWebP qualityAVIF qualityVisual result
Hero / portfolio85-9070-80Near-lossless
Product photos75-8560-70Good, no visible artifacts
Thumbnails60-7545-60Acceptable, small display hides artifacts
Decorative backgrounds50-6535-50Lower quality is fine when blurred or overlaid
05

Image CDNs

An image CDNWhat is cdn?Content Delivery Network - a network of servers around the world that caches your files and serves them from the location closest to the user, making pages load faster. automatically serves images in the optimal format and size based on the requesting device:

<!-- Cloudflare Image Resizing -->
<img src="/cdn-cgi/image/width=800,format=auto/original.jpg" alt="Product" />

<!-- Imgix -->
<img src="https://your-domain.imgix.net/photo.jpg?w=800&auto=format" alt="Product" />

The format=auto parameter means the CDN detects the browser's supported formats and serves AVIF, WebP, or JPEG accordingly.

ApproachProsCons
Manual with SharpFull control, free, works offlineMore setup, manual process
Image CDNAutomatic, handles all devicesMonthly cost, vendor dependency
Framework built-in (Next.js Image)Integrated, easy to useFramework-specific
06

Preventing CLSWhat is cls?Cumulative Layout Shift - measures how much the page layout moves around unexpectedly while loading, with a target score under 0.1. from images

Every image must have explicit dimensions so the browser can reserve space before it loads:

<!-- Bad: browser doesn't know how tall this will be -->
<img src="photo.jpg" alt="Photo" />

<!-- Good: browser reserves 600px of height immediately -->
<img src="photo.jpg" width="800" height="600" alt="Photo" />

<!-- Also good: aspect-ratio in CSS -->
<img src="photo.jpg" alt="Photo"
     style="aspect-ratio: 4/3; width: 100%; height: auto;" />
AI pitfall
AI-generated galleries produce dozens of <img> tags without dimensions, lazy loading, or responsive sizes. A gallery with 20 product images at 2MB each downloads 40MB on page load. With proper optimization, the same gallery might transfer 2MB total.
07

Quick reference

OptimizationImpactEffortWhen to use
Convert to WebP/AVIFVery high (30-50% size reduction)LowAlways
Responsive srcsetHigh (up to 90% savings on mobile)MediumAny image viewed at different sizes
Lazy loadingMedium (defers non-critical loads)LowAll below-fold images
Image CDNVery high (automated everything)Low (setup cost)Production sites with many images
CompressionHigh (50-80% reduction)LowAlways
Explicit dimensionsPrevents CLSLowEvery single image
javascript
// Image optimization patterns

// 1. React component with full optimization
function OptimizedImage({ src, alt, width, height, priority = false }) {
  return (
    <picture>
      <source
        srcSet={`${src}.avif`}
        type="image/avif"
      />
      <source
        srcSet={`${src}.webp`}
        type="image/webp"
      />
      <img
        srcSet={`
          ${src}-400w.jpg 400w,
          ${src}-800w.jpg 800w,
          ${src}-1200w.jpg 1200w
        `}
        sizes="(max-width: 600px) 100vw, 800px"
        src={`${src}-800w.jpg`}
        alt={alt}
        width={width}
        height={height}
        loading={priority ? 'eager' : 'lazy'}
        fetchpriority={priority ? 'high' : undefined}
      />
    </picture>
  );
}

// 2. Build-time optimization with Sharp
import sharp from 'sharp';
import { glob } from 'glob';

async function optimizeAll() {
  const images = await glob('src/images/**/*.{jpg,png}');

  for (const img of images) {
    // Generate WebP version
    await sharp(img)
      .resize(1200)
      .webp({ quality: 80 })
      .toFile(img.replace(/\.(jpg|png)$/, '.webp'));

    // Generate AVIF version
    await sharp(img)
      .resize(1200)
      .avif({ quality: 65 })
      .toFile(img.replace(/\.(jpg|png)$/, '.avif'));
  }
}