Skip to content

Fix: Next.js Module not found: Can't resolve 'fs' (or 'path', 'crypto', 'net')

FixDevs ·

Quick Answer

How to fix Next.js Module not found Can't resolve fs error caused by importing Node.js modules in client components, wrong server/client boundaries, and missing polyfills.

The Error

You build or run a Next.js application and get:

Module not found: Can't resolve 'fs'

Or variations:

Module not found: Can't resolve 'path'
Module not found: Can't resolve 'crypto'
Module not found: Can't resolve 'net'
Module not found: Can't resolve 'tls'
Module not found: Can't resolve 'child_process'
./node_modules/some-package/index.js
Module not found: Can't resolve 'fs'

A Node.js built-in module (fs, path, crypto, etc.) is being imported in code that runs in the browser. The browser does not have access to the filesystem, network sockets, or other Node.js APIs.

Why This Happens

Next.js runs code in two environments: the server (Node.js) and the client (browser). Node.js built-in modules like fs, path, and crypto are only available on the server. When webpack tries to bundle code for the client that imports these modules, it fails because they do not exist in the browser.

Common causes:

  • Importing a server-only module in a client component. Using fs or path in a file that renders in the browser.
  • A third-party package uses Node.js APIs. The package was designed for Node.js but you imported it in client-side code.
  • Mixing server and client code. A utility file used by both server and client imports Node.js modules.
  • Wrong component boundary in App Router. A Server Component import leaks into a Client Component.
  • getStaticProps/getServerSideProps import issue. Code imported for server-side data fetching gets bundled into the client.

Fix 1: Move Server Code to Server-Only Files

Pages Router — use getStaticProps or getServerSideProps:

// pages/index.js
import fs from "fs";
import path from "path";

// This runs ONLY on the server — never bundled for the client
export async function getStaticProps() {
  const filePath = path.join(process.cwd(), "data", "posts.json");
  const fileContents = fs.readFileSync(filePath, "utf8");
  const posts = JSON.parse(fileContents);

  return { props: { posts } };
}

// This runs on the client — no fs/path imports here
export default function Home({ posts }) {
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

getStaticProps and getServerSideProps are automatically stripped from the client bundle. Any imports they use are also excluded — but only if those imports are not used elsewhere in the file.

Pro Tip: Keep server-only imports inside getStaticProps/getServerSideProps or in separate files. If fs is imported at the top of the file AND used in the component, it gets bundled for the client.

Fix 2: Use Server Components (App Router)

In Next.js 13+ App Router, Server Components run only on the server:

// app/page.tsx — Server Component by default
import fs from "fs";
import path from "path";

export default async function Home() {
  const filePath = path.join(process.cwd(), "data", "posts.json");
  const fileContents = fs.readFileSync(filePath, "utf8");
  const posts = JSON.parse(fileContents);

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Server Components can use fs, path, and any Node.js API. They are never sent to the client.

The error occurs when you add "use client":

"use client";  // This makes it a Client Component!

import fs from "fs";  // ERROR: Can't resolve 'fs'

Remove "use client" if the component needs server-only APIs, or split the component (see Fix 3).

Fix 3: Split Server and Client Code

When a component needs both server-only APIs and client interactivity:

Server Component (fetches data):

// app/dashboard/page.tsx (Server Component)
import fs from "fs";
import { DashboardClient } from "./dashboard-client";

export default async function Dashboard() {
  const data = fs.readFileSync("data/stats.json", "utf8");
  const stats = JSON.parse(data);

  return <DashboardClient stats={stats} />;
}

Client Component (handles interactivity):

// app/dashboard/dashboard-client.tsx
"use client";

import { useState } from "react";

export function DashboardClient({ stats }) {
  const [filter, setFilter] = useState("all");

  return (
    <div>
      <select onChange={e => setFilter(e.target.value)}>
        <option value="all">All</option>
        <option value="active">Active</option>
      </select>
      <pre>{JSON.stringify(stats, null, 2)}</pre>
    </div>
  );
}

The Server Component fetches data using Node.js APIs and passes it as props to the Client Component.

Fix 4: Fix Third-Party Package Imports

A third-party package might use Node.js APIs internally:

"use client";

import SomePackage from "some-server-package";  // Uses 'fs' internally
// ERROR: Can't resolve 'fs'

Fix: Use dynamic import with ssr: false:

"use client";

import dynamic from "next/dynamic";

const SomeComponent = dynamic(() => import("some-server-package"), {
  ssr: false,
});

export default function Page() {
  return <SomeComponent />;
}

ssr: false prevents the component from rendering on the server, so the Node.js imports are never executed.

Fix: Check if the package has a client-side version:

Many packages offer separate browser and Node.js builds:

// Instead of:
import crypto from "crypto";

// Use the Web Crypto API:
const hash = await crypto.subtle.digest("SHA-256", data);

// Or a browser-compatible package:
import { sha256 } from "js-sha256";

Fix 5: Use the server-only Package

Mark files as server-only to get a clear error at build time:

npm install server-only
// lib/data.ts
import "server-only";
import fs from "fs";

export function readData() {
  return fs.readFileSync("data.json", "utf8");
}

If a Client Component imports this file, Next.js gives a clear error:

This module cannot be imported from a Client Component module.
It should only be used from a Server Component.

This is better than the confusing Can't resolve 'fs' error because it tells you exactly what is wrong.

Fix 6: Configure Webpack Fallbacks

If you need to suppress the error for a package that checks for Node.js at runtime but works without it:

next.config.js (Pages Router):

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

Setting a module to false replaces it with an empty module. The import resolves but any usage returns undefined.

Warning: This only works if the package handles the missing module gracefully (e.g., checks typeof window !== 'undefined' before using fs). If the package unconditionally calls fs.readFileSync(), it will fail at runtime with a different error.

For general module resolution errors, see Fix: Module not found: Can’t resolve.

Fix 7: Use API Routes for Server Operations

Instead of accessing Node.js APIs from client components, create an API route:

Pages Router:

// pages/api/data.js
import fs from "fs";
import path from "path";

export default function handler(req, res) {
  const filePath = path.join(process.cwd(), "data", "posts.json");
  const data = fs.readFileSync(filePath, "utf8");
  res.json(JSON.parse(data));
}

App Router:

// app/api/data/route.ts
import fs from "fs";
import path from "path";
import { NextResponse } from "next/server";

export async function GET() {
  const filePath = path.join(process.cwd(), "data", "posts.json");
  const data = fs.readFileSync(filePath, "utf8");
  return NextResponse.json(JSON.parse(data));
}

Client component calls the API:

"use client";

import { useEffect, useState } from "react";

export default function Posts() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch("/api/data")
      .then(res => res.json())
      .then(setPosts);
  }, []);

  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}

Common Mistake: Importing server-only utilities in a shared file used by both server and client code. Even if the client never calls the server function, webpack still bundles the import. Split shared files into utils.server.ts and utils.client.ts.

Fix 8: Fix Environment Variable Issues

process.env behaves differently on server and client:

// Server-only — works in Server Components and API routes
const dbUrl = process.env.DATABASE_URL;

// Client-side — must be prefixed with NEXT_PUBLIC_
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

Without the NEXT_PUBLIC_ prefix, environment variables are stripped from the client bundle. Accessing them returns undefined.

For environment variable issues in general, see Fix: environment variable is undefined.

Still Not Working?

Check your import tree. A single import chain from a Client Component to a file that imports fs causes the error. Trace the imports:

ClientComponent → utils → database → fs  ← ERROR

Break the chain by splitting utils into server and client versions.

Check for barrel file issues. A barrel file (index.ts) that re-exports both server and client utilities causes webpack to bundle everything:

// lib/index.ts — barrel file
export { readData } from "./server-utils";  // Uses fs
export { formatDate } from "./client-utils";

// Importing formatDate also pulls in readData's dependencies:
import { formatDate } from "@/lib";

Fix: Import directly from the specific file, not the barrel:

import { formatDate } from "@/lib/client-utils";

For Next.js hydration issues where server and client output differ, see Fix: Next.js hydration failed.

If the image optimization fails instead of module resolution, see Fix: Next.js image optimization error.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles