How to debug Next.js in NPM Workspaces

After recently moving a Next.js app to a monorepo setup using NPM workspaces, I noticed the debugger wasn't working as expected. I was no longer able to put breakpoints on server side components or API endpoints. I eventually discovered that debugging Next.js in NPM workspaces requires some additional configuration.
In this article I'll cover the needed changes to configure and resolve the debugger issue, including:
- Debugging Next.js in NPM workspaces
- Key benefits of using Next.js in workspaces
- Configuring Visual Studio Code for NPM workspaces
- Different debug configurations
Read on if you've been experiencing this issue so you can get back to successfully debugging Next.js in workspaces.
What is debugging Next.js in workspaces?
NPM and Yarn have supported workspaces for some time now and offer a great way to break large projects into separate workspaces or modules.
Workspaces typically have a different repo directory structures which can make debugging a specific workspace different than a standalone Next.js project.
Why is debugging Next.js in workspaces important?
As development strategies continue to evolve, a good portion of projects and teams are moving to a monorepo approach, for the following reasons:
- All of the code is in one repo location.
- Projects can be broken down into separate workspaces allowing different teams to work in different spaces and still maintain autonomy.
- Eliminates the need to publish internal dependency packages since they coexist in the same repo.
- No need for versioning packages since code is always at latest.
- Multiple Next.js and other UI frameworks can be managed using workspaces.
- Common code can be shared and is always current without version drift.
Even though there are many benefits to gain with NPM workspaces, there will still be some learning curves. I felt an article on this topic would help understand how to debug Next.js apps in NPM workspaces.
Types of ways to debug Next.js
Next.js Standalone
Standard Next.js project setup without workspaces. This is your basic setup and usage.
Next.js in NPM Workspaces
Next.js UI apps are nested within subdirectories managed by NPM workspaces.
Tips for debugging Next.js
If your new to Next.js, it helps to have a general idea of the different component module types and how they need to be debugged.
client side
- Any React component in Next.js that uses React hooks or providers needs to place use client
at the top of the module and is considered to be a Client Component
. These types of components require the Chrome debugger to set breakpoints.
server side
- All other components are React Server Component (RSC)
by default. These types of components will need to be debugged using an IDE like Visual Studio Code to add breakpoints since these components only render on the server. These are components such as:
- layout.tsx
- page.tsx (without
use client
)
- route.ts
- middleware.ts
- Any exported function, object or variables that does not use React hooks or providers generally.
Next.js debug setup in Standalone project
Let's first take a look at a standalone Next.js application project structure and debug configuration. This is the project structure you'll get out of the box when using the generator.
npx create-next-app@latest
In Visual Studio Code, a typical standalone application like Next.js will generally have all the code in the root of the project.
project-root
: representing the root of a single project that was opened in Visual Studio Code.
└── project-root
├── .vscode
├── .next
├── app
├── next.config.mjs
├── node_modules
├── public
├── package.json
└── package-lock.json
Starting a debug session
There's basically two ways to start a debug session, which include:
Launch
: Starts a Next.js application and attaches the VS Code debugger to the running process.
Attach
: Attaches the debugger to an already running Next.js process.
These options apply for any Node.js debug session with or without using workspaces.
Attaching a debug session in Next.js Standalone
The package.json
has the following dev
script, which just launches the Next.js in development mode.
Using Attach
mode type, the NODE_OPTIONS
env variable needs to be added with the --inspect
flag as follows.
/package.json
{
"name": "nextapp",
"scripts": {
"dev": "NODE_OPTIONS='--inspect' next dev"
}
...
}
The VS Code debug configuration will be as follows.
/.vscode/launch.json
{
"configurations": [
{
"name": "Attach debug server-side (Standalone)",
"port": 9230,
"request": "attach",
"skipFiles": [
"<node_internals>/**"
],
"type": "node"
}
]
}
The default port is 9229
but adding NODE_OPTIONS='--inspect' will use the next adjacent port 9230
to avoid port conflict.
To start the debug session, start the Next.js application manually.
npm run dev
Then attach the debugger with the Attach debug server-side (Standalone)
configuration.
The debugger will now be connected and ready for any breakpoints on server side code.
Launching debug session in Next.js Standalone
For Launch
mode type, you don't need to specify any NODE_OPTIONS
on the dev
script command and can just include it in the Launch
configuration as follows with runtimeArgs
.
/.vscode/launch.json
{
"configurations": [
{
"name": "Launch debug server-side (Standalone)",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/next",
"runtimeArgs": [
"--inspect"
],
"skipFiles": [
"<node_internals>/**"
]
},
]
}
Simply start the debugger with the Launch debug server-side (Standalone)
configuration to start the Next.js application process.
The debugger will now be connected and ready for any breakpoints on server side code.
Next.js debug setup in NPM workspaces
Now let's take a look at how NPM workspaces differ and the changes we'll need to make.
So if you're not familiar with using NPM workspaces or the monorepo approach, here's a high level overview.
Workspaces allow for multiple project modules to exist under one project root.
Configuring NPM workspaces
In the root package.json
you can define workspaces
as follows.
For example, consider the following:
apps
: Any UI apps can be located here.
packages
: Any shareable or internal dependency packages may be located here.
/package.json
{
"workspaces": [
"apps/*",
"packages/*"
],
}
The NPM package manager will automatically scan these locations and find all of the directories that have a package.json
defined and treat it as a separate workspace module.
A sample Next.js NPM workspaces project
So in the directory diagram below, NPM will manage all of the following workspaces we defined in the root package.json
.
└── project-root
├── apps
│ ├── nextapp1
│ │ ├── node_modules
│ │ ├── .next
│ │ ├── next.config.mjs
│ │ ├── app
│ │ └── package.json
│ └── nextapp2
│ │ ├── node_modules
│ │ ├── .next
│ │ ├── next.config.mjs
│ │ ├── app
│ │ └── package.json
├── packages
│ ├── shared
│ │ ├── package.json
│ │ └── src
│ └── services
│ ├── package.json
│ └── src
├── package.json
└── package-lock.json
In NPM workspaces, all package.json
files should have a unique package name. We'll assume in this tutorial that all the workspaces have the name of their respective directory.
Attach debug session in workspaces
Similar to standalone , each Next.js workspace
application will need to have the --inspect
flag added to the dev
start script or the launch configuration.
/apps/nextapp1/package.json
{
"name": "nextapp1",
"scripts": {
"dev": "NODE_OPTIONS='--inspect' next dev"
}
}
Since workspaces are nested in subdirectories, we'll need to add some additional settings to the launch.json
configuration.
Here's the Attach
configuration with the addition of the cwd
field to specify the current working directory path to the target workspace
.
Including cwd
is an essential step because wihout it, the debugger won't be able to reference the source maps and breakpoints won't attach properly.
/.vscode/launch.json
{"configurations": [
{
"name": "Attach debug server-side (Workspaces)",
"port": 9230,
"request": "attach",
"skipFiles": [
"<node_internals>/**"
],
"type": "node",
"cwd": "${workspaceFolder}/apps/nextapp1"
},
]}
To start and attach a debug session for nextapp1
, we'll need to use NPM workspace notation to target the Next.js workspace.
npm run dev -w nextapp1
Once the Next.js app is started, start the debugger with the Attach debug server-side (Workspace)
configuration.
The debugger is now ready for server-side breakpoints to be added.
Launch debug session in workspaces
Similar to the standalone Launch
type, we don't need to start the app manually, since it will lauch a Next.js process once we start the debugger.
Here's the debugger configuration settings to launch.
/.vscode/launch.json
{
"configurations": [
{
"name": "Launch debug server-side (Workspaces)",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/next",
"runtimeArgs": [
"--inspect"
],
"skipFiles": [
"<node_internals>/**"
],
"cwd": "${workspaceFolder}/apps/nextapp1"
},
]
}
Once the server starts, you can start adding server-side breakpoints.
Next.js client debug session
The one point we haven't mentioned is the client
side debugging in workspaces.
Since all of these debugging steps have been started in dev
mode, all of the client source maps are already available for debugging in Chrome by default.
However, to add a configuration for client
side, we could add the following to launch a new Chrome browser for debugging.
Example client configuration:
/.vscode/launch.json
{
"configurations": [
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
}
]}
Example full-stack configuration:
/.vscode/launch.json
{
"configurations": [
{
"name": "Next.js: debug full stack",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/next",
"runtimeArgs": [
"--inspect"
],
"skipFiles": [
"<node_internals>/**"
],
"serverReadyAction": {
"killOnServerStop": true,
"pattern": "listening on port ([0-9]+)",
"uriFormat": "http://localhost:%s",
"action": "openExternally"
},
"cwd": "${workspaceFolder}/apps/nextapp1"
}
]
}
Conclusion
Since React based frameworks like Next.js are moving towards (RSC) components, it's good to know how to put a debugger on your server-side code rather than just using console.log
output statements.
Whether you choose to use the Attach
or Launch
is up to you. My preference is always to attach so I can always start a debug session at any time.
Visual Studio Code has great support for debugging. There's a few extra steps to set up debugging in workspaces but well worth it.