Image Optimization Best Practices for Developers: A Technical Deep Dive

Image Optimization Best Practices for Developers: A Technical Deep Dive

Master the technical aspects of image optimization with this comprehensive guide for developers, covering formats, techniques, workflows, and implementation strategies.

Beyond the Basics: Advanced Image Optimization for Developers

Six months ago, a senior developer on my team insisted our image optimization process was "already perfect." We were using WebP, implementing basic lazy loading, and serving responsive images—what more could we possibly need?

After an audit, we discovered several critical gaps: no AVIF support for compatible browsers, missing width/height attributes causing layout shifts, inefficient lazy loading implementation causing rendering delays, and image CDN configuration that ignored client hints. Addressing these technical shortcomings ultimately improved our Core Web Vitals scores by 27 points and cut image bandwidth by 43%.

This experience highlighted that truly effective image optimization requires deep technical knowledge that goes far beyond simply converting images to WebP. This post provides a comprehensive technical guide for developers who want to implement truly best-in-class image optimization.

Format Selection and Encoding Strategies

Let's start with the technical details of image formats and when to use each:

Vector vs. Raster Considerations

Different image types require fundamentally different approaches:

<!-- SVG for vector graphics -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2L1 21h22L12 2z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>

<!-- Optimized raster format for photographs -->
<img
  src="https://demo.skymage/net/v1/example.com/photo.jpg?f=avif&q=75"
  alt="Product photo"
  width="800"
  height="600"
  onerror="this.onerror=null; this.src='https://demo.skymage/net/v1/example.com/photo.jpg?f=webp&q=75'">

Format Selection Logic

Implement smart content-type-based format selection:

// TypeScript example of format selection logic
interface FormatOptions {
  supportsAVIF: boolean;
  supportsWebP: boolean;
  supportsJPEGXL: boolean;
  hasPerfectTransparency: boolean;
  isPhotographic: boolean;
  hasAnimation: boolean;
  hasText: boolean;
}

function selectOptimalFormat(options: FormatOptions): string {
  const {
    supportsAVIF,
    supportsWebP,
    supportsJPEGXL,
    hasPerfectTransparency,
    isPhotographic,
    hasAnimation,
    hasText
  } = options;

  // Vector graphics should use SVG
  if (hasText && !isPhotographic) {
    return 'svg';
  }

  // Animations require GIF, WebP or AVIF
  if (hasAnimation) {
    if (supportsAVIF) return 'avif';
    if (supportsWebP) return 'webp';
    return 'gif';
  }

  // Photographic content
  if (isPhotographic) {
    if (supportsJPEGXL) return 'jxl'; // Future-proofing
    if (supportsAVIF) return 'avif';
    if (supportsWebP) return 'webp';
    return 'jpeg';
  }

  // Non-photographic with transparency
  if (hasPerfectTransparency) {
    if (supportsAVIF) return 'avif';
    if (supportsWebP) return 'webp';
    return 'png';
  }

  // Default fallback
  if (supportsAVIF) return 'avif';
  if (supportsWebP) return 'webp';
  return 'jpeg';
}

Using the picture Element for Format Fallbacks

Implement robust fallback chains:

<picture>
  <!-- AVIF: Best compression, newest -->
  <source
    srcset="https://demo.skymage/net/v1/example.com/image.jpg?f=avif&q=65 1x,
            https://demo.skymage/net/v1/example.com/image.jpg?f=avif&q=65&dpr=2 2x"
    type="image/avif">

  <!-- WebP: Good compression, better support -->
  <source
    srcset="https://demo.skymage/net/v1/example.com/image.jpg?f=webp&q=75 1x,
            https://demo.skymage/net/v1/example.com/image.jpg?f=webp&q=75&dpr=2 2x"
    type="image/webp">

  <!-- JPEG: Universal fallback -->
  <img
    src="https://demo.skymage/net/v1/example.com/image.jpg?q=80"
    srcset="https://demo.skymage/net/v1/example.com/image.jpg?q=80&dpr=2 2x"
    alt="Description"
    width="800"
    height="600"
    loading="lazy">
</picture>

Quality Selection Algorithms

Implement perceptual quality techniques:

// PHP example of content-aware quality selection
public function determineOptimalQuality(string $imagePath, string $format): int
{
    // Load image
    $image = $this->imageProcessor->load($imagePath);

    // Extract image characteristics
    $characteristics = $this->analyzeImageCharacteristics($image);

    // Base quality varies by format
    $baseQuality = match($format) {
        'avif' => 65,
        'webp' => 75,
        'jpeg' => 80,
        'png' => 90,
        default => 75,
    };

    // Adjust based on content type
    if ($characteristics['isPhotographic']) {
        // Photos can often use lower quality with good results
        $adjustment = -5;
    } elseif ($characteristics['hasText']) {
        // Text needs higher quality to remain crisp
        $adjustment = +10;
    } elseif ($characteristics['hasGradients']) {
        // Gradients often show banding at low quality
        $adjustment = +5;
    } else {
        $adjustment = 0;
    }

    // Adjust based on color complexity
    $colorComplexity = $characteristics['colorComplexity']; // 0-100 scale
    $complexityAdjustment = (int)($colorComplexity / 20) - 2; // -2 to +3 range

    // Calculate final quality
    $finalQuality = min(95, max(40, $baseQuality + $adjustment + $complexityAdjustment));

    return $finalQuality;
}

Responsive Images Implementation

Optimal responsive images require attention to several technical details:

The Complete Responsive Image Syntax

Here's a complete example with all responsive attributes:

<picture>
  <!-- Art direction: different crops for different viewports -->
  <source
    media="(max-width: 640px)"
    srcset="https://demo.skymage/net/v1/example.com/product-mobile.jpg?f=avif 640w,
            https://demo.skymage/net/v1/example.com/product-mobile.jpg?f=avif&dpr=2 1280w"
    sizes="100vw"
    type="image/avif">

  <source
    media="(max-width: 640px)"
    srcset="https://demo.skymage/net/v1/example.com/product-mobile.jpg?f=webp 640w,
            https://demo.skymage/net/v1/example.com/product-mobile.jpg?f=webp&dpr=2 1280w"
    sizes="100vw"
    type="image/webp">

  <!-- Desktop image variants (resolution switching) -->
  <source
    srcset="https://demo.skymage/net/v1/example.com/product.jpg?f=avif&w=400 400w,
            https://demo.skymage/net/v1/example.com/product.jpg?f=avif&w=800 800w,
            https://demo.skymage/net/v1/example.com/product.jpg?f=avif&w=1200 1200w,
            https://demo.skymage/net/v1/example.com/product.jpg?f=avif&w=1600 1600w"
    sizes="(max-width: 1200px) 50vw, 33vw"
    type="image/avif">

  <source
    srcset="https://demo.skymage/net/v1/example.com/product.jpg?f=webp&w=400 400w,
            https://demo.skymage/net/v1/example.com/product.jpg?f=webp&w=800 800w,
            https://demo.skymage/net/v1/example.com/product.jpg?f=webp&w=1200 1200w,
            https://demo.skymage/net/v1/example.com/product.jpg?f=webp&w=1600 1600w"
    sizes="(max-width: 1200px) 50vw, 33vw"
    type="image/webp">

  <!-- Fallback image -->
  <img
    src="https://demo.skymage/net/v1/example.com/product.jpg?w=800"
    srcset="https://demo.skymage/net/v1/example.com/product.jpg?w=400 400w,
            https://demo.skymage/net/v1/example.com/product.jpg?w=800 800w,
            https://demo.skymage/net/v1/example.com/product.jpg?w=1200 1200w,
            https://demo.skymage/net/v1/example.com/product.jpg?w=1600 1600w"
    sizes="(max-width: 1200px) 50vw, 33vw"
    alt="Product description"
    width="800"
    height="600"
    loading="lazy"
    decoding="async"
    fetchpriority="auto">
</picture>

Automating the sizes Attribute

The sizes attribute is crucial but often misconfigured. Here's how to generate it programmatically:

// Function to calculate sizes attribute based on layout
function calculateSizesAttribute(elementSelector, breakpoints) {
  const element = document.querySelector(elementSelector);
  if (!element) return '100vw'; // Default fallback

  // Get element's parent to determine constraints
  const parent = element.parentElement;
  const parentStyles = window.getComputedStyle(parent);

  // Calculate sizes based on layout constraints
  let sizesValue = '';

  // Sort breakpoints in descending order
  const sortedBreakpoints = [...breakpoints].sort((a, b) => b - a);

  for (const breakpoint of sortedBreakpoints) {
    // We need to test how the element behaves at each breakpoint
    // This is a simplification - in production you'd need to use
    // more sophisticated layout calculation
    const relativeWidth = calculateRelativeWidth(element, breakpoint);
    sizesValue += `(max-width: ${breakpoint}px) ${relativeWidth}vw, `;
  }

  // Add default size (for largest viewport)
  const defaultRelativeWidth = calculateRelativeWidth(element);
  sizesValue += `${defaultRelativeWidth}vw`;

  return sizesValue;
}

// Helper function to estimate element's width relative to viewport
function calculateRelativeWidth(element, breakpoint = window.innerWidth) {
  // This is a simplified approach - production code would
  // need to handle more complex layouts
  const parent = element.parentElement;
  const parentStyles = window.getComputedStyle(parent);

  // Check if parent has max-width constraint
  let parentMaxWidth = Infinity;
  if (parentStyles.maxWidth !== 'none') {
    parentMaxWidth = parseInt(parentStyles.maxWidth);
  }

  // Determine if element is constrained by parent max-width
  const effectiveViewportWidth = Math.min(breakpoint, parentMaxWidth);

  // Calculate how much of the parent container the element occupies
  const parentWidth = parent.offsetWidth;
  const elementWidth = element.offsetWidth;
  const elementRatio = elementWidth / parentWidth;

  // Calculate parent container's width relative to viewport
  const parentToViewportRatio = parentWidth / window.innerWidth;

  // Element width as percentage of viewport
  const elementToViewportRatio = elementRatio * parentToViewportRatio;

  // Return as percentage value
  return Math.round(elementToViewportRatio * 100);
}

// Usage
const imageSizes = calculateSizesAttribute('.product-image', [576, 768, 992, 1200]);
document.querySelector('.product-image').setAttribute('sizes', imageSizes);

Avoiding Common CLS Issues with Images

Prevent Cumulative Layout Shift with proper image handling:

<!-- Approach 1: Explicit dimensions -->
<img
  src="https://demo.skymage/net/v1/example.com/product.jpg"
  width="800"
  height="600"
  alt="Product">

<!-- Approach 2: Aspect ratio via CSS -->
<div style="aspect-ratio: 4/3;">
  <img
    src="https://demo.skymage/net/v1/example.com/product.jpg"
    alt="Product"
    style="width: 100%; height: 100%; object-fit: cover;">
</div>

<!-- Approach 3: Aspect ratio via padding technique -->
<div style="position: relative; padding-bottom: 75%;">
  <img
    src="https://demo.skymage/net/v1/example.com/product.jpg"
    alt="Product"
    style="position: absolute; width: 100%; height: 100%; object-fit: cover;">
</div>

Advanced Lazy Loading Techniques

Upgrade your lazy loading implementation:

Modern Native Lazy Loading with Proper Thresholds

<!-- Basic native lazy loading -->
<img
  src="https://demo.skymage/net/v1/example.com/product.jpg"
  loading="lazy"
  alt="Product"
  width="800"
  height="600">

Pair with Intersection Observer for more control:

// Enhanced lazy loading with Intersection Observer
document.addEventListener('DOMContentLoaded', () => {
  const lazyImages = document.querySelectorAll('img[loading="lazy"]');

  const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;

        // If the image has a data-src attribute, use that for the source
        if (img.dataset.src) {
          img.src = img.dataset.src;

          // Handle srcset as well if present
          if (img.dataset.srcset) {
            img.srcset = img.dataset.srcset;
          }

          // Clean up data attributes
          delete img.dataset.src;
          delete img.dataset.srcset;
        }

        // Upgrade image quality if needed
        if (img.dataset.highQualitySrc) {
          // Create a new image to preload the high-quality version
          const highQualityImg = new Image();
          highQualityImg.onload = () => {
            img.src = highQualityImg.src;
            if (img.dataset.highQualitySrcset) {
              img.srcset = img.dataset.highQualitySrcset;
            }
          };
          highQualityImg.src = img.dataset.highQualitySrc;
        }

        // Stop observing this image
        observer.unobserve(img);
      }
    });
  }, {
    // Load when image is within 200px of viewport
    rootMargin: '200px 0px',
    threshold: 0.01
  });

  // Observe all lazy images
  lazyImages.forEach(img => {
    imageObserver.observe(img);
  });
});

Priority Hints for Critical Images

Use the fetchpriority attribute to prioritize important images:

<!-- Hero image: high priority -->
<img
  src="https://demo.skymage/net/v1/example.com/hero.jpg"
  alt="Hero"
  fetchpriority="high"
  width="1600"
  height="900">

<!-- Non-critical image: low priority -->
<img
  src="https://demo.skymage/net/v1/example.com/secondary.jpg"
  alt="Secondary content"
  fetchpriority="low"
  loading="lazy"
  width="800"
  height="600">

Preloading Critical Images

Preload LCP images for faster rendering:

<!-- Preload hero image in the document head -->
<link
  rel="preload"
  as="image"
  href="https://demo.skymage/net/v1/example.com/hero.jpg?w=1200&f=avif"
  type="image/avif"
  imagesrcset="https://demo.skymage/net/v1/example.com/hero.jpg?w=400&f=avif 400w,
               https://demo.skymage/net/v1/example.com/hero.jpg?w=800&f=avif 800w,
               https://demo.skymage/net/v1/example.com/hero.jpg?w=1200&f=avif 1200w"
  imagesizes="100vw">

<!-- Also preload WebP version for browsers without AVIF support -->
<link
  rel="preload"
  as="image"
  href="https://demo.skymage/net/v1/example.com/hero.jpg?w=1200&f=webp"
  type="image/webp"
  imagesrcset="https://demo.skymage/net/v1/example.com/hero.jpg?w=400&f=webp 400w,
               https://demo.skymage/net/v1/example.com/hero.jpg?w=800&f=webp 800w,
               https://demo.skymage/net/v1/example.com/hero.jpg?w=1200&f=webp 1200w"
  imagesizes="100vw">

Client Hints Implementation

Use Client Hints to deliver optimized images:

Setting Up Client Hints

<!-- Enable client hints in the document head -->
<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, RTT, Downlink, ECT">

<!-- Set lifetime for client hints -->
<meta http-equiv="Accept-CH-Lifetime" content="86400"> <!-- 24 hours -->

<!-- Delegate client hints to image CDN -->
<meta http-equiv="Critical-CH" content="DPR, Width, Viewport-Width">
<meta http-equiv="Delegate-CH" content="DPR https://demo.skymage, Width https://demo.skymage, Viewport-Width https://demo.skymage">

Using Client Hints with Images

<!-- Image utilizing client hints -->
<img
  src="https://demo.skymage/net/v1/example.com/product.jpg?ch=true"
  alt="Product"
  width="800"
  height="600"
  sizes="50vw">

Server-Side Processing of Client Hints

// PHP example of handling Client Hints
public function processImageRequest(Request $request): Response
{
    // Extract client hints from request headers
    $clientHints = [
        'dpr' => (float)($request->header('DPR') ?? 1.0),
        'width' => (int)($request->header('Width') ?? 0),
        'viewportWidth' => (int)($request->header('Viewport-Width') ?? 0),
        'deviceMemory' => (float)($request->header('Device-Memory') ?? 4.0),
        'rtt' => (int)($request->header('RTT') ?? 0),
        'downlink' => (float)($request->header('Downlink') ?? 10.0),
        'ect' => $request->header('ECT') ?? '4g',
    ];

    // Extract requested image parameters
    $imagePath = $request->input('path');
    $requestedWidth = $request->input('w');
    $requestedFormat = $request->input('f');

    // Use client hints if explicit parameters aren't provided
    $width = $requestedWidth ?: $this->calculateOptimalWidth($clientHints);
    $format = $requestedFormat ?: $this->determineOptimalFormat($request->userAgent(), $clientHints);
    $quality = $this->determineOptimalQuality($clientHints);

    // Generate and serve optimized image
    $optimizedImage = $this->imageService->optimize($imagePath, [
        'width' => $width,
        'format' => $format,
        'quality' => $quality,
    ]);

    return response($optimizedImage->getContents())
        ->header('Content-Type', $optimizedImage->getMimeType())
        ->header('Cache-Control', 'public, max-age=31536000')
        ->header('Vary', 'Accept, DPR, Width, Viewport-Width');
}

private function calculateOptimalWidth(array $clientHints): int
{
    $dpr = $clientHints['dpr'];
    $viewportWidth = $clientHints['viewportWidth'];
    $requestedWidth = $clientHints['width'];

    // If explicit Width hint is available, use that
    if ($requestedWidth > 0) {
        return $requestedWidth;
    }

    // Otherwise estimate based on viewport and DPR
    if ($viewportWidth > 0) {
        // Assume the image takes up about half the viewport
        // This is a simplification - real logic would be more sophisticated
        $estimatedWidth = $viewportWidth * 0.5 * $dpr;

        // Round to nearest standard size
        return $this->roundToStandardSize((int)$estimatedWidth);
    }

    // Fallback to a reasonable default
    return 800;
}

private function roundToStandardSize(int $width): int
{
    // Round to nearest standard breakpoint
    $standardSizes = [320, 480, 640, 768, 1024, 1280, 1536, 1920, 2560];

    foreach ($standardSizes as $standardSize) {
        if ($width <= $standardSize) {
            return $standardSize;
        }
    }

    return end($standardSizes);
}

Performance Measurement and Monitoring

Implement proper measurement for image optimization:

Core Web Vitals Tracking for Images

Track and report on image-specific metrics:

// Register a performance observer for LCP
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    // Check if LCP element is an image
    if (entry.element && entry.element.tagName === 'IMG') {
      console.log('LCP is an image:', entry.element.src);

      // Report image-specific LCP
      analytics.track('image_lcp', {
        url: entry.element.src,
        lcp_value: entry.startTime,
        element_id: entry.element.id || null,
        element_class: entry.element.className || null,
        viewport_width: window.innerWidth,
        connection_type: navigator.connection ? navigator.connection.effectiveType : null
      });
    }
  }
}).observe({ type: 'largest-contentful-paint', buffered: true });

// Track CLS caused by images
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    // Check if layout shift involves images
    if (entry.sources) {
      const involvesImages = entry.sources.some(source =>
        source.node && source.node.tagName === 'IMG'
      );

      if (involvesImages) {
        // Report image-related layout shift
        analytics.track('image_layout_shift', {
          cls_value: entry.value,
          elements: entry.sources.map(source => ({
            tag: source.node ? source.node.tagName : null,
            id: source.node ? source.node.id : null,
            src: source.node && source.node.tagName === 'IMG' ? source.node.src : null
          }))
        });
      }
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

Resource Timing Analysis for Images

Analyze image loading performance:

// Analyze image resource timing
function analyzeImagePerformance() {
  // Get all resource entries
  const entries = performance.getEntriesByType('resource');

  // Filter for image resources
  const imageEntries = entries.filter(entry =>
    entry.initiatorType === 'img' ||
    /\.(jpe?g|png|gif|webp|avif|svg)$/i.test(entry.name)
  );

  // Analyze each image
  const imageStats = imageEntries.map(entry => {
    // Find the corresponding DOM element
    const selector = `img[src="${entry.name}"],
                      source[srcset*="${entry.name}"],
                      img[srcset*="${entry.name}"]`;
    const element = document.querySelector(selector);

    // Calculate metrics
    const tcpTime = entry.connectEnd - entry.connectStart;
    const requestTime = entry.responseStart - entry.requestStart;
    const downloadTime = entry.responseEnd - entry.responseStart;
    const totalTime = entry.responseEnd - entry.startTime;

    return {
      url: entry.name,
      element: element ? {
        id: element.id,
        class: element.className,
        width: element.width,
        height: element.height,
        loading: element.loading,
        inViewport: isInViewport(element)
      } : null,
      size: entry.transferSize,
      timing: {
        dns: entry.domainLookupEnd - entry.domainLookupStart,
        tcp: tcpTime,
        request: requestTime,
        download: downloadTime,
        total: totalTime
      },
      cached: entry.transferSize === 0
    };
  });

  // Group images by performance characteristics
  const performanceGroups = {
    fast: imageStats.filter(img => img.timing.total < 200),
    medium: imageStats.filter(img => img.timing.total >= 200 && img.timing.total < 1000),
    slow: imageStats.filter(img => img.timing.total >= 1000),
    cached: imageStats.filter(img => img.cached)
  };

  // Identify problematic images
  const problemImages = imageStats.filter(img => {
    const hasLongDownload = img.timing.download > 500;
    const isLarge = img.size > 200 * 1024; // 200KB
    const isVisible = img.element && img.element.inViewport;

    return (hasLongDownload || isLarge) && isVisible;
  });

  return {
    totalImages: imageStats.length,
    totalSize: imageStats.reduce((sum, img) => sum + img.size, 0),
    averageLoadTime: imageStats.reduce((sum, img) => sum + img.timing.total, 0) / imageStats.length,
    performanceGroups,
    problemImages
  };
}

// Helper function to check if element is in viewport
function isInViewport(element) {
  const rect = element.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= window.innerHeight &&
    rect.right <= window.innerWidth
  );
}

Build and Deployment Pipeline for Images

Implement an optimized image workflow:

Automated Build-Time Optimization

Here's a webpack configuration for image optimization:

// webpack.config.js
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

module.exports = {
  // ... other webpack config
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024 // Inline images < 10kb
          }
        }
      }
    ]
  },
  optimization: {
    minimizer: [
      // ... other minimizers
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminMinify,
          options: {
            plugins: [
              ['gifsicle', { interlaced: true, optimizationLevel: 3 }],
              ['jpegtran', { progressive: true }],
              ['optipng', { optimizationLevel: 5 }],
              [
                'svgo',
                {
                  plugins: [
                    {
                      name: 'preset-default',
                      params: {
                        overrides: {
                          removeViewBox: false,
                          addAttributesToSVGElement: {
                            params: {
                              attributes: [
                                { xmlns: 'http://www.w3.org/2000/svg' }
                              ]
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              ]
            ]
          }
        },
        generator: [
          {
            preset: 'webp',
            implementation: ImageMinimizerPlugin.imageminGenerate,
            options: {
              plugins: ['imagemin-webp']
            }
          },
          {
            preset: 'avif',
            implementation: ImageMinimizerPlugin.imageminGenerate,
            options: {
              plugins: ['imagemin-avif']
            }
          }
        ]
      })
    ]
  },
  // Generate manifest of optimized images
  plugins: [
    // ... other plugins
    new (class {
      apply(compiler) {
        compiler.hooks.emit.tapAsync('ImageManifestPlugin', (compilation, callback) => {
          const manifest = {};

          // Collect all image assets
          for (const filename in compilation.assets) {
            if (/\.(jpe?g|png|gif|webp|avif|svg)$/i.test(filename)) {
              const asset = compilation.assets[filename];

              manifest[filename] = {
                size: asset.size(),
                originalName: filename.replace(/\.(webp|avif)$/, '')
              };
            }
          }

          // Add manifest to compilation
          compilation.assets['image-manifest.json'] = {
            source: () => JSON.stringify(manifest, null, 2),
            size: () => JSON.stringify(manifest, null, 2).length
          };

          callback();
        });
      }
    })()
  ]
};

Git Hooks for Image Optimization

Use Git hooks to ensure all committed images are optimized:

#!/bin/bash
# .git/hooks/pre-commit

# Get all staged image files
staged_images=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(jpg|jpeg|png|gif|svg)$')

if [ -n "$staged_images" ]; then
  echo "Optimizing staged images..."

  # Create a temporary directory for optimized images
  temp_dir=$(mktemp -d)

  for image in $staged_images; do
    # Skip already optimized images
    if grep -q "$image" .optimized-images 2>/dev/null; then
      echo "Skipping already optimized image: $image"
      continue
    fi

    echo "Optimizing: $image"

    # Optimize based on file extension
    if [[ $image =~ \.(jpg|jpeg)$ ]]; then
      # Optimize JPEG
      jpegoptim --strip-all --max=85 "$image"
    elif [[ $image =~ \.png$ ]]; then
      # Optimize PNG
      optipng -o5 "$image"
    elif [[ $image =~ \.gif$ ]]; then
      # Optimize GIF
      gifsicle -O3 -o "${temp_dir}/$(basename "$image")" "$image"
      mv "${temp_dir}/$(basename "$image")" "$image"
    elif [[ $image =~ \.svg$ ]]; then
      # Optimize SVG
      svgo --multipass "$image"
    fi

    # Add to optimized images list
    echo "$image" >> .optimized-images

    # Re-add the optimized image to staging
    git add "$image"
  done

  # Clean up
  rm -rf "$temp_dir"

  echo "Image optimization complete"
fi

# Continue with commit
exit 0

Implementing with Skymage

Let's look at how to implement these best practices using Skymage:

Direct Integration with HTML

<!-- Basic implementation -->
<img
  src="https://demo.skymage/net/v1/example.com/image.jpg?auto=format,compress&q=auto"
  alt="Description"
  width="800"
  height="600"
  loading="lazy">

<!-- Advanced implementation with all optimizations -->
<picture>
  <source
    type="image/avif"
    srcset="https://demo.skymage/net/v1/example.com/image.jpg?f=avif&w=400&q=65 400w,
            https://demo.skymage/net/v1/example.com/image.jpg?f=avif&w=800&q=65 800w,
            https://demo.skymage/net/v1/example.com/image.jpg?f=avif&w=1200&q=65 1200w"
    sizes="(max-width: 768px) 100vw, 50vw">

  <source
    type="image/webp"
    srcset="https://demo.skymage/net/v1/example.com/image.jpg?f=webp&w=400&q=75 400w,
            https://demo.skymage/net/v1/example.com/image.jpg?f=webp&w=800&q=75 800w,
            https://demo.skymage/net/v1/example.com/image.jpg?f=webp&w=1200&q=75 1200w"
    sizes="(max-width: 768px) 100vw, 50vw">

  <img
    src="https://demo.skymage/net/v1/example.com/image.jpg?w=800&q=80"
    srcset="https://demo.skymage/net/v1/example.com/image.jpg?w=400&q=80 400w,
            https://demo.skymage/net/v1/example.com/image.jpg?w=800&q=80 800w,
            https://demo.skymage/net/v1/example.com/image.jpg?w=1200&q=80 1200w"
    sizes="(max-width: 768px) 100vw, 50vw"
    alt="Description"
    width="800"
    height="600"
    loading="lazy">
</picture>

Using the JavaScript SDK

// Skymage JavaScript SDK integration
import { SkymageOptimizer } from '@skymage/js';

// Initialize with your account details
const skymage = new SkymageOptimizer({
  domain: 'demo.skymage',
  detectWebpSupport: true,
  detectAvifSupport: true,
  detectViewportWidth: true,
  detectDPR: true,
  useSrcset: true
});

// Basic URL transformation
const optimizedUrl = skymage.getOptimizedUrl('https://example.com/image.jpg', {
  width: 800,
  quality: 'auto',
  format: 'auto'
});

// Generate complete picture element
function generateOptimizedPicture(originalUrl, alt, sizes, options = {}) {
  const {
    width = 800,
    height = 600,
    loading = 'lazy',
    widths = [400, 800, 1200, 1600],
    formats = ['avif', 'webp', 'original']
  } = options;

  const picture = document.createElement('picture');

  // Add source elements for each format
  formats.forEach(format => {
    if (format === 'original') return; // Skip original format for source elements

    const source = document.createElement('source');
    source.type = `image/${format}`;

    // Generate srcset with multiple widths
    const srcset = widths.map(w => {
      const url = skymage.getOptimizedUrl(originalUrl, {
        width: w,
        format
      });
      return `${url} ${w}w`;
    }).join(', ');

    source.srcset = srcset;
    source.sizes = sizes;

    picture.appendChild(source);
  });

  // Create img element (fallback)
  const img = document.createElement('img');
  img.src = skymage.getOptimizedUrl(originalUrl, { width });

  // Generate srcset for fallback image
  const imgSrcset = widths.map(w => {
    const url = skymage.getOptimizedUrl(originalUrl, { width: w });
    return `${url} ${w}w`;
  }).join(', ');

  img.srcset = imgSrcset;
  img.sizes = sizes;
  img.alt = alt;
  img.width = width;
  img.height = height;
  img.loading = loading;

  picture.appendChild(img);

  return picture;
}

// Usage
const productPicture = generateOptimizedPicture(
  'https://example.com/product.jpg',
  'Product description',
  '(max-width: 768px) 100vw, 50vw',
  {
    width: 800,
    height: 600,
    loading: 'lazy',
    widths: [400, 800, 1200]
  }
);

document.querySelector('.product-container').appendChild(productPicture);

Server-Side Integration

// PHP server-side integration
class SkymageImageOptimizer
{
    private string $domain = 'demo.skymage';

    public function getOptimizedUrl(string $originalUrl, array $options = []): string
    {
        // Extract domain and path from original URL
        $urlParts = parse_url($originalUrl);
        $path = $urlParts['path'] ?? '';
        $host = $urlParts['host'] ?? '';

        if (empty($host)) {
            throw new InvalidArgumentException('Invalid URL provided');
        }

        // Build base URL
        $optimizedUrl = "https://{$this->domain}/net/v1/{$host}{$path}";

        // Add query parameters
        $queryParams = [];

        // Process options
        if (!empty($options['width'])) {
            $queryParams['w'] = (int)$options['width'];
        }

        if (!empty($options['height'])) {
            $queryParams['h'] = (int)$options['height'];
        }

        if (!empty($options['format'])) {
            $queryParams['f'] = $options['format'];
        }

        if (!empty($options['quality'])) {
            $queryParams['q'] = $options['quality'];
        }

        if (!empty($options['crop'])) {
            $queryParams['fit'] = $options['crop'];
        }

        if (!empty($options['blur'])) {
            $queryParams['blur'] = (int)$options['blur'];
        }

        if (!empty($options['dpr'])) {
            $queryParams['dpr'] = (float)$options['dpr'];
        }

        // Add client hints support
        if (!empty($options['clientHints']) && $options['clientHints'] === true) {
            $queryParams['ch'] = 'true';
        }

        // Add automatic optimization
        if (empty($options['format']) && empty($options['quality'])) {
            $queryParams['auto'] = 'format,quality';
        }

        // Combine into URL
        if (!empty($queryParams)) {
            $queryString = http_build_query($queryParams);
            $optimizedUrl .= "?{$queryString}";
        }

        return $optimizedUrl;
    }

    public function generatePictureElement(
        string $originalUrl,
        string $alt,
        string $sizes,
        array $options = []
    ): string {
        // Default options
        $options = array_merge([
            'width' => 800,
            'height' => 600,
            'loading' => 'lazy',
            'widths' => [400, 800, 1200],
            'formats' => ['avif', 'webp', 'original'],
            'quality' => [
                'avif' => 65,
                'webp' => 75,
                'original' => 80
            ]
        ], $options);

        // Start building picture element
        $html = '<picture>';

        // Add source elements for each format
        foreach ($options['formats'] as $format) {
            if ($format === 'original') continue;

            $quality = $options['quality'][$format] ?? 'auto';

            $html .= '<source type="image/' . htmlspecialchars($format) . '" ';

            // Generate srcset
            $srcset = [];
            foreach ($options['widths'] as $width) {
                $url = $this->getOptimizedUrl($originalUrl, [
                    'width' => $width,
                    'format' => $format,
                    'quality' => $quality
                ]);

                $srcset[] = htmlspecialchars($url) . ' ' . $width . 'w';
            }

            $html .= 'srcset="' . implode(', ', $srcset) . '" ';
            $html .= 'sizes="' . htmlspecialchars($sizes) . '">';
        }

        // Add fallback img element
        $quality = $options['quality']['original'] ?? 'auto';
        $fallbackUrl = $this->getOptimizedUrl($originalUrl, [
            'width' => $options['width'],
            'quality' => $quality
        ]);

        $html .= '<img ';
        $html .= 'src="' . htmlspecialchars($fallbackUrl) . '" ';

        // Generate srcset for fallback
        $srcset = [];
        foreach ($options['widths'] as $width) {
            $url = $this->getOptimizedUrl($originalUrl, [
                'width' => $width,
                'quality' => $quality
            ]);

            $srcset[] = htmlspecialchars($url) . ' ' . $width . 'w';
        }

        $html .= 'srcset="' . implode(', ', $srcset) . '" ';
        $html .= 'sizes="' . htmlspecialchars($sizes) . '" ';
        $html .= 'alt="' . htmlspecialchars($alt) . '" ';
        $html .= 'width="' . (int)$options['width'] . '" ';
        $html .= 'height="' . (int)$options['height'] . '" ';
        $html .= 'loading="' . htmlspecialchars($options['loading']) . '">';

        $html .= '</picture>';

        return $html;
    }
}

// Usage in a PHP application
$imageOptimizer = new SkymageImageOptimizer();

// Simple usage
$optimizedUrl = $imageOptimizer->getOptimizedUrl(
    'https://example.com/product.jpg',
    ['width' => 800, 'auto' => 'format,quality']
);

// Generate complete picture element
$pictureHtml = $imageOptimizer->generatePictureElement(
    'https://example.com/product.jpg',
    'Product description',
    '(max-width: 768px) 100vw, 50vw',
    [
        'width' => 800,
        'height' => 600,
        'loading' => 'lazy',
        'widths' => [400, 800, 1200]
    ]
);

echo $pictureHtml;

Conclusion

Implementing comprehensive image optimization requires going beyond the basics. By applying the advanced techniques outlined in this post—format selection based on content type, complete responsive image syntax, client hints integration, sophisticated lazy loading, and proper performance measurement—you can achieve significantly better results than standard approaches.

Remember that image optimization is not a one-time task but an ongoing process that requires continued monitoring and refinement. The technical approaches outlined here should be integrated into your development workflow to ensure consistent implementation across your application.

Skymage provides all the tools needed to implement these advanced techniques with minimal development effort, allowing you to focus on your application while still delivering best-in-class image performance.

Ready to implement advanced image optimization techniques in your application? Contact Skymage to discuss the best approach for your specific technical requirements.

Share this article:

Ready to Optimize Your Images?

Join thousands of developers and companies who trust Skymage for their image optimization needs.

No credit card required. 14-day free trial.