Steve Kinney

Advanced Image Optimization Techniques

Advanced image optimization goes beyond basic responsive images and lazy loading. This guide covers CDN integration, build-time optimization, performance monitoring, and automated optimization pipelines that ensure your images perform optimally at scale in production environments.

CDN Integration and Optimization

CDN Configuration Helper

// utils/cdn.ts
interface CDNConfig {
  baseUrl: string;
  transformations: {
    format?: 'auto' | 'webp' | 'avif';
    quality?: number;
    width?: number;
    height?: number;
    fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
    dpr?: number;
  };
}

export class CDNImageOptimizer {
  constructor(private config: CDNConfig) {}

  generateUrl(path: string, options: Partial<CDNConfig['transformations']> = {}): string {
    const transformations = { ...this.config.transformations, ...options };

    // Cloudinary format
    if (this.config.baseUrl.includes('cloudinary')) {
      return this.cloudinaryUrl(path, transformations);
    }

    // Imgix format
    if (this.config.baseUrl.includes('imgix')) {
      return this.imgixUrl(path, transformations);
    }

    // Cloudflare Images format
    if (this.config.baseUrl.includes('cloudflare')) {
      return this.cloudflareUrl(path, transformations);
    }

    // Generic CDN with query params
    return this.genericUrl(path, transformations);
  }

  private cloudinaryUrl(path: string, transforms: any): string {
    const transformString = Object.entries(transforms)
      .map(([key, value]) => {
        switch (key) {
          case 'width':
            return `w_${value}`;
          case 'height':
            return `h_${value}`;
          case 'quality':
            return `q_${value}`;
          case 'format':
            return value === 'auto' ? 'f_auto' : `f_${value}`;
          case 'dpr':
            return `dpr_${value}`;
          case 'fit':
            return `c_${value}`;
          default:
            return '';
        }
      })
      .filter(Boolean)
      .join(',');

    return `${this.config.baseUrl}/${transformString}/${path}`;
  }

  private imgixUrl(path: string, transforms: any): string {
    const params = new URLSearchParams();

    Object.entries(transforms).forEach(([key, value]) => {
      switch (key) {
        case 'width':
          params.set('w', value.toString());
          break;
        case 'height':
          params.set('h', value.toString());
          break;
        case 'quality':
          params.set('q', value.toString());
          break;
        case 'format':
          if (value === 'auto') {
            params.set('auto', 'format');
          } else {
            params.set('fm', value.toString());
          }
          break;
        case 'dpr':
          params.set('dpr', value.toString());
          break;
        case 'fit':
          params.set('fit', value.toString());
          break;
      }
    });

    return `${this.config.baseUrl}/${path}?${params.toString()}`;
  }

  private cloudflareUrl(path: string, transforms: any): string {
    const options = Object.entries(transforms)
      .map(([key, value]) => {
        switch (key) {
          case 'width':
            return `width=${value}`;
          case 'height':
            return `height=${value}`;
          case 'quality':
            return `quality=${value}`;
          case 'format':
            return `format=${value}`;
          case 'fit':
            return `fit=${value}`;
          default:
            return '';
        }
      })
      .filter(Boolean)
      .join(',');

    return `${this.config.baseUrl}/cdn-cgi/image/${options}/${path}`;
  }

  private genericUrl(path: string, transforms: any): string {
    const params = new URLSearchParams();
    Object.entries(transforms).forEach(([key, value]) => {
      params.set(key, value.toString());
    });

    return `${this.config.baseUrl}/${path}?${params.toString()}`;
  }
}

// React hook for CDN images
function useCDNImage() {
  const optimizer = useMemo(() => {
    return new CDNImageOptimizer({
      baseUrl: process.env.NEXT_PUBLIC_CDN_URL || '',
      transformations: {
        format: 'auto',
        quality: 80,
      },
    });
  }, []);

  const generateSrcSet = useCallback(
    (path: string, widths: number[]) => {
      return widths
        .map((width) => {
          const url = optimizer.generateUrl(path, { width });
          return `${url} ${width}w`;
        })
        .join(', ');
    },
    [optimizer],
  );

  const generateUrl = useCallback(
    (path: string, options?: any) => {
      return optimizer.generateUrl(path, options);
    },
    [optimizer],
  );

  return { generateUrl, generateSrcSet };
}

Smart CDN Caching Strategy

// CDN cache optimization
interface CacheStrategy {
  images: {
    maxAge: number;
    staleWhileRevalidate: number;
    immutable?: boolean;
  };
  transforms: {
    maxAge: number;
    staleWhileRevalidate: number;
  };
}

const cacheStrategy: CacheStrategy = {
  images: {
    maxAge: 31536000, // 1 year for original images
    staleWhileRevalidate: 86400, // 1 day
    immutable: true,
  },
  transforms: {
    maxAge: 2592000, // 30 days for transformed images
    staleWhileRevalidate: 86400, // 1 day
  },
};

// Cloudflare Worker for image optimization
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const { pathname, searchParams } = url;

    // Extract image path and transformation params
    const imagePath = pathname.replace('/images/', '');
    const width = searchParams.get('w');
    const quality = searchParams.get('q') || '80';
    const format = searchParams.get('f') || 'auto';

    // Check cache first
    const cacheKey = `image:${imagePath}:${width}:${quality}:${format}`;
    const cached = await caches.default.match(cacheKey);

    if (cached) {
      return cached;
    }

    // Fetch and transform image
    const transformedImage = await transformImage(imagePath, {
      width: width ? parseInt(width) : undefined,
      quality: parseInt(quality),
      format,
    });

    // Cache response
    const response = new Response(transformedImage, {
      headers: {
        'Content-Type': `image/${format}`,
        'Cache-Control': `public, max-age=${cacheStrategy.transforms.maxAge}, stale-while-revalidate=${cacheStrategy.transforms.staleWhileRevalidate}`,
        'CDN-Cache-Control': `max-age=${cacheStrategy.transforms.maxAge}`,
      },
    });

    // Store in cache
    await caches.default.put(cacheKey, response.clone());

    return response;
  },
};

Image Loading Performance Patterns

Priority Loading Strategy

// Priority-based image loading
interface ImagePriority {
  path: string;
  priority: 'high' | 'medium' | 'low';
  viewport: 'above-fold' | 'below-fold';
  critical?: boolean;
}

class ImageLoadingManager {
  private loadingQueue: Map<string, ImagePriority> = new Map();
  private loadedImages: Set<string> = new Set();
  private maxConcurrent = 3;
  private currentLoading = 0;

  addToQueue(image: ImagePriority): void {
    this.loadingQueue.set(image.path, image);
    this.processQueue();
  }

  private async processQueue(): Promise<void> {
    if (this.currentLoading >= this.maxConcurrent) return;

    // Sort by priority
    const sorted = Array.from(this.loadingQueue.entries()).sort(([, a], [, b]) => {
      const priorityWeight = { high: 3, medium: 2, low: 1 };
      const viewportWeight = { 'above-fold': 2, 'below-fold': 1 };
      const criticalWeight = a.critical ? 10 : 0;

      const scoreA = priorityWeight[a.priority] + viewportWeight[a.viewport] + criticalWeight;
      const scoreB =
        priorityWeight[b.priority] + viewportWeight[b.viewport] + (b.critical ? 10 : 0);

      return scoreB - scoreA;
    });

    const [path, priority] = sorted[0] || [];
    if (!path || this.loadedImages.has(path)) return;

    this.loadingQueue.delete(path);
    this.currentLoading++;

    try {
      await this.loadImage(path, priority);
      this.loadedImages.add(path);
    } finally {
      this.currentLoading--;
      if (this.loadingQueue.size > 0) {
        this.processQueue();
      }
    }
  }

  private loadImage(path: string, priority: ImagePriority): Promise<void> {
    return new Promise((resolve, reject) => {
      const img = new Image();

      // Set priority attributes
      if (priority.critical) {
        img.decoding = 'sync';
        img.fetchPriority = 'high';
      } else {
        img.decoding = 'async';
        img.fetchPriority = priority.priority;
      }

      img.onload = () => resolve();
      img.onerror = reject;
      img.src = path;
    });
  }
}

// React hook for priority loading
function usePriorityImageLoading() {
  const manager = useMemo(() => new ImageLoadingManager(), []);

  const loadImage = useCallback(
    (path: string, options: Omit<ImagePriority, 'path'>) => {
      manager.addToQueue({ path, ...options });
    },
    [manager],
  );

  return { loadImage };
}

Adaptive Loading Based on Network

// Network-aware image loading
function useNetworkAwareImages() {
  const [connectionType, setConnectionType] = useState<string>('4g');
  const [effectiveType, setEffectiveType] = useState<string>('4g');

  useEffect(() => {
    if ('connection' in navigator) {
      const connection = (navigator as any).connection;

      setConnectionType(connection.type || '4g');
      setEffectiveType(connection.effectiveType || '4g');

      const updateConnection = () => {
        setConnectionType(connection.type || '4g');
        setEffectiveType(connection.effectiveType || '4g');
      };

      connection.addEventListener('change', updateConnection);
      return () => connection.removeEventListener('change', updateConnection);
    }
  }, []);

  const getImageQuality = useCallback(() => {
    if (effectiveType === 'slow-2g' || effectiveType === '2g') {
      return 40; // Low quality for slow connections
    }
    if (effectiveType === '3g') {
      return 60; // Medium quality
    }
    return 80; // High quality for 4g+
  }, [effectiveType]);

  const getImageFormat = useCallback(() => {
    if (effectiveType === 'slow-2g' || effectiveType === '2g') {
      return 'webp'; // Smaller file size
    }
    return 'auto'; // Let CDN decide
  }, [effectiveType]);

  const shouldLoadImage = useCallback(
    (priority: 'high' | 'medium' | 'low') => {
      if (effectiveType === 'slow-2g') {
        return priority === 'high';
      }
      if (effectiveType === '2g') {
        return priority !== 'low';
      }
      return true; // Load all images on faster connections
    },
    [effectiveType],
  );

  return {
    connectionType,
    effectiveType,
    getImageQuality,
    getImageFormat,
    shouldLoadImage,
  };
}

Build-Time Image Optimization

Webpack Image Optimization

// webpack.config.js
const ImageMinimizerPlugin = require('imagemin-webpack-plugin').default;
const imageminWebp = require('imagemin-webp');
const imageminAvif = require('imagemin-avif');

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              outputPath: 'images/',
              name: '[name].[contenthash].[ext]',
            },
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 80,
              },
              optipng: {
                enabled: false,
              },
              pngquant: {
                quality: [0.65, 0.9],
                speed: 4,
              },
              gifsicle: {
                interlaced: false,
              },
              webp: {
                quality: 80,
              },
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new ImageMinimizerPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      minimizerOptions: {
        plugins: [
          // Generate WebP versions
          ['imagemin-webp', { quality: 80 }],
          // Generate AVIF versions
          ['imagemin-avif', { quality: 80 }],
        ],
      },
    }),
  ],
};

Next.js Image Component Configuration

// next.config.js
module.exports = {
  images: {
    domains: ['example.com', 'cdn.example.com'],
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 768, 1024, 1280, 1600],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    minimumCacheTTL: 31536000, // 1 year
    dangerouslyAllowSVG: false,
  },
  // Custom image loader
  loader: 'custom',
  loaderFile: './lib/imageLoader.js',
};

// lib/imageLoader.js
export default function customLoader({ src, width, quality }) {
  const params = new URLSearchParams();
  params.set('w', width.toString());
  if (quality) params.set('q', quality.toString());

  return `https://cdn.example.com/images/${src}?${params.toString()}`;
}

// Optimized Image component
import Image from 'next/image';

interface OptimizedImageProps {
  src: string;
  alt: string;
  priority?: boolean;
  className?: string;
  sizes?: string;
}

export function OptimizedImage({
  src,
  alt,
  priority = false,
  className,
  sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw',
}: OptimizedImageProps) {
  return (
    <Image
      src={src}
      alt={alt}
      fill
      priority={priority}
      className={className}
      sizes={sizes}
      quality={80}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R+JNnI5Ny+3V6ArkHqiOmQPd4aZJ2JB6HZ8qNHqidHTbGDKLGbewlVN0BxvM3nLzrflPIxdoFhYFgtWSDMIJOwAP7tBQXExN3SLBpFhLzJ2TL6BwuZKAACQoM9VlkjSPXLH4KYo5J8W8oo0OTKmVzRqIAyTnOFAhV0pMDj8Xb8aMsL43/tB/9k="
    />
  );
}

Performance Monitoring and Metrics

// Image performance monitoring
interface ImageMetrics {
  src: string;
  loadTime: number;
  fileSize: number;
  renderTime: number;
  format: string;
  dimensions: { width: number; height: number };
  cacheHit: boolean;
}

class ImagePerformanceMonitor {
  private metrics: Map<string, ImageMetrics> = new Map();
  private observer: PerformanceObserver;

  constructor() {
    this.setupPerformanceObserver();
    this.setupIntersectionObserver();
  }

  private setupPerformanceObserver(): void {
    this.observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (entry.entryType === 'resource' && entry.initiatorType === 'img') {
          this.recordImageLoad(entry as PerformanceResourceTiming);
        }
      }
    });

    this.observer.observe({ entryTypes: ['resource'] });
  }

  private recordImageLoad(entry: PerformanceResourceTiming): void {
    const metrics: ImageMetrics = {
      src: entry.name,
      loadTime: entry.duration,
      fileSize: entry.transferSize,
      renderTime: entry.responseEnd - entry.startTime,
      format: this.extractFormat(entry.name),
      dimensions: { width: 0, height: 0 }, // Will be updated when image loads
      cacheHit: entry.transferSize === 0,
    };

    this.metrics.set(entry.name, metrics);
    this.reportMetrics(metrics);
  }

  private extractFormat(url: string): string {
    const match = url.match(/\.(\w+)(\?|$)/);
    return match ? match[1] : 'unknown';
  }

  private reportMetrics(metrics: ImageMetrics): void {
    // Report to analytics
    if (typeof gtag !== 'undefined') {
      gtag('event', 'image_load', {
        event_category: 'performance',
        event_label: metrics.format,
        value: Math.round(metrics.loadTime),
        custom_map: {
          file_size: metrics.fileSize,
          cache_hit: metrics.cacheHit,
        },
      });
    }

    // Report to custom analytics
    fetch('/api/analytics/image-performance', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(metrics),
    }).catch(() => {
      // Silently fail
    });
  }

  getMetrics(): ImageMetrics[] {
    return Array.from(this.metrics.values());
  }

  getAverageLoadTime(): number {
    const metrics = this.getMetrics();
    return metrics.reduce((sum, m) => sum + m.loadTime, 0) / metrics.length;
  }

  getCacheHitRate(): number {
    const metrics = this.getMetrics();
    const cacheHits = metrics.filter((m) => m.cacheHit).length;
    return cacheHits / metrics.length;
  }

  private setupIntersectionObserver(): void {
    // Monitor when images become visible
    const imageObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting && entry.target.tagName === 'IMG') {
          const img = entry.target as HTMLImageElement;
          const metrics = this.metrics.get(img.src);

          if (metrics) {
            metrics.dimensions = {
              width: img.naturalWidth,
              height: img.naturalHeight,
            };
          }
        }
      });
    });

    // Observe all images
    document.querySelectorAll('img').forEach((img) => {
      imageObserver.observe(img);
    });
  }
}

// React hook for image performance monitoring
function useImagePerformanceMonitoring() {
  const [monitor] = useState(() => new ImagePerformanceMonitor());

  const getPerformanceReport = useCallback(() => {
    return {
      totalImages: monitor.getMetrics().length,
      averageLoadTime: monitor.getAverageLoadTime(),
      cacheHitRate: monitor.getCacheHitRate(),
      metrics: monitor.getMetrics(),
    };
  }, [monitor]);

  return { getPerformanceReport };
}

Last modified on .