How to Implement a Custom sourceImageParser for Image Paths in next-export-optimize-images

A custom sourceImageParser allows you to intercept and transform image paths before the optimizer processes them, enabling support for CDN prefixes, query strings, or custom directory structures by returning { pathWithoutName, name, extension }.

The sourceImageParser configuration option in dc7290/next-export-optimize-images provides a powerful extensibility hook for projects with non-standard image storage conventions. When the optimizer encounters an image import, it uses this parser to extract the directory path, filename, and extension—information that drives filename generation and output directory calculation. By overriding the default parser in export-images.config.js, you can rewrite paths to handle CDN domains, cache-busting query parameters, or custom folder mappings.

What is sourceImageParser?

By default, next-export-optimize-images uses defaultImageParser to split every image src into three components:

  • pathWithoutName: The directory path excluding the filename
  • name: The filename without extension
  • extension: The file extension (e.g., png, jpg)

The built-in parser handles typical static imports like src="/images/foo.png". However, if your images reside on external CDNs, include version query strings, or require path rewriting, the sourceImageParser option lets you replace this logic entirely.

According to the source code in src/utils/getConfig.ts, the library evaluates your custom parser from export-images.config.js at runtime. Then, in src/utils/buildOutputInfo.ts, the optimizer calls your parser (or falls back to the default) to obtain these three values for every processed image.

The Parser API and Return Types

Your custom parser must conform to the following TypeScript signature:

type SourceImageParser = (props: {
  src: string
  defaultParser: DefaultImageParser
}) => {
  pathWithoutName: string
  name: string
  extension: string
}

type DefaultImageParser = (src: string) => {
  pathWithoutName: string
  name: string
  extension: string
}

The function receives two arguments:

  • src: The raw image source string (after optional basePath stripping)
  • defaultParser: A reference to the built-in parser, useful for delegating standard path logic

You must return an object containing exactly three string properties. These values determine the final output paths and filenames generated during the export phase.

Common Use Cases

Stripping CDN Domains

When images are stored under a CDN prefix like https://cdn.example.com/assets/, you typically want to remove the domain before the optimizer calculates output directories:

sourceImageParser: ({ src, defaultParser }) => {
  const cleanSrc = src.replace(/^https?:\/\/cdn\.example\.com\//, '');
  const { pathWithoutName, name, extension } = defaultParser(cleanSrc);
  
  return { pathWithoutName, name, extension };
}

Removing Query Parameters for Cache Busting

If your build system appends version query strings (e.g., /img/photo.png?v=123), strip them before parsing to ensure the optimizer recognizes the correct extension:

sourceImageParser: ({ src, defaultParser }) => {
  const cleanSrc = src.split('?')[0];
  return defaultParser(cleanSrc);
}

Rewriting Output Directories

To map all images from a source folder like /static/media/ to a different output location such as assets/:

sourceImageParser: ({ src, defaultParser }) => {
  const { pathWithoutName, name, extension } = defaultParser(src);
  
  return {
    pathWithoutName: `assets/${pathWithoutName}`,
    name,
    extension
  };
}

Step-by-Step Implementation

  1. Create or edit export-images.config.js in your project root.

  2. Add the sourceImageParser property with a function matching the API signature:

// export-images.config.js
/**
 * @type {import('next-export-optimize-images').Config}
 */
const config = {
  filenameGenerator: ({ path, name, width, extension }) =>
    `${path}/${name}_${width}.${extension}`,

  sourceImageParser: ({ src, defaultParser }) => {
    // Example: Strip CDN domain and query strings
    const cleanSrc = src
      .replace(/^https?:\/\/cdn\.example\.com\//, '')
      .split('?')[0];
    
    const { pathWithoutName, name, extension } = defaultParser(cleanSrc);
    
    // Rewrite path for output organization
    const rewrittenPath = `assets/${pathWithoutName}`;
    
    return { pathWithoutName: rewrittenPath, name, extension };
  },
};

module.exports = config;
  1. Delegate to defaultParser when you only need to tweak specific parts of the path, or bypass it entirely for complete custom logic.

  2. Run your export build. The library automatically evaluates the function via Function(...) in src/utils/getConfig.ts and applies it to every image during the optimization phase.

Testing Your Custom Parser

The repository includes tests in __tests__/utils/buildOutputInfo/index.test.ts that verify the parser hook works correctly. You can validate your implementation using a similar pattern:

test('Custom sourceImageParser transforms paths correctly', () => {
  const customParser = jest.fn(() => ({
    pathWithoutName: 'custom/assets',
    name: 'hero',
    extension: 'webp',
  }));

  const input = {
    src: 'https://cdn.example.com/static/hero.png?v=2',
    width: 800,
    config: { sourceImageParser: customParser },
  };

  const output = buildOutputInfo(input);

  expect(customParser).toHaveBeenCalledWith({
    src: input.src,
    defaultParser: expect.any(Function),
  });
  
  // Verify the returned values influence output paths
  expect(output[0].output).toContain('custom/assets');
});

Running npm test in the repository confirms that your parser receives the correct arguments and that the returned values directly influence filename generation logic in src/utils/buildOutputInfo.ts.

Summary

  • sourceImageParser is defined in export-images.config.js and evaluated by src/utils/getConfig.ts at runtime.
  • The parser receives { src, defaultParser } and must return { pathWithoutName, name, extension }.
  • Use it to handle CDN domains, query strings, or custom directory mappings before the optimizer processes images.
  • The defaultParser reference allows you to leverage standard parsing logic while modifying specific components.
  • Return values directly control output path generation in src/utils/buildOutputInfo.ts.

Frequently Asked Questions

How do I access the default parser logic inside my custom sourceImageParser?

The sourceImageParser function receives defaultParser as a property in its argument object. Call defaultParser(src) to obtain the standard { pathWithoutName, name, extension } result, then modify whichever fields your project requires. This approach ensures you only override the specific path transformations you need while preserving the core parsing logic.

Can I use sourceImageParser to change file extensions dynamically?

Yes. While the parser typically extracts extensions from the source path, you can override the extension field in your return object. For example, normalize .jpeg to .jpg for compatibility, or force specific extensions based on naming conventions. The optimizer will use your returned extension for format conversion and filename generation.

What happens if my sourceImageParser returns an invalid object?

src/utils/buildOutputInfo.ts expects exactly three string properties: pathWithoutName, name, and extension. If any are missing or non-strings, the filename generation logic may produce broken paths or throw runtime errors during the export phase. Always ensure your parser returns a complete, valid object matching the SourceImageParser type signature.

Where does next-export-optimize-images load my sourceImageParser configuration?

The library loads export-images.config.js via src/utils/getConfig.ts, which evaluates the sourceImageParser property into a real function using Function(...). This resolved function is then passed into src/utils/buildOutputInfo.ts and invoked for every image processed during the static export. Ensure your config file is in the project root or correctly referenced in your build setup.

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 →