How the Caching Mechanism Prevents Repeated Image Optimization in next-export-optimize-images

The caching mechanism prevents repeated image optimization by storing SHA-256 hashes of source images in a JSON manifest; when processing images, it compares the current file hash against stored values and skips the expensive Sharp pipeline entirely if they match.

The next-export-optimize-images package accelerates static site builds by eliminating redundant image processing work. By persisting optimization results between runs, the tool ensures that unchanged images are copied from cache rather than re-processed through CPU-intensive Sharp operations.

How the Cache Manifest Works

The caching system relies on a JSON manifest file that tracks previously optimized images. Located by default at node_modules/.cache/next-export-optimize-images/cached-images.json, this file stores an array of objects containing output paths and their corresponding source hashes.

When the CLI initializes, it invokes readCacheManifest from src/cli/utils/cache.ts to load this data into memory:

// src/cli/utils/cache.ts
export const readCacheManifest = (filePath = defaultCacheFilePath): CacheImages => {
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf-8'))
  } catch (_) {
    return []
  }
}

If the cache file does not exist—such as during the first run—the function returns an empty array, and the CLI proceeds to optimize all images normally.

The SHA-256 Hash Comparison Logic

The core optimization prevention occurs in src/cli/index.ts within the getOptimizeResult function. For each image processed, the system performs a cryptographic comparison to determine if the source file has changed since the last run.

First, the CLI computes a SHA-256 hash of the current image buffer:

const hash = createHash('sha256').update(imageBuffer).digest('hex')

Next, it searches the in-memory cacheImages array for an entry matching the current output path. If found, it compares the stored hash against the newly computed value:

if (currentCacheImage?.hash === hash) {
  await fs.copy(outputPath, filePath)
  cacheMeasurement()
  cliProgressBarIncrement()
  return
}

When the hashes match, the CLI bypasses the Sharp processing pipeline entirely, copying the previously optimized file directly from the cache directory to the final destination. This eliminates CPU-intensive operations for unchanged images.

Cache Hit vs. Cache Miss Workflow

The distinction between cache hits and misses determines the processing path for each image.

Cache Hit Workflow:

  1. Locate existing entry in cached-images.json
  2. Compute SHA-256 hash of source image
  3. Compare with stored hash
  4. If equal, copy cached file to output directory
  5. Skip Sharp optimization entirely

Cache Miss Workflow:

  1. No matching entry found, or hash mismatch detected
  2. Process image through Sharp with specified quality, format, and dimensions
  3. Write optimized image to output directory
  4. Update or create cache entry with new hash
  5. Persist updated manifest to disk

When hashes differ—indicating the source image has been modified—the CLI updates the cache entry with the new hash value after processing:

if (currentCacheImage !== undefined) {
  currentCacheImage.hash = hash
}

Persisting Cache Between Runs

After processing all images, the CLI writes the updated manifest back to disk using writeCacheManifest from src/cli/utils/cache.ts:

export const writeCacheManifest = (cacheImages: CacheImages, filePath = defaultCacheFilePath) => {
  fs.writeFileSync(filePath, JSON.stringify(cacheImages), 'utf-8')
}

This persistence ensures that subsequent builds benefit from previous optimization work. The cache directory defaults to node_modules/.cache/next-export-optimize-images, but this location can be configured through options.

Disabling the Cache

For scenarios requiring fresh optimization—such as debugging or when the cache may be corrupted—the system provides a --noCache CLI flag. When invoked, this bypasses the cache lookup and write operations entirely:

npx next-export-optimize-images --noCache

Programmatically, the same behavior is achieved by setting noCache: true in the options object passed to optimizeImages:

import { optimizeImages } from 'next-export-optimize-images/src/cli'

await optimizeImages({
  manifestJsonPath: './.next/next-export-optimize-images-list.nd.json',
  noCache: true,
  config: getConfig(),
  nextImageConfig: nextConfig.images,
})

Summary

  • The caching mechanism stores image metadata in cached-images.json within node_modules/.cache/next-export-optimize-images
  • SHA-256 hashes of source images

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →