The 30-Second Fix: Use Dynamic Imports
If you only need the Node.js API in one spot, wrap the import in a dynamic import and put it inside a useEffect or an event handler. This tells Next.js to only load it on the server, and your client bundle stays clean.
// Instead of:
import fs from 'fs';
// Do:
useEffect(() => {
import('fs').then((fs) => {
// use fs.readFileSync etc.
});
}, []);
This works because dynamic imports are only resolved at runtime, and Next.js knows not to include them in the client bundle if they're not statically imported. I've used this trick for reading config files in a client component that fetches data on mount—just remember to catch errors if the module fails to load.
Note: This doesn't work if you need the module at the top level of your component. But for most use cases—reading a file on a button click or in an effect—it's perfect.
The 5-Minute Fix: Move the Code to a Server Component or API Route
If dynamic imports feel hacky or you're using the Node.js API in multiple places, the cleaner approach is to move that logic to a server component or an API route. Client components should never touch the filesystem—that's a server job.
Here's the pattern for an API route (works in both Pages and App Router):
// pages/api/read-file.js (Pages Router)
export default function handler(req, res) {
const fs = require('fs');
const data = fs.readFileSync('./data.json', 'utf8');
res.status(200).json(JSON.parse(data));
}
In the App Router, you can create a route handler:
// app/api/read-file/route.js
export async function GET() {
const fs = require('fs');
const data = fs.readFileSync('./data.json', 'utf8');
return new Response(data, { status: 200 });
}
Then from your client component, just fetch that endpoint. It's an extra network call, but it keeps your bundle size down and your code maintainable. If you're using server components (the default in App Router), you can keep the fs import right in the component—that's fine, as long as the component has 'use client' at the top? Actually no—if it has 'use client', it's a client component, so don't.
Let me clarify: In App Router, any component with 'use client' is a client component. If you have a server component (no 'use client'), you can use fs directly. So if you don't need interactivity, just remove 'use client' and you're done.
The 15+ Minute Fix: Configure Webpack Fallbacks (Not Recommended)
I'm including this because you'll find it on Stack Overflow, but I'll tell you straight: skip it. People suggest adding a fallback to false in next.config.js:
// next.config.js
module.exports = {
webpack: (config) => {
config.resolve.fallback = { fs: false, path: false };
return config;
},
};
This silences the error by telling webpack 'just ignore the missing module'. But your code will crash when it tries to use fs on the client, because fs doesn't exist in the browser. You'll get a runtime error like Cannot read properties of undefined (reading 'readFileSync'). That's worse than a build error.
The only time this makes sense is if you have a third-party library that imports fs but you know it's only used on the server. In that case, you'd want to conditionally require it based on process.env.NODE_ENV or use a proxy. But honestly, it's a rabbit hole. I've seen teams spend hours debugging why their client bundle is huge because they didn't realize the fallback was pulling in polyfills.
Real-World Scenario: Why This Error Happens
Picture this: you're building a dashboard that shows file statistics. You write a client component that imports fs to read a log file. In development, it works if you're using a Node.js runtime, but when you run next build, it fails because the client bundle is built for the browser, and Node's core modules aren't available.
Another trigger: you upgrade to Next.js 13 with the App Router from a Pages Router app. In Pages Router, you could sometimes get away with import fs from 'fs' in a page component that had getServerSideProps—but if you forgot to add that function, or moved to App Router where the default is server components, you'd hit this error if you added 'use client' incorrectly.
Quick Prevention Tips
- Always check if your component needs to be a client component. If it doesn't use hooks like useState or useEffect, keep it as a server component (no 'use client').
- Keep all Node.js API usage in API routes, server actions, or server components.
- If you're using a library that internally uses
fs, check its docs—many have browser-compatible versions or require you to use a different import path.
Remember, the error message says exactly what's wrong: you're trying to resolve a Node.js module in a context where it doesn't exist. The fix is to move that code to where Node.js actually runs—the server. Start with the dynamic import if you're in a pinch, but the real solution is architectural.