Skip to main content
CodeRocket
Imageslowformats

Use progressive JPEG encoding

rule · progressive-jpeg

Progressive JPEGs improve perceived performance by showing a preview immediately.

Code Examples

Shell
# Using ImageMagick
identify -verbose image.jpg | grep Interlace
# Progressive: Interlace: JPEG
# Baseline: Interlace: None
 
# Using file command
file image.jpg
# Progressive shows "progressive"
JavaScript
// Browser check using canvas
async function isProgressiveJPEG(url: string): Promise<boolean> {
  const response = await fetch(url)
  const buffer = await response.arrayBuffer()
  const bytes = new Uint8Array(buffer)
 
  // Check for progressive marker (0xFF 0xC2)
  for (let i = 0; i < bytes.length - 1; i++) {
    if (bytes[i] === 0xFF && bytes[i + 1] === 0xC2) {
      return true
    }
  }
  return false
}

Why It Matters

Progressive JPEGs display a low-quality preview immediately rather than loading line-by-line—users see something faster, improving perceived performance even if total load time is similar.

Baseline vs Progressive JPEG

TypeLoading BehaviorUser Experience
BaselineTop-to-bottom, line by lineIncomplete image visible
ProgressiveBlurry → clear (multiple passes)Full image visible immediately

Converting to Progressive

Shell
# ImageMagick
convert input.jpg -interlace JPEG output.jpg
 
# Sharp (Node.js)
sharp('input.jpg')
  .jpeg({ progressive: true, quality: 80 })
  .toFile('output.jpg')
 
# ImageOptim CLI
imageoptim --jpeg-encoding progressive input.jpg

Build Tool Integration

JavaScript
// sharp.config.js for build process
const sharp = require('sharp')
 
async function optimizeJpeg(inputPath, outputPath) {
  await sharp(inputPath)
    .jpeg({
      quality: 80,
      progressive: true,
      mozjpeg: true // Even better compression
    })
    .toFile(outputPath)
}

Webpack Configuration

JavaScript
// webpack.config.js with image-minimizer-webpack-plugin
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')
 
module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.sharpMinify,
          options: {
            encodeOptions: {
              jpeg: {
                quality: 80,
                progressive: true
              }
            }
          }
        }
      })
    ]
  }
}

React Image Component

TSX
// Ensure JPEG generation uses progressive encoding
function ProductImage({ src, alt }: { src: string; alt: string }) {
  // If using image CDN, request progressive format
  const progressiveSrc = src.includes('cloudinary')
    ? src.replace('/upload/', '/upload/fl_progressive/')
    : src
 
  return (
    <img
      src={progressiveSrc}
      alt={alt}
      loading="lazy"
      decoding="async"
    />
  )
}

CDN Progressive Flags

HTML
<!-- Cloudinary -->
<img src="https://res.cloudinary.com/demo/image/upload/fl_progressive/sample.jpg">
 
<!-- Imgix -->
<img src="https://example.imgix.net/image.jpg?fm=pjpg">
 
<!-- Cloudflare -->
<!-- Progressive is typically default for JPEGs -->

When to Use Progressive JPEG

ScenarioRecommendation
Large hero imagesUse progressive
Thumbnails (small)Baseline is fine
Above-the-foldProgressive helps perceived LCP
WebP/AVIF availableLess important (use modern formats)

Standards

  • Use these references as the standard for the final image format, delivery, accessibility, and rendering behavior.
  • Check the implementation against MDN: Responsive images before treating the rule as satisfied.
  • Check the implementation against web.dev: Image performance before treating the rule as satisfied.

Verification

  1. Use browser DevTools to observe image loading
  2. Compare baseline vs progressive on slow connection (DevTools throttling)
  3. Check file sizes—progressive should be similar or smaller
  4. Verify with identify -verbose command