You're running Webpack to build a browser app — maybe React, Vue, or plain JS. Then boom: Module not found: Error: Can't resolve 'fs'. This hit me last week with a client using a PDF library that pulled in Node.js file system calls. The app worked fine in Node, but Webpack in browser mode had no idea what 'fs' was.
Why this happens
Webpack compiles code for the browser — there's no file system in a browser tab. 'fs' is a Node.js core module. Some third-party packages are written for Node but get dragged into a browser bundle. The error means one of your dependencies — or your own code — is doing something like require('fs') or import fs from 'fs'.
Webpack 5 stopped automatically polyfilling Node core modules. That's actually good — it forces you to be explicit. But it breaks builds until you configure it right.
The fix: tell Webpack to ignore or mock 'fs'
There are two ways. If you don't need the file system code to actually run (most browser apps don't), just ignore it. If you need a stub, provide an empty module. Here's the step-by-step.
- Find where 'fs' is used. Look at the error stack trace. It'll tell you which file triggered it. Common culprits: any Node.js library that does file I/O —
fs-extra,graceful-fs,adm-zip,archiver. Sometimes it's your own code that's not wrapped in a browser check. - Open your Webpack config. Usually
webpack.config.jsorwebpack.common.js. - Add a fallback for 'fs'. Inside the
resolvesection, add this:
Settingresolve: { fallback: { "fs": false } }falsetells Webpack: "If you see 'fs', just leave it as an empty module." The code that tries to use it will either skip gracefully or fail silently — which is usually fine for browser bundles. - If you need a stub (rare case). Install
browserify-fsor write your own mock. Add it like this:
But honestly, in 99% of browser builds,resolve: { fallback: { "fs": require.resolve("browserify-fs") } }falseis what you want. The browser can't write files anyway. - If you're on Webpack 4 or older, you need
node: { fs: 'empty' }ornode: { fs: false }. That old syntax was deprecated in Webpack 5. - Rebuild. Run your build command again. If it still fails, there's another module causing it — apply the same fix for
path,os,crypto, etc. They show up often.
What else to check if it still fails
Sometimes the error hides deeper. Here's what I've seen trip people up:
- Multiple Webpack configs. If you have separate configs for dev and prod, both need the fallback.
- TypeScript. If you're using TypeScript declarations, you might need a
declare module 'fs'in a.d.tsfile — but Webpack ignoring it is still the fix. - Webpack's
targetsetting. If you settarget: 'node', Webpack expects Node modules. For browser, settarget: 'web'or leave it default. - Webpack 5's
resolve.alias. Some guides tell you to alias 'fs' to false, butfallbackis the proper Webpack 5 way. I wasted an hour once becausealiasdidn't work.
Real story: Client last month used
crypto-jsandfs-extratogether. Turned out a sub-dependency of their PDF generator callednode:fsdirectly. Webpack 5's strict mode caught it. One fallback entry fixed the whole thing.
That's it. You don't need polyfills, you don't need to rewrite the library. Just tell Webpack to ignore what can't exist in a browser. It's not elegant, but it's honest — and it works.