window is not defined. Module not found: Can't resolve 'fs'. Two of the most common errors in a Next.js project, and neither one is really a bug in your code. Both mean the same thing: a piece of code ran somewhere it was never going to work.

The idea

A web app is split across two computers. The runs on your visitor's machine, inside their browser, where they can read it, edit it and lie to it. The runs on a machine you control, where your secrets are safe. The sits behind the backend and remembers everything after the visitor closes the tab. Almost every confusing error is code running on the wrong side of that line.

code
        USER'S MACHINE                 |            YOUR SERVER
                                       |
   +------------------------+          |     +------------------------+
   |  browser               |  request |     |  backend               |
   |  HTML, CSS, JavaScript | -------> |     |  route handlers        |
   |  = the frontend        | <------- |     |  API keys, passwords   |
   +------------------------+ response |     +-----------+------------+
                                       |                 |
   anything here is visible            |                 | queries
   to the user and editable            |                 v
   by the user                         |     +------------------------+
                                       |     |  database (Postgres)   |
                                       |     +------------------------+

How it works

One page view is a short conversation:

  1. The browser asks your server for a URL.
  2. Your server runs backend code. It may read the database, call another company's service, check who is logged in.
  3. Your server sends back HTML plus a bundle of JavaScript.
  4. The browser draws the page, then runs that JavaScript for clicks, typing and animation.
  5. Every later action (submit a form, load more rows) is another request over the same line.

The line in the middle is a trust boundary. Everything on the left arrives on a stranger's computer. Browser code can call window and document because a browser has them, and it cannot call fs because it has no filesystem. Server code is the reverse. Put a Stripe secret key in a frontend file and you have not hidden it, you have published it.

What to do

  1. For any file you are about to write, answer out loud: does this run in the browser or on the server? In Next.js the App Router treats components as server code by default and "use client" moves a file to the browser.
  2. Put anything with a password, an API key or a database connection under a server-only folder such as src/server/, and never import it from a component that carries "use client".
  3. When an error names a browser thing (window, localStorage) or a server thing (fs, process), read it as a location complaint before you read it as a logic bug. Tell your AI assistant which side the file is supposed to run on. It will usually fix it in one move.

Where it breaks

Modern frameworks blur the line on purpose, which is why it confuses people. A Next.js server component and a client component look like the same React code in the same folder. Some apps have no backend at all: a static site is frontend files sitting on a file host with nothing running behind them, and that works fine until you need to keep a secret. Others push backend work to someone else's servers, so Supabase or Firebase is your backend and you never write one. The split still exists in all of those. It just moved.