How to organize instrumentation for NextJS

Instrumentation in Next.js offers valuable insights into the performance and behavior of your application by enabling detailed monitoring, tracing, and logging. With instrumentation, developers can track metrics like route changes, server-side rendering times, API response durations, and client-side interactions, helping identify bottlenecks and optimize the user experience. It also aids in error detection, performance tuning, and understanding usage patterns through analytics. By integrating with observability tools such as OpenTelemetry, Sentry, or Datadog, instrumentation empowers teams to maintain high application reliability and respond proactively to issues in both development and production environments.

This guide will show you how to enable instrumentation in Next.js and organize your instrumentation logic into folders. You'll also understand the reasoning behind this folder structure and why multiple instrumentation files are needed.

Setting up the project folder structure

To ensure you're on the same page with this guide, set up the project structure as follows:

root
├── .env.local
├── next.config.js
├── tsconfig.json
├── package.json
├── package-lock.json
└── src
    ├── app
    ├── components
    ├── config
    ├── hooks
    ├── instrumentation
    ├── styles
    ├── utils
    └── instrumentation.ts

Having an instrumentation folder with files like instrumentation.node.ts, instrumentation.browser.ts, and instrumentation.edge.ts—which we’ll discuss later—enables environment-specific monitoring and observability in a Next.js project. Each environment—Node.js (used in server-side rendering and API routes), the browser (client-side interactions), and Edge (Edge Functions or Middleware)—has unique characteristics, performance metrics, and logging needs. By separating the logic into dedicated files, developers can tailor the instrumentation to capture the most relevant data for each context without mixing concerns. This separation also aligns with Next.js’s execution model, ensuring instrumentation logic is only loaded where needed, improving both performance and maintainability. It provides flexibility to integrate with different observability tools or techniques depending on the runtime, enabling more accurate and efficient monitoring across the entire stack.

Add the following alias to your tsconfig.json as we’ll use it later

We recommend using aliases in your project. If you're like us, add the following code to your tsconfig.json:

// tsconfig.json
{
  "compilerOptions": {
    // compiler options ...
    "paths": {
      // some aliases for your project
      "@instrumentation/*": ["./src/instrumentation/*"]
      // other aliases for your project
    }
  }
  // other options here
}

Enabling instrumentation in Next.js

The next.config.js file enables instrumentation in a Next.js project by setting the instrumentationHook flag to true under the experimental options. This tells Next.js to look for a file named instrumentation.ts in the src directory and execute the register() function exported from it during server startup. This allows us to hook into the server runtime and initialize tracing and metrics collection. By enabling this feature, we gain deeper insights into the application’s performance without affecting the client-side bundle or introducing unnecessary overhead.

// next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    instrumentationHook: true,
  },
};

module.exports = nextConfig;

Adding the instrumentation.ts file

The instrumentation.ts file in a Next.js project is used to conditionally load server-side instrumentation logic, particularly when integrating observability tools or any server-related hooks. It checks whether the application is running in the Node.js runtime (process.env.NEXT_RUNTIME === 'nodejs') and, if so, dynamically imports a separate module, instrumentation.node.ts, that contains the actual instrumentation setup—which we’ll discuss shortly. First, try the following code:

// src/instrumentation.ts

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    console.log("Hello from instrumentation")
  }
}

To ensure you’ve set up instrumentation correctly, run the app in dev mode and check whether the output matches the following:

Adding the instrumentation.node.ts file

The instrumentation.node.ts file is used to set up observability for server-side operations like API routes and server-side rendering. It typically includes the initialization of monitoring tools such as Sentry or OpenTelemetry, performance tracing, custom logging, and metrics collection. This file ensures that server-specific telemetry is captured accurately, helping us monitor performance, detect errors, and gain insights into backend behavior without affecting client-side or edge runtimes.

// src/instrumentation/instrumentation.node.ts

// Here you can run your own server-side instrumentation hook

Adding the instrumentation.edge.ts file

The instrumentation.edge.ts file is intended for setting up observability specifically in Edge runtimes like middleware or Edge Functions in Next.js. Since Edge environments have different constraints (e.g., limited APIs, no Node.js), this file should contain lightweight, compatible instrumentation logic.

// src/instrumentation/instrumentation.edge.ts

// Here you can run your instrumentation hook for edge runtimes

Adding the instrumentation.browser.ts file

The instrumentation.browser.ts file is used to set up client-side monitoring in your app, such as initializing analytics or error tracking tools. Wrapping the logic in a function like initInstrumentation() instead of running it immediately allows you to control when the instrumentation runs—typically during app initialization—ensuring it’s triggered at the right time and only in the appropriate environment.

// src/instrumentation/instrumentation.browser.ts

export function initInstrumentation() {
  // Here you can initialize client-side instrumentation
}

Calling instrumentation.node from the instrumentation.ts file

Now, edit your instrumentation.ts to run the instrumentation logic in instrumentation.node.ts. We're dynamically importing it to ensure the logic runs only on the server side, preventing unnecessary code from being bundled into the client-side build and avoiding runtime errors in the browser. It also keeps the codebase clean and modular, separating server-specific concerns from the rest of the application logic.

// src/instrumentation.ts

function resolveRuntime(): 'nodejs' | 'edge' {
  // Check if process is available and has NEXT_RUNTIME
  if (typeof process !== 'undefined' && process?.env?.NEXT_RUNTIME) {
    const runtime = process.env.NEXT_RUNTIME
    if (runtime === 'nodejs' || runtime === 'edge') {
      return runtime
    }
  }
  
  // Fallback: detect Node.js runtime via process.release
  if (typeof process !== 'undefined' && process.release?.name === 'node') {
    return 'nodejs'
  }
  
  // Default fallback to nodejs for server-side execution
  return 'nodejs'
}

export async function register() {
  const runtime = resolveRuntime()
  
  switch (runtime) {
    case 'nodejs':
      await import('./instrumentation/instrumentation.node')
      break
    case 'edge':
      await import('./instrumentation/instrumentation.edge')
      break
    default:
      // Fallback to nodejs if runtime is unknown
      await import('./instrumentation/instrumentation.node')
      break
  }
}

The instrumentation.browser.ts file can be used in components where server-side rendering is disabled. You can import it into your layout and call the initInstrumentation() function. An example of how to do this will be provided in upcoming guides.

We also have other useful guides on how to set up the frontend to report errors to popular APMs such as Sentry and SigNoz. Check out our latest guides:

  • How to set up Next.js to report errors to SigNoz
  • How to set up Next.js to report errors to Sentry
If you need a reference, check the instrumentation-infra-setup folder.

Written by

Engineering Team

Engineering Team

Development

Our engineering team is a group of highly skilled and experienced software engineers with a passion for building high-quality web and mobile applications. They are dedicated to creating reliable, scalable, and user-friendly software solutions that meet the needs of our clients.

Tap a star to rate

More posts

The full NextJS Setup Guide For Error Reporting to Sentry

The full NextJS Setup Guide For Error Reporting to Sentry

The full guide for setting up Sentry for NextJS server side and browser side to report errors

Apr 10, 2025
The full NextJS Setup Guide For Error Reporting to Signoz

The full NextJS Setup Guide For Error Reporting to Signoz

The full guide for setting up SigNoz for NextJS server side and browser side to report errors

Apr 9, 2025