できない.dev

Next.js で「Module not found: Can't resolve 'fs'」が解決できない

fs などの Node コアモジュールをクライアントに含まれるコードで import すると出る。
サーバー専用コードへ移すか、Server Component / Route Handler 側でのみ使う。

公開: 更新:

実行例あり(2026-09-15 に実環境で検証)

要約

Module not found: Can't resolve 'fs' は、fs や path などブラウザに存在しない Node コアモジュールを、クライアントバンドルに含まれるコードから import したときに出る。

ファイルに 'use client' を付けると、その import 連鎖はすべてクライアントに含まれる。fs はサーバーにしか無いのでバンドルできず、解決に失敗する。

対策の基本は、サーバー専用の処理を Server Component や Route Handler 側へ寄せること。

実行例

'use client' の data-view.js から fs を使う lib/read-data.js を import すると、next build は Module not found: Can't resolve 'fs' を出して終了コード 1 で止まり、読み込みを Server Component の page.js へ移すとビルドが通って / は静的ページ(○)として生成された。read-data.js に import "server-only" を足してクライアントから読み込み直すとビルド時にエラーで失敗し、resolve.fallback で fs を false にした構成では同じ import のままでもビルドは通っている。

$ npx next build
▲ Next.js 15.5.25
 
   Creating an optimized production build ...
Failed to compile.
 
./lib/read-data.js
Module not found: Can't resolve 'fs'
 
https://nextjs.org/docs/messages/module-not-found
 
Import trace for requested module:
./app/data-view.js
 
> Build failed because of webpack errors
$ echo $?
1
$ npx next build
▲ Next.js 15.5.25
 
   Creating an optimized production build ...
 ✓ Compiled successfully in 2.5s
   Linting and checking validity of types ...
   Collecting page data ...
   Generating static pages (0/4) ...
   Generating static pages (1/4) 
   Generating static pages (2/4) 
   Generating static pages (3/4) 
 ✓ Generating static pages (4/4)
   Finalizing page optimization ...
   Collecting build traces ...
 
Route (app)                                 Size  First Load JS
┌ ○ /                                      127 B         103 kB
└ ○ /_not-found                            993 B         103 kB
+ First Load JS shared by all             102 kB
  ├ chunks/255-37e0f0325134c4d7.js       46.4 kB
  ├ chunks/4bd1b696-c023c6e3521b1417.js  54.2 kB
  └ other shared chunks (total)          1.88 kB
 
○  (Static)  prerendered as static content
$ echo $?
0
$ npx next build
▲ Next.js 15.5.25
 
   Creating an optimized production build ...
Failed to compile.
 
./lib/read-data.js
Error:   x You're importing a component that needs "server-only". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
  |
 
   ,-[/tmp/tmp.HuYe3X40Af/lib/read-data.js:1:1]
 1 | import "server-only";
   : ^^^^^^^^^^^^^^^^^^^^^
 2 | import { readFileSync } from "fs";
 3 | 
 4 | export function readData() {
   `----
 
Import trace for requested module:
./lib/read-data.js
./app/data-view.js
 
> Build failed because of webpack errors
$ echo $?
1
$ npx next build
▲ Next.js 15.5.25
 
   Creating an optimized production build ...
 ✓ Compiled successfully in 2.2s
   Linting and checking validity of types ...
   Collecting page data ...
   Generating static pages (0/4) ...
   Generating static pages (1/4) 
   Generating static pages (2/4) 
   Generating static pages (3/4) 
 ✓ Generating static pages (4/4)
   Finalizing page optimization ...
   Collecting build traces ...
 
Route (app)                                 Size  First Load JS
┌ ○ /                                      301 B         103 kB
└ ○ /_not-found                            993 B         103 kB
+ First Load JS shared by all             102 kB
  ├ chunks/255-37e0f0325134c4d7.js       46.4 kB
  ├ chunks/4bd1b696-c023c6e3521b1417.js  54.2 kB
  └ other shared chunks (total)          1.88 kB
 
○  (Static)  prerendered as static content
$ echo $?
0

— 2026-09-15 時点の出力

検証環境

検証日
実行環境
node:20 Debian GNU/Linux 12 (bookworm)
バージョン
  • Node.js 20.20.2
  • npm 10.8.2
  • Python 3.11.2
  • Git 2.39.5

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. クライアント境界の巻き込み: 'use client' のファイル(またはそこから import した先)で fs を参照している。
  2. サーバー専用コードの混入: ファイル読み込みなどサーバー用ユーティリティをクライアントから import している。
  3. 依存ライブラリ経由: ライブラリが内部で Node コアモジュールに依存し、クライアント側に引き込まれている。

解決策

1. サーバー側へ処理を移す(推奨)

// app/page.tsx (Server Component: 'use client' を付けない)
import { readFileSync } from "node:fs";
 
export default function Page() {
  const data = readFileSync("data.json", "utf8");
  return <pre>{data}</pre>;
}

fs を使う読み込みは Server Component 側で行い、結果だけをクライアントへ渡す。

2. server-only でガードする

import "server-only";

このモジュールを import したファイルがクライアントに含まれるとビルドが失敗するため、混入を早期に検知できる。

3. resolve.fallback で空に割り当てる(最終手段)

// next.config.js (webpack 利用時)
module.exports = {
  webpack: (config) => {
    config.resolve.fallback = { fs: false };
    return config;
  },
};

どうしてもリファクタできない場合の逃げ道。
Turbopack を使う場合は turbopack.resolveAlias で同様に空モジュールへ割り当てる。
まずはリファクタで fs をクライアントから外すのが本筋。

この記事は役立ちましたか?