Next.js is a modern JavaScript framework that optimizes the speed and performance of pages and applications, in addition to development speed and experience. It allows you to build hybrid or server-side rendered applications with minimal configuration. However, even Next.js applications need to be monitored for performance issues and errors. On the back end, you monitor application metrics—such as Apdex score, throughput, and transactions—to help scale projects. On the front end, you monitor browser-related metrics—such as the largest contentful paint, input delay, AJAX requests, or page views—for insight into performance and user behavior.
In this blog post, you’ll learn how to set up application performance monitoring for the server side and then browser monitoring for the front end, giving you full stack observability in your Next.js application. To start, you’ll need a New Relic account and license key, both available for free.
Backend observability
On the back end, Next.js uses Node.js and React for server-side rendering. In this blog post, we’ll go through the official New Relic Next.js instrumentation written in Node.js.
Installing the agent and middleware
Run the following command in your Next.js project to install the New Relic Node.js (APM) agent and New Relic middleware for Next.js.
npm install newrelic @newrelic/next
After the command completes successfully, you’ll see the dependencies included in your package.json
file.
"dependencies": {
"@newrelic/next": "^0.3.0",
"newrelic": "^9.0.0",
"next": "latest",
"react": "17.0.2",
"react-dom": "17.0.2"
},
Configuration
Next, modify your dev
and start
npm scripts by amending the scripts
section of package.json
file. Allow your application to run with Node’s -r
option, which will preload @newrelic/next
middleware.
NODE_OPTIONS='-r @newrelic/next' next start
The scripts
section should looks something like this:
"scripts": {
"dev": "NODE_OPTIONS='-r @newrelic/next' next",
"build": "next build",
"start": "NODE_OPTIONS='-r @newrelic/next' next start",
"lint": "next lint"
},
If you’re using Next.js to run a custom server, add the -r
option before your program runs:
"dev": "NODE_OPTIONS='-r @newrelic/next' ts-node --project tsconfig.server.json server/index.ts",
Whether you’re using node
or ts-node
, you can add NODE_OPTIONS
to the command in the exact same way.
Before you run your application, add the newrelic.js
AMP agent configuration file to the root directory of your project. Remember to add app_name
and license_key
values to the file. For more information, see an example config file for your Next.js app.
Viewing performance data in New Relic
Run your application and go to the APM page in New Relic. You’ll see your application’s server-side data flowing into New Relic.
Frontend observability
To monitor the front end of a Next.js application, you’ll need to inject the New Relic Browser agent. The agent uses a custom Document
concept and the newrelic
npm package you installed in the previous section. For more information, read our Docs about browser monitoring and the Node.js agent.
Configuration
The next code snippet, which you can also see in GitHub, shows the _document.tsx
file. You can use it to update the HTML tags (like <html>
, <body>
) that are used to render a Next.js page. In this case, you need to modify the <head>
tag of the document by injecting the New Relic browser agent script.
// pages/_document.tsx
const newrelic = require("newrelic");
import Document, {
DocumentContext,
DocumentInitialProps,
Html,
Head,
Main,
NextScript,
} from "next/document";
class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
const browserTimingHeader = newrelic.getBrowserTimingHeader({
hasToRemoveScriptWrapper: true,
});
return {
...initialProps,
browserTimingHeader,
};
}
render() {
return (
<Html>
<Head>
<script
type="text/javascript"
dangerouslySetInnerHTML={{ __html: this.props.browserTimingHeader }}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
Here are the steps for this process:
- Install the
newrelic
npm package if you haven’t already with thenpm install newrelic @newrelic/next
command. - Add the
newrelic.getBrowserTimingHeader
method inside thegetInitialProps
method..- Pass
hasToRemoveScriptWrapper: true
as an argument tonewrelic.getBrowserTimingHeader
so that the browser script is returned without the<script>
wrapper. See the node-newrelic docs for more details. - Return the initial props from the
getInitialProps
method along with thebrowserTimingHeader
script.
- Pass
- In the
render
method, inject the New Relic Browser agent script to the<head>
of the document. - The
_document.tsx(.js)
file should be in the root of thepages
directory of your project.
Viewing browser data in New Relic
Start the application and go to the browser monitoring page in New Relic to see client-side data from your application flowing into New Relic.
Sending detailed error information to New Relic
Send your application’s JavaScript error details to New Relic along with stack traces by utilizing the _error.tsx
page described in the More Advanced Error Page Customizing section of Next.js documentation.
The following example, which you can also find on the GitHub page, shows how the _error.tsx
file is customized to send error information to New Relic.
// pages/_error.tsx
function Error({ statusCode }) {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: "An error occurred on client"}
</p>
);
}
Error.getInitialProps = ({ res, err }) => {
if (typeof window == "undefined") {
const newrelic = require("newrelic");
newrelic.noticeError(err);
} else {
window.newrelic.noticeError(err);
}
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
return { statusCode };
};
export default Error;
The getInitialProps
prototype of the error function is updated to capture both backend and frontend errors and send them to New Relic using the noticeError
method.
Because _error.tsx
can be run on both the server and the browser, you need a condition that specifies whether the code is executing in the server or client. This is the if (typeof window == "undefined")
condition, which means the code is running on the server side and the newrelic
npm package should be required. Otherwise, you don’t need to worry about it because the client side already has the agent injected into it, as discussed in the previous section. Whether the code is run on the client or the server, the noticeError
method will then pass the error along to New Relic as an argument.
Here’s an example of a stack trace for a server-side error displaying in New Relic:
On the client side, since the New Relic browser agent attaches itself to the window
object, the noticeError
method is called on the window
object.
Here’s an example of a front-end error and its stack trace displayed in New Relic:
Putting your Next.js logs in context
New Relic supports automatic logs in context options. Log contexualization is important because it means sharing logs across related events, giving you additional context that can help you understand your application better. With the Node APM agent, you can automatically correlate and contextualize your Next.js application logs. Let’s look at the simplest way to forward logs to New Relic with the Winston framework. You can also look at other options in the Node.js: Configure logs in context documentation.
The first step is to update your newrelic.js
config file with the following attributes:
application_logging: {
forwarding: {
enabled: true,
},
},
This tells the Node APM agent to forward all application logs and automatically link them to spans, traces, and other telemetry data. You can make additional adjustments to the configuration, too, such as limiting the maximum number of logs sent to New Relic.
You’ll also need to enable distributed tracing in the agent configuration:
distributed_tracing: {
/**
* Enables/disables distributed tracing.
*
* @env NEW_RELIC_DISTRIBUTED_TRACING_ENABLED
*/
enabled: true,
},
The best way to integrate with the Winston logging framework is to write a simple component that can be used across your application in different files. It can look as simple as this Logger.tsx example.
// components/Logger.tsx
const winston = require("winston");
const logger = winston.createLogger({
transports: [new winston.transports.Console()],
});
export { logger };
Next, import the component into your project where it’s needed:
import { logger } from "../components/Logger";
Include the log information you’re interested in. For example, you could include it in your _document.tsx
file.
logger.info("NextJs New Relic redirecting to a page", {
application: "NextJs NewRelic app logging",
test: "Testing logging with Winston",
pathname: ctx.pathname,
});
Alternatively, you might want to gather information from a subpage:
logger.info("Getting post id", { postId: params.id });
Since Next.js is an isomorphic framework, it’s important to use the Winston logging framework in methods and files that are pre-rendered on the back end. In this example, the logger is added to the getServerSideProps
method. (It can be also used inside getStaticPaths
and getStaticProps
methods). If used in the wrong places, the Next.js compilation process won’t work properly and you might experience build errors. These errors can happen when adding Node-only compatible NPM modules to pages rendered by browsers only. Browsers don’t have access to the Node API. They throw errors and stop the compilation process.
After adding Winston and log statements to your application, you can run the application and to see the results in New Relic under Logs:
The next image shows automatic correlation between a transaction and logs:
Next steps
- You can find all the code samples in this blog post in the New Relic Next.js integration GitHub repository. You can give us any feedback in the GitHub repository issues section.
- Check out our Node.js integration page on GitHub.
- Sign up for a free New Relic account. Your free account includes 100 GB/month of free data ingest, one free full-access user, and unlimited free basic users.
The views expressed on this blog are those of the author and do not necessarily reflect the views of New Relic. Any solutions offered by the author are environment-specific and not part of the commercial solutions or support offered by New Relic. Please join us exclusively at the Explorers Hub (discuss.newrelic.com) for questions and support related to this blog post. This blog may contain links to content on third-party sites. By providing such links, New Relic does not adopt, guarantee, approve or endorse the information, views or products available on such sites.