How to Configure Image Optimization During `next export` with next‑export‑optimize‑images

To configure image optimization during next export, create an export-images.config.js file in your project root and wrap your next.config.js with the withExportImages plugin provided by next-export-optimize-images.

The dc7290/next-export-optimize-images library enables the same image optimization pipeline that powers next/image, but specifically for static HTML exports. All configuration lives in an optional export-images.config.js (or .cjs) file that the library reads at build time and injects into both the Webpack loader and the CLI optimizer.

Configuration Architecture

Understanding how the configuration flows through the build process helps you debug issues and leverage advanced features.

The Wrapper Mechanism

The entry point is src/withExportImages.ts. When you wrap your Next.js config with withExportImages, the wrapper searches for export-images.config.* in your project root. If found, it writes the configuration as a temporary CommonJS module inside node_modules/next-export-optimize-images/ so that both the Webpack loader and the standalone CLI can import it consistently.

Webpack Loader Integration

During the production build, the custom loader (src/loader/index.ts) receives the configuration via getConfig() from src/utils/getConfig.ts. This loader intercepts every next/image import and builds a manifest (.next/next-export-optimize-images-list.nd.json) containing every image size, format, and destination that will be generated.

CLI Optimization Pipeline

After the build finishes, the CLI (src/cli/index.ts) reads the manifest and calls buildOutputInfo() (src/utils/buildOutputInfo.ts) for each entry to compute final output paths, filenames, and conversion rules. Then getOptimizeResult() runs sharp to process images, respecting your quality, sharpOptions, convertFormat, and generateFormats settings.

The export-images.config.js Configuration File

The library exports a Config type that defines all available options. Here is the complete configuration schema:

export type Config = {
  outDir?: string;                    // Destination of static export (default: 'out')
  imageDir?: string;                  // Where optimized images live (default: '_next/static/chunks/images')
  cacheDir?: string;                  // Cache location (default: 'node_modules/.cache')
  ignorePaths?: string[];             // Paths under public/ to skip
  basePath?: string;                  // Must match next.config.js basePath
  externalImageDir?: string;           // For downloaded remote images (default: '_next/static/media')
  quality?: number;                    // JPEG/WEBP/AVIF quality 0-100 (default: 75)
  filenameGenerator?: Function;        // Custom naming logic
  sharpOptions?: {                     // Format-specific sharp settings
    png?: import('sharp').PngOptions;
    jpg?: import('sharp').JpegOptions;
    webp?: import('sharp').WebpOptions;
    avif?: import('sharp').AvifOptions;
  };
  convertFormat?: [string, string][];  // e.g., [['png', 'webp']]
  generateFormats?: ('webp' | 'avif')[]; // Additional formats to generate (default: ['webp'])
  sourceImageParser?: Function;        // Rewrite parsed image paths
  remoteImages?: string[] | Function; // URLs to download at build time
  remoteImagesDownloadsDelay?: number; // Rate limiting in ms
  processingConcurrency?: number;      // Parallel processing limit (default: 10)
  mode?: 'build' | 'export';          // 'export' for static, 'build' for next start
};

All fields are optional; the library provides sensible defaults for each.

Step-by-Step Implementation

1. Create the Configuration File

Create export-images.config.js at your project root:

// export-images.config.js
module.exports = {
  outDir: 'out',
  imageDir: '_next/static/chunks/images',
  quality: 80,
  convertFormat: [['png', 'webp']],
  generateFormats: ['webp', 'avif'],
  filenameGenerator: ({ path, name, width, extension }) => {
    return `${path}/${name}-${width}.${extension}`;
  },
  remoteImages: async () => {
    const res = await fetch('https://example.com/api/images');
    return res.json(); // ['https://cdn.example.com/img1.jpg', ...]
  },
  processingConcurrency: 20,
};

2. Wrap next.config.js

Modify your Next.js configuration to use the wrapper:

// next.config.js
const withExportImages = require('next-export-optimize-images').default;

module.exports = withExportImages({
  images: {
    domains: ['example.com'],
  },
});

The wrapper automatically sets images.loader = 'custom' and injects a Webpack rule that replaces the built-in next-image-loader with next-export-optimize-images-loader (lines 74-96 in src/withExportImages.ts).

3. Execute the Build Pipeline

Add a script to your package.json:

{
  "scripts": {
    "build": "next build",
    "export": "next export",
    "optimize-images": "next-export-optimize-images",
    "static": "npm run build && npm run export && npm run optimize-images"
  }
}

Running npm run static executes the full pipeline: Next.js builds the app, exports static HTML, and the CLI optimizes every referenced image according to your configuration.

Advanced Configuration Examples

Custom Filename Generation

For cache-busting or organizational purposes, implement a custom filenameGenerator:

// export-images.config.js
const crypto = require('crypto');

module.exports = {
  filenameGenerator: ({ path, name, width, extension }) => {
    const hash = crypto
      .createHash('sha256')
      .update(`${path}/${name}`)
      .digest('hex')
      .slice(0, 8);
    return `${path}/${name}-${width}-${hash}.${extension}`;
  },
};

Sharp Format Options

Pass format-specific options directly to the sharp library:

module.exports = {
  sharpOptions: {
    webp: { effort: 6, preset: 'photo' },
    avif: { quality: 70, speed: 4 },
    jpg: { progressive: true, mozjpeg: true },
  },
};

Remote Image Handling

Configure external images with rate limiting:

module.exports = {
  remoteImages: [
    'https://cdn.example.com/logo.png',
    'https://cdn.example.com/banner.jpg',
  ],
  remoteImagesDownloadsDelay: 200, // 200ms between downloads
  externalImageDir: '_next/static/media',
};

Summary

  • Configuration File: Create export-images.config.js at the project root to define optimization settings.
  • Wrapper Integration: Use withExportImages in next.config.js to inject the custom Webpack loader and prepare the CLI environment.
  • Build Flow: Run next build && next export, then execute next-export-optimize-images to process the manifest generated during the Webpack phase.
  • Processing: The CLI (src/cli/index.ts) uses sharp to handle format conversion, quality settings, and concurrent processing according to your configuration.
  • Output: Optimized images are written to the configured imageDir inside your static export folder, with automatic generation of WebP/AVIF variants if specified.

Frequently Asked Questions

Where does next-export-optimize-images read the configuration from?

The library looks for export-images.config.js or export-images.config.cjs in your project root. The withExportImages wrapper (src/withExportImages.ts) loads this file and writes it as a temporary module inside node_modules/next-export-optimize-images/ so that both the Webpack loader (src/loader/index.ts) and the CLI (src/cli/index.ts) can access the same configuration via getConfig() (src/utils/getConfig.ts).

Can I use this with next start instead of next export?

Yes. Set mode: 'build' in your export-images.config.js. This switches the library from static export mode to build mode, allowing you to use the same optimization pipeline with next start rather than generating a static out/ directory.

How do I handle external images from a CDN?

Use the remoteImages configuration option. Provide either a static array of URLs or an async function that returns URLs to download. The CLI (src/cli/index.ts) downloads these during the optimization phase, respecting the remoteImagesDownloadsDelay setting to avoid rate limits, and saves them to externalImageDir (default: _next/static/media).

What is the difference between convertFormat and generateFormats?

convertFormat replaces the original format (e.g., converting PNG to WebP), while generateFormats creates additional copies alongside the original (e.g., keeping PNG but also generating WebP and AVIF versions). Use convertFormat for size reduction, and generateFormats when you need fallback formats for older browsers.

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 →