How to debug a Next.js application
Debugging Next.js is harder than debugging a plain React app because your code runs in two places: the Node.js server and the browser. This guide shows where each kind of bug shows up, and how to see logs, network calls and variables on both sides.
First, work out where the code runs
Almost every confusing Next.js bug comes down to looking in the wrong place. With the App Router:
- On the server: Server Components (the default in
app/), Route Handlers (route.ts), Server Actions ('use server'),generateMetadata, and middleware (proxy.tssince Next.js 16). - In the browser: components marked
'use client'after they hydrate, their event handlers and their effects. - Both: Client Components also render once on the server to produce the first HTML. That is why a client component can crash on the server with
window is not defined.
| You see | Look here first |
|---|---|
| Wrong or missing data on the page | Server network calls and server logs |
| A button or form does nothing | Browser console and Network tab, then the Server Action or Route Handler |
| Hydration error | The error overlay's diff, then the component it names |
| 500 error or blank page | The terminal running next dev |
Step 1: read the logs in the right place
console.log in server code prints to the terminal running next dev, not the browser. Logs from client code print to the browser console. Since Next.js 15, logs from Server Components are also replayed in the browser console with a Server label, but Route Handlers and Server Actions still only log to the terminal.
Keeping two windows side by side works for small apps. Once several requests run at once, it gets hard to tell which log line belongs to which page load. See Next.js logs for the details.
Step 2: check the network, on both sides
The browser's Network tab shows what the page requested. It never shows the calls your server makes to your backend or third-party APIs while rendering. For those, Next.js can log fetch calls in the terminal:
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
logging: { fetches: { fullUrl: true } },
};
export default nextConfig;
That prints each fetch with its URL, status, cache result and duration. It does not show headers or bodies, and it misses axios, got and any other client. Seeing server-side requests covers the alternatives.
Step 3: pause server code with breakpoints
Start the dev server with the Node.js inspector turned on:
NODE_OPTIONS='--inspect' npx next dev
Recent Next.js versions also accept next dev --inspect. Then open chrome://inspect in Chrome, click Open dedicated DevTools for Node (or inspect under Remote Target), and find your file with Cmd/Ctrl + P. Click a line number to set a breakpoint, then load the page. A debugger; statement in your code works too.
A breakpoint in a file Next.js has not compiled yet will not bind. Load a page that uses the file once, then set it again.
VS Code users can do the same with a launch.json: see breakpoints in VS Code and Chrome.
Step 4: pause client code
Open the browser's DevTools, go to Sources, press Cmd/Ctrl + P and type your component's file name. Source maps are on in development, so you see your TypeScript, not the bundle. Install React Developer Tools to read props, state and hooks of any component.
Step 5: Route Handlers and Server Actions
These are server code reached from the browser, so you need both halves: what the browser sent, and what the server did with it. Call a Route Handler directly with curl -i to rule out the UI, and log or pause inside it. The details are in debugging API routes and debugging Server Actions.
Step 6: read the error overlay and the stack
In development, Next.js shows runtime errors in an overlay with a source-mapped stack. Click a frame to open it in your editor. In production, errors thrown on the server are replaced with a generic message and a digest; search your server logs for that digest to find the real error.
Doing it all from one screen
qaisdebugs puts these steps in one inspector. Run qaisdebugs dev instead of npm run dev and you get:
- every browser and server request, with headers, bodies, timing and the file and line that made it, nested under the page load that caused it;
- server and browser logs in one list, each tied to its request;
- breakpoints in Server and Client Components with a click on the line number, no
--inspectorlaunch.json.
A quick checklist
- Decide whether the failing code runs on the server, in the browser, or both.
- Read the terminal and the browser console.
- Check what the browser sent and what the server called.
- Pause on the line where the data first goes wrong and read the variables.
- Fix it, reload, and confirm the request and logs look right.