Next.js debugging: tips and techniques from dev to prod
ON THIS PAGE
- Effective Next.js debugging tools and techniques
- How to solve common errors in Next.JS
- How the architecture of your app can affect debuggability
- How to leverage Sentry to improve error tracking and optimize Next.js app performance
- Debug Next.js applications with confidence
Debugging. It’s a critical skill for all developers. And when you’re building a dynamic, high-performance application with Next.js, Chrome DevTools, and console.log() aren’t always enough. There are more effective and structured ways to debug Next.js apps as they scale.
You will also find practical tips from our Next.js debugging workshop sprinkled throughout. Also, while this guide is focused primarily on Next.js, there is a similar guide for debugging React apps here.
Effective Next.js debugging tools and techniques
Debugging starts when you’re still in your editor and often testing on localhost, so that’s where this guide will start, too. Don’t worry. By the end, you will also know how to debug once your app has been deployed to production and real users are finding the bugs instead of your dev team.
1. Leverage React Developer Tools
Next.js is a React framework, and luckily, the React Developer Tools are effective for inspecting component hierarchies, props, and states. React DevTools give you a clear, visual representation of your component tree, making it easy to understand the component hierarchy even with dynamic routing and server-side rendering (SSR), allow you to inspect and edit component state and props in real-time, track re-renders, and even help visualize how React Suspense handles lazy-loading.
Debugging Tip: Use React DevTools to uncover hydration errors
"During SSR and CSR transitions, subtle hydration bugs arise when props change. React DevTools can show when components re-render with inconsistent props."
A common issue we discussed during our Next.js debugging workshop (now on-demand) was debugging misaligned props between server-side and client-side rendering. With React DevTools, you can inspect props during hydration and catch mismatches early on, which will really help you improve the performance of your app.
Example:
function MyComponent({ propFromServer }) { useEffect(() => { console.log(propFromServer); // Check for inconsistencies in logs }, [propFromServer]); }
2. Custom error pages
Whether you’re writing 100% of the code or using AI to help you, errors will happen. When your users encounter those errors in the wild, you had better have effective error handling in place. Creating a custom error page (pages/_error.js
) to handle application-level errors is a quick and effective way to ensure that when something breaks, your users aren’t confused and that you don’t lose their trust.
You can use getInitialProps
to capture error data, like HTTP status codes, and provide custom messaging to the user. Consider also sending the users to a different part of your application and helping them complete as much of the task as possible. Handling errors gracefully in both SSR and CSR and a custom error page could provide better insights, especially when combined with automated error tracking.
Example:
function Error({ statusCode }) { return ( <p> {statusCode ? `An error ${statusCode} occurred on server` : 'An error occurred on client'} </p> ); } Error.getInitialProps = ({ res, err }) => { const statusCode = res ? res.statusCode : err ? err.statusCode : 404; return { statusCode }; }; export default Error;
3. Debugging Next.js client-side and server-side in VSCode
In Next.js, pages have both server-side and client-side aspects: the server-side handles page generation, and the client-side manages interactivity and rendering. To debug both effectively in VSCode, you’ll need to configure two launch tasks: one for server-side debugging (attaching to the Next.js server process) and one for client-side debugging (attaching to the browser’s JavaScript runtime).
By combining server-side and client-side debugging tasks in VSCode, you can fully leverage the debugger to handle both aspects of Next.js applications, offering a complete view of the execution flow and allowing you to identify issues efficiently.
Server-side debugging
Server-side logic in Next.js often resides in the getServerSideProps
method, which runs on every request. To debug it, open (or create) the .vscode
folder in your project’s root directory and find (or create) the launch.json
file. Configure a Node.js launch task to debug the server-side code by specifying the Next.js runtime as the runtimeExecutable
.
Example configuration:
{ "type": "pwa-node", "request": "launch", "name": "Next: Node", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/next" }
Now, add a breakpoint in one of your getServerSideProps
methods and start the debugger. This allows you to step through server-side logic, inspecting data from sources like external APIs in real-time.
Client-side debugging
To debug the client-side in Next.js, install the Debugger for Chrome extension. Then, add a Chrome launch task in launch.json
. This will allow you to debug client-side React code directly in the browser as it interacts with the Next.js app.
Example configuration:
{ "name": "Launch Chrome", "request": "launch", "type": "pwa-chrome", "url": "http://localhost:3000", "webRoot": "${workspaceFolder}" }
Now, add breakpoints in your React components, such as index.tsx
. Launch Chrome from VSCode, and you can debug client-side properties like component state or props, step through the code, and inspect changes.
How to solve common errors in Next.JS
As you build (and inevitably debug) Next.js applications, you’ll likely encounter a few common errors that even seasoned developers deal with every day. To save you from scouring multiple sources, we’ve compiled a comprehensive guide on Common Next.js Errors and How to Resolve Them, detailing why these bugs happen and how to fix them.
Here’s a sneak peek at some of the most frequently reported errors:
Document/Window Object Error
Middleware Error
API/Slug Error
Module Error
CORS Error (Next API Routes)
And if you can’t find the issue you’re dealing with in that guide, you’ll find even more common Next.js errors with step-by-step fixes on the Sentry Answers hub for Next.js (might be something you want to bookmark).
How the architecture of your app can affect debuggability
In the age of AI, writing code is giving developers the opportunity to spend more time thinking about the design of our applications instead. If you haven’t been thinking about the right architecture to use for what you’re building, take this as a sign to do so because the architecture you choose very much influences how easy your application is to extend, maintain, and debug.
For example, Clean Architecture is a clean (pun intended) option when it comes to debugging complex applications like those often built with Next.js. By organizing your code into layers—UI, business logic, and data—you can easily isolate and fix issues without affecting unrelated parts of your application. This not only enhances readability but also improves maintainability.
For instance, when debugging a UI issue, you won’t have to dig through business logic, saving time and reducing confusion. Adopting this approach in your Next.js projects will lead to fewer dependencies and faster debugging sessions. Check out our deep dive workshop into Clean Architecture or Lazar Nikolov’s YouTube video here.
How to leverage Sentry to improve error tracking and optimize Next.js app performance
So you’ve got your basics covered with the tips, tools, and techniques we’ve covered so far, and your app is ready to be deployed and used by real users; what else do you need to ensure a quality and reliable experience? Well, Sentry of course! But why?
As mentioned before, when your app is in the wild, being used by real users around the world on various device types and with inconsistent network access, you will begin to discover issues that you never knew existed and are likely not even able to reproduce locally. “It works on my machine” isn’t good enough, but that’s where Sentry can help.
With a focus on debuggability, Sentry is a performance monitoring platform that aims to get you to the root cause and fix an issue as quickly as possible so that you can get back to building the app and not just debugging it. For a short demo, check out this workshop using a Next.js e-commerce site as an example. Below are five key Sentry features you need to know about for debugging Next.js.
1. Tracing and debugging API errors with Sentry
One of the major pain points when debugging Next.js applications involves diagnosing API issues. Sentry’s real-time API monitoring helps capture errors that might be lost in server logs.
Debugging Tip: Use Sentry breadcrumbs to discover race conditions
"We tracked down a persistent 500 error during a real-time demo. By using Sentry’s breadcrumbs, we identified a race condition occurring in our getServerSideProps
fetch logic."
Example of error handling in getServerSideProps
:
export async function getServerSideProps() { try { const res = await fetch('https://api.example.com/data'); if (!res.ok) throw new Error('Failed to fetch data'); const data = await res.json(); return { props: { data } }; } catch (error) { Sentry.captureException(error); return { props: { error: 'Data not available' } }; } }
2. Performance issues are bugs and should be debugged
Performance is critical in Next.js, especially when using server-side rendering (SSR). Sentry’s Performance Insights helps track slow database queries, API calls, and rendering bottlenecks, providing a detailed view of how your app is performing.
Debugging Tip: Try out distributed tracing with Sentry to identify performance bottlenecks
"We used Sentry’s performance tracing to pinpoint slow external API calls in an SSR context. By visualizing request timings in a waterfall view, we identified areas to optimize."
To get started, you can set up tracing by initializing Sentry in your app:
Sentry.init({ dsn: process.env.SENTRY_DSN, tracesSampleRate: 1.0, });
You can use Sentry Profiling to monitor SSR page loads and API requests with spans that track performance:
export async function getServerSideProps() { const transaction = Sentry.startTransaction({ name: "SSR Page Load" }); try { const res = await fetch('https://api.example.com/data'); const data = await res.json(); return { props: { data } }; } finally { transaction.finish(); } }
In this example, Sentry tracks how long it takes to fetch data during an SSR request, allowing you to pinpoint slow external APIs or rendering issues. Sentry's waterfall view makes it easy to see how each operation performs and where optimizations are needed.
By monitoring both frontend and backend operations, Sentry helps you identify and resolve performance bottlenecks, improving the overall speed of your Next.js app. For more information, here is a guide on how to use Sentry to monitor Next.js performance.
3. Session Replay helps developers understand and debug real user experiences
Sentry's Session Replay product captures user interactions, making it easier to reproduce bugs that are otherwise hard to replicate. It records the user journey and helps you see exactly what led to an error.
Debugging Tip: Leverage Session Replay to discover how users reach complex states
"Session Replay captured user interactions leading to a complex state management bug. We replayed the session to observe user inputs, which gave us crucial context we couldn’t obtain through logs alone."
Example of enabling session replay:
Sentry.init({ dsn: process.env.SENTRY_DSN, replaysSessionSampleRate: 1.0, // Capture 100% of sessions });
With this setup, Sentry automatically captures and replays user sessions, allowing you to step through interactions that lead to bugs. This feature is especially helpful for understanding UI or state-related issues that are difficult to reproduce during development.
By combining session replay with error monitoring and performance insights, you gain a complete picture of how your Next.js app behaves in the real world, improving your ability to diagnose and resolve issues quickly. For more details, explore Sentry’s session replay documentation.
4. Distributed tracing shines light on complex issues
Distributed tracing is essential for tracking how requests flow through your application from the frontend to the backend and through all of the various services you’re likely using. Even more, if you’re building a microservices architecture or using a serverless environment, distributed tracing might be one of the only effective ways to determine the root cause of an issue. And it’s actually pretty easy if you’re using a platform like Sentry that is centered around debuggability. By installing the Sentry SDK on your frontend, backend, and in all of your services, Sentry’s distributed tracing capabilities automatically log spans, making it easy to trace tricky errors and even identify performance bottlenecks; when a slowdown is happening on the frontend, it seems all too often that it’s due to a slow database query.
By tracing requests across your entire stack, you gain visibility into errors and performance issues that are otherwise hard to detect. Whether you’re building something on your own or a part of a large team, being able to not only trace to the root cause but also identify what area of the app (and therefore, which team) should dive into debugging the issue quickly will give you more time to innovate rather than dig through logs and attempting to repro unique customer environments.
Learn more about setting up distributed tracing in Next.jsin Lazar Nikolov’s full eight-part YouTube series on distributed tracing in Next.js.
5. Integrating Sentry into Next.js applications is easy
You now know that Sentry enables you to capture unhandled exceptions, API errors, and performance bottlenecks in real-time. When integrated into Next.js applications (especially with SSR), Sentry captures context, stack traces, and performance metrics for both client-side and server-side errors. By tracing errors back to the exact API call or code location, Sentry reduces debugging time dramatically and is quite easy to install.
After you’ve signed up for Sentry, all you really have to do is run the installation wizard:
npx @sentry/wizard@latest -i nextjs
This wizard will create three config files at the root of your application: sentry.client.config.js,
sentry.edge.config.js
, and sentry.server.config.js
. We highly recommend you set up tracing and Session Replay right when you start building your application. This means your client config file should look like this:
Filename: sentry.client.config.js
``` import * as Sentry from "@sentry/nextjs"; Sentry.init({ dsn: YOUR_DSN, integrations: [ Sentry.replayIntegration(), ], tracesSampleRate: 1, replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, debug: false, }); ```
However you might consider changing the sample rates to better match your expected usage. The wizard will also create a page with a sample error so that you can make sure your setup is correct.
Then, capturing exceptions is pretty easy from there! Just use Sentry’s captureException
to log handled errors:
try { const res = await fetch('/api/data'); if (!res.ok) throw new Error('API Error'); } catch (error) { Sentry.captureException(error); }
Debug Next.js applications with confidence
Debugging Next.js apps efficiently requires a blend of traditional techniques and advanced error-tracking tools. By starting with some basics, understanding common issues, designing your app for clean architecture, and debugging with sentry, you are equipped to solve even the most complex Next.js issues quickly and effectively.
Next.js Debugging FAQs
Hydration issues typically occur when server-rendered content differs from the client-rendered content. Ensure your server-side and client-side states are aligned, and use React.StrictMode to catch potential issues early. More on this here.
Use the Node.js inspector to attach a debugger to your SSR code. For production debugging, Sentry captures SSR-specific exceptions and provides stack traces for faster resolution.
Sentry’s Next.js monitoring tracks slow API routes, page load times, and bottlenecks in server-side rendering, providing flame graphs for detailed insights into performance issues.