reactjswebpacknext.jsnext.js13nextjs-image

How can I load an image from an installed node_module in Next.js?


I am installed a module in my project that includes a <img /> tag within it, and next.js is unable to properly render the image. Currently, the image in the module is being displayed as follows:

<img src="[Object Object]" />

I think this issue may exist after version 11 of Next.js. I am using Next.js version 14. In Next.js version 14, most aspects work smoothly without any issues. However, there is a problem with rendering images that are located in the installed node_modules.

In my opinion, this issue occurs because of the use of the next.js webpack loader, which imports the image module in the following format:

export interface StaticImageData {
    src: string;
    height: number;
    width: number;
    blurDataURL?: string;
    blurWidth?: number;
    blurHeight?: number;
}

declare module '*.png' {
  const content: import('../dist/shared/lib/image-external').StaticImageData

  export default content
}

I attempted to use the next-images package but unfortunately, I was unable to solve the issue or problem I was facing.


Solution

  • I finally realized that Next.js uses the next-image-loader as the image loader in Webpack, based on the configuration found in the Next.js repository at this link.

    Next.js webpack image loader config
    {
      test: nextImageLoaderRegex,
      loader: 'next-image-loader',
      issuer: { not: regexLikeCss },
      dependency: { not: ['url'] },
      resourceQuery: {
        not: [
          new RegExp(WEBPACK_RESOURCE_QUERIES.metadata),
          new RegExp(WEBPACK_RESOURCE_QUERIES.metadataRoute),
          new RegExp(WEBPACK_RESOURCE_QUERIES.metadataImageMeta),
        ],
      },
      options: {
        isDev: dev,
        compilerType,
        basePath: config.basePath,
        assetPrefix: config.assetPrefix,
      },
    }
    

    In order to address the compatibility issue with next-image-loader, I excluded it for the specific node_module and opted instead to use the asset module of webpack to load images within the installed package. I achieved this by combining the image loader of create-react-app webpack configurations and Next.js webpack image loader. You can find the Webpack image loader configuration of Create React App's react-scripts package at this link.

    My Custom webpack config in next.config.js to load all images
    /** @type {import('next').NextConfig} */
    
    const path = require("path")
    
    const IMAGE_INLINE_SIZE_LIMIT = parseInt(
        process.env.IMAGE_INLINE_SIZE_LIMIT || '10240'
    );
    
    
    const NEXT_IMAGE_LOADER_EXCLUDE = ['SPECIFIC_NODE_MODULE_NAME'].map((module) => {
        return path.resolve(__dirname, `node_modules/${module}`);
    })
    
    module.exports = {
        webpack(config) {
            const nextImageLoaderIndex = config.module.rules.findIndex(item => item.loader === "next-image-loader");
            const { loader, options, ...ruleProps } = config.module.rules[nextImageLoaderIndex];
    
            config.module.rules[nextImageLoaderIndex] = {
                ...ruleProps,
                oneOf: [
                    {
                        exclude: NEXT_IMAGE_LOADER_EXCLUDE,
                        loader,
                        options,
                    },
                    {
                        type: 'asset',
                        parser: {
                            dataUrlCondition: {
                                maxSize: IMAGE_INLINE_SIZE_LIMIT,
                            },
                        },
                    }
                ]
            };
            return config
        }
    }
    

    If there is a better way to solve this problem, please mention it.