Ibad Shaikh
Ibad Shaikh

Reputation: 3556

Module not found: Can't resolve 'fs' in Next.js application

Unable to identify what's happening in my next.js app. As fs is a default file system module of nodejs. It is giving the error of module not found.

enter image description here

enter image description here

Upvotes: 160

Views: 311950

Answers (25)

Seng Wee
Seng Wee

Reputation: 624

if you're on Next.js, you might want to do it like this,

you might want to import fs like this,

import { promises as fs } from 'fs';
.
.
.

await fs.readFile(process.cwd() + "/lib/db-schema.md");

Upvotes: 0

Thomas Heniart
Thomas Heniart

Reputation: 21

For those of you having the same issue with "node:fs", here is a next.config.(m)js that works :

const nextConfig = {
  // Your config
  webpack: (config, { nextRuntime }) => {
    if (nextRuntime !== "nodejs") {
      const { IgnorePlugin } = require("webpack");
      const ignoreNode = new IgnorePlugin({ resourceRegExp: /node:.*/ });
      config.plugins.push(ignoreNode);
    }
    return config;
  },
};

module.exports = nextConfig;

Upvotes: -1

Mohsen
Mohsen

Reputation: 61

Sometimes code that is only intended to be run on the server mistakenly is used on the client, this was my case. I had a util.ts file that exported a few functions and one of those functions was using the fs module and was only intended to be used on the server.

To "mark" a file as "server-only", that is, only intended to be run on the server, install the server-only package and put import "server-only" at the top of the file.

Likewise, to mark a file as "client-only", there is a package called "client-only".

So, first install the package:

npm i server-only

then, mark the file as server-only by putting this at the top of the file:

import "server-only"

More info: NEXT.JS Docs - Keeping Server-only Code out of the Client Environment

Upvotes: 0

Dushan Wijesinghe
Dushan Wijesinghe

Reputation: 365

Only this worked for me

// next.config.js
const nextConfig = {
  webpack: (config, { isServer }) => {
    // Only attempt to resolve fs module on the server side
    if (!isServer) {
      config.resolve.fallback = {
        fs: false,
      };
    }
    return config;
  },
};

module.exports = nextConfig;

Upvotes: -1

Chris Josh
Chris Josh

Reputation: 475

On the server, there are two runtimes where parts of your application code can be rendered:

  • The Node.js Runtime (default) has access to all Node.js APIs and compatible packages from the ecosystem.
  • The Edge Runtime is based on Web APIs.

So, if you have something like this in your code, consider removing it or updating it to use more features from node:

export const runtime = 'edge' // 'nodejs' (default) | 'edge'

FYI: while adding the object below to your package.json may remove errors such as "module not found...", you won't be able to use the module as expected.

  ...
  "browser": {
    "fs": false,
    "path": false
  }
  ...

You can read more here

Upvotes: 0

asigari0711
asigari0711

Reputation: 73

I started receiving this error today when deploying to vercel, adding the following to my package.json seemed to work

"browser": { "fs": false },

Upvotes: -1

Mawnir fas
Mawnir fas

Reputation: 105

in Next 13 an 14 you need to add 'use server' at the top of your page:

'use server'
import { promises as fs } from 'fs';

const Test = async () => {

    const file = await fs.readFile(process.cwd() + '../../data/gamelist.json', 'utf8');
    const data = JSON.parse(file);
...
}

export default Test;

Upvotes: 5

OZZIE
OZZIE

Reputation: 7378

Many needlessly long answers here, here's the TLDR solution, it only works Server side (why see other answers). (Answer Without App/src dir - using Pages)

Statically generated page:

export async function getStaticProps() {
  const fs = require('fs');
  const { promises: { readFile } } = fs;

  const csharp = (await readFile('./data/c#.txt')).toString();

  return {
    props: {
      csharp,
    }
  }
}

or Server side generated page:

export async function getServerSideProps() {
  const fs = require('fs');
  const { promises: { readFile } } = fs;

  const csharp = (await readFile('./data/c#.txt')).toString();

  return {
    props: {
      csharp,
    }
  }
}

Upvotes: 2

diazlp
diazlp

Reputation: 750

I spent hours on this and the solution is also here on Stackoverflow but on different issue -> https://stackoverflow.com/a/67478653/17562602

Hereby I asked for MOD permission to reshare this, since this issue is the first one to show up on Google and probably more and more people stumble would upon the same problem as I am, so I'll try to saved them some sweats

Soo, You need to add this in your next.config.js

module.exports = {

  // Can be safely removed in newer versions of Next.js
  future: {

    // by default, if you customize webpack config, they switch back to version 4.
    // Looks like backward compatibility approach.
    webpack5: true,   
  },

  webpack(config) {
    config.resolve.fallback = {

      // if you miss it, all the other options in fallback, specified
      // by next.js will be dropped.
      ...config.resolve.fallback,  

      fs: false, // the solution
    };
    
    return config;
  },
};

It works like a charm for me.

Edit: For newer versions of Next.JS, the option future.webpack5 has been replaced by webpack5, which defaults to true. Since we are using Webpack 5 for this solution, this option can be safely removed. https://nextjs.org/docs/messages/future-webpack5-moved-to-webpack5#possible-ways-to-fix-it

Upvotes: 67

Sayed Sajad Hosseini
Sayed Sajad Hosseini

Reputation: 233

Sometimes this error can be because you have imported something but not mastered it anywhere. This worked for me. I reviewed my code and removed the unused dependencies.

Upvotes: 2

Neil Girardi
Neil Girardi

Reputation: 4933

In my case, this error appeared while refactoring the auth flow of a Next.js page. The cause was some an unused imports that I had not yet removed.

Previously I made the page a protected route like so:

export async function getServerSideProps ({ query, req, res }) {
  const session = await unstable_getServerSession(req, res, authOptions)
  if (!session) {
    return {
      redirect: {
        destination: '/signin',
        permanent: false,
      },
    }
  }
//... rest of server-side logic
}

Whilst refactoring, I read up on NextAuth useSession. Based on what I read there, I was able to change the implementation such that I simply needed to add MyComponent.auth = true to make a page protected. I then deleted the aforementioned code block inside of getServerSideProps. However, I had not yet deleted the two imports used by said code block:

import { unstable_getServerSession } from 'next-auth/next'
import { authOptions } from 'pages/api/auth/[...nextauth]'

I believe the second of those two imports was causing the problem. So the summary is that in addition to all of the great answers above, it could also be an unused import.

Upvotes: 1

cwtuan
cwtuan

Reputation: 1861

Don't use fs in the pages directory, since next.js suppose that files in pages directory are running in browser environment.

You could put the util file which uses fs to other directory such as /core

Then require the util in getStaticProps which runs in node.js environment.

// /pages/myPage/index.tsx
import View from './view';
export default View;

export async function getStaticProps() {
  const util = require('core/some-util-uses-fs').default; // getStaticProps runs in nodes
  const data = await util.getDataFromDisk();
  return {
    props: {
      data,
    },
  };
}

Upvotes: 0

Zach Gollwitzer
Zach Gollwitzer

Reputation: 2393

While this error requires a bit more reasoning than most errors you'll encounter, it happens for a straightforward reason.

Why this happens

Next.js, unlike many frameworks allows you to import server-only (Node.js APIs that don't work in a browser) code into your page files. When Next.js builds your project, it removes server only code from your client-side bundle by checking which code exists inside one any of the following built-in methods (code splitting):

  • getServerSideProps
  • getStaticProps
  • getStaticPaths

Side note: there is a demo app that visualizes how this works.

The Module not found: can't resolve 'xyz' error happens when you try to use server only code outside of these methods.

Error example 1 - basic

To reproduce this error, let's start with a working simple Next.js page file.

WORKING file

/** THIS FILE WORKS FINE! */

import type { GetServerSideProps } from "next";

import fs from "fs"; // our server-only import

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  const fileExists = fs.existsSync("/some-file"); 

  return {
    props: {
      doesFileExist: fileExists,
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};

export default ExamplePage;

Now, let's reproduce the error by moving our fs.existsSync method outside of getServerSideProps. The difference is subtle, but the code below will throw our dreaded Module not found error.

ERROR file

import type { GetServerSideProps } from "next";
import fs from "fs";

type Props = {
  doesFileExist: boolean;
};

/** ERROR!! - Module not found: can't resolve 'fs' */
const fileExists = fs.existsSync("/some-file");

export const getServerSideProps: GetServerSideProps = async () => {
  return {
    props: {
      doesFileExist: fileExists,
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};

export default ExamplePage;

Error example 2 - realistic

The most common (and confusing) occurrence of this error happens when you are using modules that contain multiple types of code (client-side + server-side).

Let's say I have the following module called file-utils.ts:

import fs from 'fs'

// This code only works server-side
export function getFileExistence(filepath: string) {
  return fs.existsSync(filepath)
}

// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
  return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}

In this module, we have one server-only method and one "shared" method that in theory should work client-side (but as we'll see, theory isn't perfect).

Now, let's try incorporating this into our Next.js page file.

/** ERROR!! */

import type { GetServerSideProps } from "next";

import { getFileExistence, formatResult } from './file-utils.ts'

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  return {
    props: {
      doesFileExist: getFileExistence('/some-file')
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {

  // ERROR!!!
  return <div>File exists?: {formatResult(doesFileExist)}</div>;
};

export default ExamplePage;

As you can see, we get an error here because when we attempt to use formatResult client-side, our module still has to import the server-side code.

To fix this, we need to split our modules up into two categories:

  1. Server only
  2. Shared code (client or server)
// file-utils.ts

import fs from 'fs'

// This code (and entire file) only works server-side
export function getFileExistence(filepath: string) {
  return fs.existsSync(filepath)
}
// file-format-utils.ts

// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
  return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}

Now, we can create a WORKING page file:

/** WORKING! */

import type { GetServerSideProps } from "next";

import { getFileExistence } from './file-utils.ts' // server only
import { formatResult } from './file-format-utils.ts' // shared

type Props = {
  doesFileExist: boolean;
};

export const getServerSideProps: GetServerSideProps = async () => {
  return {
    props: {
      doesFileExist: getFileExistence('/some-file')
    },
  };
};

const ExamplePage = ({ doesFileExist }: Props) => {
  return <div>File exists?: {formatResult(doesFileExist)}</div>;
};

export default ExamplePage;

Solutions

There are 2 ways to solve this:

  1. The "correct" way
  2. The "just get it working" way

The "Correct" way

The best way to solve this error is to make sure that you understand why it is happening (above) and make sure you are only using server-side code inside getStaticPaths, getStaticProps, or getServerSideProps and NOWHERE else.

And remember, if you import a module that contains both server-side and client-side code, you cannot use any of the imports from that module client-side (revisit example #2 above).

The "Just get it working" way

As others have suggested, you can alter your next.config.js to ignore certain modules at build-time. This means that when Next.js attempts to split your page file between server only and shared code, it will not try to polyfill Node.js APIs that fail to build client-side.

In this case, you just need:

/** next.config.js - with Webpack v5.x */
module.exports = {

  ... other settings ... 

  webpack: (config, { isServer }) => {
    
    // If client-side, don't polyfill `fs`
    if (!isServer) {
      config.resolve.fallback = {
        fs: false,
      };
    }

    return config;
  },

};

Drawbacks of this approach

As shown in the resolve.fallback section of the Webpack documentation, the primary reason for this config option is because as-of Webpack v5.x, core Node.js modules are no longer polyfilled by default. Therefore, the main purpose for this option is to provide a way for you to define which polyfill you want to use.

When you pass false as an option, this means, "do not include a polyfill".

While this works, it can be fragile and require ongoing maintenance to include any new modules that you introduce to your project. Unless you are converting an existing project / supporting legacy code, it is best to go for option #1 above as it promotes better module organization according to how Next.js actually splits the code under the hood.

Upvotes: 24

Minimal reproducible example

A clean minimal example will be beneficial to Webpack beginners since auto splitting based on usage is so mind-blowingly magic.

Working hello world baseline:

pages/index.js

// Client + server code.

export default function IndexPage(props) {
  return <div>{props.msg}</div>
}

// Server-only code.

export function getStaticProps() {
  return { props: { msg: 'hello world' } }
}

package.json

{
  "name": "test",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "12.0.7",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  }
}

Run with:

npm install
npm run dev

Now let's add a dummy require('fs') to blow things up:

// Client + server code.

export default function IndexPage(props) {
  return <div>{props.msg}</div>
}

// Server-only code.

const fs = require('fs')

export function getStaticProps() {
  return { props: { msg: 'hello world' } }
}

fails with:

Module not found: Can't resolve 'fs' 

which is not too surprising, since there was no way for Next.js to know that that fs was server only, and we wouldn't want it to just ignore random require errors, right? Next.js only knows that for getStaticProps because that's a hardcoded Next.js function name.

OK, so let's inform Next.js by using fs inside getStaticProps, the following works again:

// Client + server code.

export default function IndexPage(props) {
  return <div>{props.msg}</div>
}

// Server-only code.

const fs = require('fs')

export function getStaticProps() {
  fs
  return { props: { msg: 'hello world' } }
}

Mind equals blown. So we understand that any mention of fs inside of the body of getStaticProps, even an useless one like the above, makes Next.js/Webpack understand that it is going to be server-only.

Things would work the same for getServerSideProps and getStaticPaths.

Higher order components (HOCs) have to be in their own files

Now, the way that we factor out IndexPage and getStaticProps across different but similar pages is to use HOCs, which are just functions that return other functions.

HOCs will normally be put outside of pages/ and then required from multiple locations, but when you are about to factor things out to generalize, you might be tempted to put them directly in the pages/ file temporarily, something like:

// Client + server code.

import Link from 'next/link'

export function makeIndexPage(isIndex) {
  return (props) => {
    return <>
      <Link href={isIndex ? '/index' : '/notindex'}>
        <a>{isIndex ? 'index' : 'notindex'}</a>
      </Link>
      <div>{props.fs}</div>
      <div>{props.isBlue}</div>
    </>
  }
}

export default makeIndexPage(true)

// Server-only code.

const fs = require('fs')

export function makeGetStaticProps(isBlue) {
  return () => {
    return { props: {
      fs: Object.keys(fs).join(' '),
      isBlue,
    } }
  }
}

export const getStaticProps = makeGetStaticProps(true)

but if you do this you will be saddened to see:

Module not found: Can't resolve 'fs' 

So we understand another thing: the fs usage has to be directly inside the getStaticProps function body, Webpack can't catch it in subfunctions.

The only way to solve this is to have a separate file for the backend-only stuff as in:

pages/index.js

// Client + server code.

import { makeIndexPage } from "../front"

export default makeIndexPage(true)

// Server-only code.

import { makeGetStaticProps } from "../back"

export const getStaticProps = makeGetStaticProps(true)

pages/notindex.js

// Client + server code.

import { makeIndexPage } from "../front"

export default makeIndexPage(false)

// Server-only code.

import { makeGetStaticProps } from "../back"

export const getStaticProps = makeGetStaticProps(false)

front.js

// Client + server code.

import Link from 'next/link'

export function makeIndexPage(isIndex) {
  return (props) => {
    console.error('page');
    return <>
      <Link href={isIndex ? '/notindex' : '/'}>
        <a>{isIndex ? 'notindex' : 'index'}</a>
      </Link>
      <div>{props.fs}</div>
      <div>{props.isBlue}</div>
    </>
  }
}

back.js

// Server-only code.

const fs = require('fs')

export function makeGetStaticProps(isBlue) {
  return () => {
    return { props: {
      fs: Object.keys(fs).join(' '),
      isBlue,
    } }
  }
}

Webpack must see that name makeGetStaticProps getting assigned to getStaticProps, so it decides that the entire back file is server-only.

Note that it does not work if you try to merge back.js and front.js into a single file, probably because when you do export default makeIndexPage(true) webpack necessarily tries to pull the entire front.js file into the frontend, which includes the fs, so it fails.

This leads to a natural (and basically almost mandatory) split of library files between:

  • front.js and front/*: front-end + backend files. These are safe for the frontend. And the backend can do whatever the frontend can do (we are doing SSR right?) so those are also usable from the backend.

    Perhaps this is the idea behind the conventional "components" folder in many official examples. But that is a bad name, because that folder should not only contain components, but also any library non-component helpers/constants that will be used from the frontend.

  • back.js and back/* (or alternatively anything outside of front/*): backend only files. These can only be used by the backend, importing them on frontend will lead to the error

Upvotes: 44

Hazrat Gafulov
Hazrat Gafulov

Reputation: 348

/** @type {import('next').NextConfig} */
module.exports = {
  reactStrictMode: false,
  webpack5: true,
  webpack: (config) => {
    config.resolve.fallback = {
      fs: false,
      net: false,
      dns: false,
      child_process: false,
      tls: false,
    };

    return config;
  },
};

This code fixed my problem and I want to share.Add this code to your next.config file.i'm using

webpack5

Upvotes: 4

Nate
Nate

Reputation: 1360

I ran into this in a NextJS application because I had defined a new helper function directly below getServerSideProps(), but had not yet called that function inside getServerSideProps().

I'm not sure why this created a problem, but it did. I could only get it to work by either calling that function, removing it, or commenting it out.

Upvotes: 0

Barrard
Barrard

Reputation: 2053

If trying to use fs-extra in Next.js, this worked for me

module.exports = {
  webpack: (config) => {
    config.resolve.fallback = { fs: false, path: false, stream: false, constants: false };
    return config;

  }
}

Upvotes: 5

Arjun Kava
Arjun Kava

Reputation: 6141

If you use fs, be sure it's only within getInitialProps or getServerSideProps. (anything includes server-side rendering).

You may also need to create a next.config.js file with the following content to get the client bundle to build:

For webpack4

module.exports = {
  webpack: (config, { isServer }) => {
    // Fixes npm packages that depend on `fs` module
    if (!isServer) {
      config.node = {
        fs: 'empty'
      }
    }

    return config
  }
}

For webpack5

module.exports = {
  webpack5: true,
  webpack: (config) => {
    config.resolve.fallback = { fs: false };

    return config;
  },
};

Note: for other modules such as path, you can add multiple arguments such as

{
  fs: false,
  path: false
}

Upvotes: 151

Yilmaz
Yilmaz

Reputation: 49671

fs,path or other node native modules can be used only inside server-side code, like "getServerSide" functions. If you try to use it in client you get error even you just console.log it.. That console.log should run inside server-side functions as well.

When you import "fs" and use it in server-side, next.js is clever enough to see that you use it in server-side so it wont add that import into the client bundle

One of the packages that I used was giving me this error, I fixed this with

module.exports = {
 
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback.fs = false
    }

    return config
  },
  
}

but this was throwing warning on terminal:

"Critical dependency: require function is used in a way in which

 dependencies cannot be statically extracted"

Then I tried to load the node module on the browser. I copied the "min.js" of the node module from the node_modules and placed in "public/js/myPackage.js" and load it with Script

export default function BaseLayout({children}) {
  return (
    <>
      <Script
        // this in public folder
        src="/js/myPackage.js"
        // this means this script will be loaded first
        strategy="beforeInteractive"
      />
    </>
  )
}

This package was attached to window object and in node_modules source code's index.js:

if (typeof window !== "undefined") {
  window.TruffleContract = contract;
}

So I could access to this script as window.TruffleContract. BUt this was not an efficient way.

Upvotes: 16

Hrvoje
Hrvoje

Reputation: 15212

I got this error in my NextJS app because I was missing export in

export function getStaticProps()

Upvotes: 2

Munib
Munib

Reputation: 967

I had this exact issue. My problem was that I was importing types that I had declared in a types.d.ts file.

I was importing it like this, thanks to the autofill provided by VSCode.

import {CUSTOM_TYPE} from './types'

It should have been like this:

import {CUSTOM_TYPE} from './types.d'

In my case, I think the .d was unnecessary so I ended up removing it entirely and renamed my file to types.ts.

Weird enough, it was being imported directly into index.tsx without issues, but any helper files/functions inside the src directory would give me errors.

Upvotes: 0

thomsjel
thomsjel

Reputation: 1

I had the same issue when I was trying to use babel.

For me this worked:

#add a .babelrc file to the root of the project and define presets and plugins (in my case, I had some issues with the macros of babel, so I defined them)

{
    "presets": ["next/babel"],
    "plugins": ["macros"]
}

after that shut down your server and run it again

Upvotes: 0

Farid Huseynov
Farid Huseynov

Reputation: 159

For me, the problem was the old version of the node.js installed. It requires node.js version 14 and higher. The solution was to go to the node.js web page, download the latest version and just install it. And then re-run the project. All worked!

Upvotes: 0

Saurabh Jindal
Saurabh Jindal

Reputation: 39

For me clearing the cache npm cache clean -f

and then updating the node version to the latest stable release(14.17.0) worked

Upvotes: 2

Larry Kosher
Larry Kosher

Reputation: 333

It might be that the module you are trying to implement is not supposed to run in a browser. I.e. it's server-side only.

Upvotes: 0

Related Questions