How next-export-optimize-images Works with Next.js Static Export
The next-export-optimize-images plugin extends Next.js static export by injecting a custom webpack loader that collects image metadata during the build into a manifest file, then executes a post-build CLI process using Sharp to generate optimized, responsive image variants before deployment.
The dc7290/next-export-optimize-images repository solves the limitation that standard Next.js image optimization requires a running Node.js server. By hooking into the static export pipeline, this plugin generates all optimized assets ahead of time, enabling fully static deployments on CDN or edge hosting without sacrificing image performance.
Hooking into the Next.js Configuration
The plugin entry point is the withExportImages function exported from src/withExportImages.ts. This wrapper modifies the Next.js configuration to intercept image processing during the build.
Configuration Validation and Setup
The plugin first validates that images.unoptimized is not enabled in next.config.js (src/withExportImages.ts:15-19). It then loads the user-provided configuration from export-images.config.{js,cjs} and writes it into node_modules/next-export-optimize-images/export-images.config.js so the loader and CLI can require it later (lines 32-58).
Webpack Loader Injection
The core mechanism replaces the default next-image-loader with a chain that runs the plugin's custom loader first, then the original Next.js loader (src/withExportImages.ts:73-96). An alias is added so Webpack can resolve the plugin's loader (lines 98-100).
Build-Time Image Collection
During production builds, the custom webpack loader (src/loader/index.ts) intercepts every image import processed by Next.js.
The Custom Webpack Loader
The loader receives the JSON metadata (staticImageData) that Next.js emits for each image and extracts the original src path (src/loader/index.ts:28-30). Using the buildOutputInfo utility (src/utils/buildOutputInfo.ts), it calculates every output variant including different widths, formats, and custom filenames (src/loader/index.ts:38-45).
Manifest Generation
For each image variant, the loader appends a line to a newline-delimited JSON manifest located at .next/next-export-optimize-images-list.nd.json (src/loader/index.ts:47-50). This manifest serves as the source of truth for the optimization CLI.
CLI Optimization Pipeline
After next build completes, the user runs next-export-optimize-images to execute the post-build optimization. The CLI entry point is src/cli/index.ts.
Manifest Processing
The CLI reads the manifest file (src/cli/index.ts:74-77) and expands the list with remote images (if configured) and public-folder images (lines 84-138). For each entry, buildOutputInfo is called again to compute exact output paths and formats (lines 102-110).
Remote Image Handling
External images configured via remoteImages are downloaded once into the configured externalImageDir (src/cli/external-images/index.ts). The CLI ensures these assets are processed alongside local images.
Sharp-Based Optimization
The plugin uses Sharp to perform the heavy lifting. Each image buffer is resized to every required width, optionally converted to formats like WebP or AVIF, and written to both the cache and final output directory (src/cli/index.ts:91-126). A cache directory at node_modules/.cache/next-export-optimize-images skips re-optimizing unchanged files (lines 70-89).
Runtime Component Support
For remote images rendered at request time, the plugin provides specialized components. When using <RemotePicture/> or <RemoteImage/>, the components write entries to the same manifest at runtime (production only), ensuring external assets are captured for the CLI step. See the generation loop in src/components/server/remote-picture.tsx (lines 24-58).
Configuration System
The plugin reads its configuration via src/utils/getConfig.ts. This utility loads the user-provided config that was copied into node_modules during withExportImages execution, evaluates any stringified functions (such as filenameGenerator), and returns a fully typed Config object (lines 37-55).
Implementation Examples
Integrate the plugin in next.config.js:
const withExportImages = require('next-export-optimize-images')
module.exports = withExportImages({
output: 'export',
// Additional Next.js config
})
Add the post-build script to package.json:
{
"scripts": {
"build": "next build && next-export-optimize-images"
}
}
Configure optimization settings in export-images.config.js:
module.exports = {
imageDir: '_next/static/optimized',
filenameGenerator: ({ path, name, width, extension }) =>
`${path}/${name}-${width}.${extension}`,
quality: 80,
generateFormats: ['webp', 'avif'],
}
Use the optimized Image component:
import Image from 'next-export-optimize-images/image'
export default function Home() {
return (
<Image
src="/photos/hero.jpg"
width={1920}
height={1080}
alt="Hero"
/>
)
}
For external images:
import RemotePicture from 'next-export-optimize-images/remote-picture'
export default function Avatar() {
return (
<RemotePicture
src="https://example.com/avatar.png"
width={200}
height={200}
alt="User avatar"
/>
)
}
Summary
- The
withExportImagesfunction wraps Next.js config to inject a custom webpack loader and validate settings - The custom loader writes image metadata to
.next/next-export-optimize-images-list.nd.jsonduring the build - The post-build CLI processes the manifest using Sharp to generate responsive variants and alternative formats
- Remote images are supported through runtime manifest entries in
src/components/server/remote-picture.tsx - Optimized images are cached in
node_modules/.cache/next-export-optimize-imagesand output to/_next/static/chunks/images(or a customimageDir)
Frequently Asked Questions
How does next-export-optimize-images differ from Next.js built-in image optimization?
Next.js built-in optimization requires a Node.js server to dynamically serve optimized images based on request parameters. next-export-optimize-images performs all optimization during the build phase, generating static files suitable for deployment to static hosting or CDNs without a running server.
Where does the plugin store the list of images to optimize?
During the build, the custom webpack loader writes entries to .next/next-export-optimize-images-list.nd.json using newline-delimited JSON format. The CLI reads this manifest to determine which images require processing, including both local and remote assets.
Can next-export-optimize-images handle external remote images?
Yes. The plugin provides <RemotePicture/> and <RemoteImage/> components that write remote URLs to the manifest during server rendering. The CLI then downloads these to the configured externalImageDir and processes them alongside local assets using the same Sharp pipeline.
What image processing library does the plugin use?
The plugin uses Sharp to resize images to multiple widths, convert between formats like WebP and AVIF, and control quality settings. Processed images are cached in node_modules/.cache/next-export-optimize-images to avoid redundant processing in subsequent builds.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →