Home > OS >  Installing Plaiceholder in Next.js / Webpack 5 causes: Module not found: Can't resolve 'ch
Installing Plaiceholder in Next.js / Webpack 5 causes: Module not found: Can't resolve 'ch

Time:11-18

I'm building on Next.js app and when I install / import Plaiceholder (for generating placeholder images), I get the following error: Module not found: Can't resolve 'child_process'

  • Node v14.18.0
  • Next.js v11.1.2
  • Plaiceholder v2.2.0
  • Sharp v0.29.2

I understand this error message to mean that webpack5 is trying to bundle node packages that aren't available to the client. But I'm not clear why it is doing this. I haven't customized any of the webpack configs, and I can't find any mention of this issue in the Plaiceholder docs. How do I fix it?

NOTE: I want the base64 data URL to get created during the build, so that it available as soon as the page loads (not fetched asynchronously at run time).

Here's my next.config.js

module.exports = {
  reactStrictMode: true,
};

My package.json only has scripts, dependencies, and devDependencies (nothing to change module resolution)

In case it's relevant, here's a simplified example using Plaiceholder:

import Image from "next/image";
import { getPlaiceholder } from "plaiceholder";
import React, { useState } from "react";

...

const { base64 } = await getPlaiceholder(imgUrl);

...

return (<Image
            src={imgUrl}
            placeholder="blur"
            blurDataURL={base64}
          />);

CodePudding user response:

It seems like plaiceholder is not suitable for client-side rendering. I believe that package is for the Node.js environment. That's why you get this error when you try to render your component on the client side.

To solve this problem, you need to move import { getPlaiceholder } from 'plaiceholder' to the NextJS API section. Then you can call that API with your URL data in the body. Then get the base64.

/api/getBase64.js

import { getPlaiceholder } from "plaiceholder";

export default async (req, res) => {
  const { body } = req;
  const { url } = body;

  const { base64 } = getPlaiceholder(url);

  res.status(200).send(base64);
};

/component.js

import Image from "next/image";
import React, { useState, useEffect } from "react";

const [base64, setBase64] = useState()

useEffect(() => {
  (async () => {
   const _base64 = await fetch.post('/api/getBase64', {url: imgUrl}); // wrote for demonstration
   setBase64(_base64);
  })()
})

return (<Image
            src={imgUrl}
            placeholder="blur"
            blurDataURL={base64}
          />);

I know blurDataURL will be undefined until you fetch the data but this is the way how you can use plaiceholder library to manage your images. It should be imported only for the NodeJS environment. If you do not like this approach, you can try to find another library that also works for the browser environment (client)

UPDATED according to the comment:

If you want to generate this base64 at build time, you can use getStaticProps in the pages that use this Image component. NextJS is smart enough to understand which libraries are used in the client-side or server-side. So you can do this:

import { getPlaiceholder } from "plaiceholder";  // place it at the root of file. This will not be bundled inside of client-side code

export async function getStaticProps(context) {
  const { base64 } = await getPlaiceholder(imgUrl);

  return {
    props: { base64 }, // will be passed to the page component as props
  }
}

This way, by using getStaticProps, the page will be created at build time. You can get the base64 prop inside of the page that uses the image component and pass that prop to blurDataURL. Also, you can use this approach with getServerSideProps too.

This is from NextJS website:

Note: You can import modules in top-level scope for use in getServerSideProps. Imports used in getServerSideProps will not be bundled for the client-side.

https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering

  • Related