Fix the 'can't resolve module' error in Next.js

By

Fix the Next.js Module not found: Can't resolve 'fs' error when a Node helper is imported but not used only on the server in App Router or Pages.

~~~

I ran into this issue with Next.js:

Module not found: Can’t resolve ‘fs’

This error means Next.js tried to include a Node.js-only module in the browser bundle. fs reads files from disk. Browsers don’t have a filesystem to read from, so the module doesn’t exist there, and the build fails.

The fix is making sure the code that touches Node.js modules only runs on the server. In Next.js 16 with the App Router that is a Server Component or a Route Handler. On the Pages Router it is getStaticProps() or getServerSideProps().

Let me show you how I hit this, because the cause was not obvious.

How I hit the error

In a Next.js page you can import methods from a file that loads Node.js modules.

This is fine, as long as you also use the imported method in getStaticProps().

Example, I had this Pages Router code:

import { getData } from '../lib/data'

//...

export async function getStaticProps() {
  const data = getData()
  return {
    props: {
      data,
    },
  }
}

When I commented const data = getData(), Next started giving me the error 'fs' module not found because fs was the first module I imported in lib/data.

It might happen with any other Node library you import first. path, child_process, net: anything that only exists in Node.js. Same family of problems as the Can’t resolve ‘net’ case.

Why does this happen?

A Pages Router page runs in two places: on the server and in the browser. When Next.js builds the browser bundle, it strips out getStaticProps() along with every import that is used only inside it.

That last part is the key. The elimination is based on usage. As long as getData() is called inside getStaticProps(), Next.js knows the import is server-only and drops it from the client bundle.

The moment I commented out that call, getData stopped looking server-only. The import stayed in the client bundle, the bundler followed it into lib/data, found require('fs'), and gave up.

This is why anything in getStaticProps() is only called in a server environment, but if we don’t invoke the Node.js function in there, Next.js can’t know that.

App Router fix (Next.js 16)

The App Router does not have this trap. A Server Component (a component under app/ without 'use client') is never bundled for the browser, so you can import a helper that uses fs there and call it, or not call it, without breaking the build:

import { getData } from '@/lib/data'

export default async function Page() {
  const data = getData()
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

You get the same Can't resolve 'fs' error in the App Router only when a Client Component imports @/lib/data, directly or through another file. Move the call into a Server Component or a Route Handler and pass the result down as props.

Pages Router fix (legacy)

Either call the imported function inside getStaticProps() (or getServerSideProps()), or remove the import while you don’t need it.

Commenting out the usage while keeping the import is the one combination that breaks. When I want to temporarily disable the data loading, I now comment out the import line too.

Tagged: Next.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about next: