The full NextJS Setup Guide For Error Reporting to Signoz

Monitoring Next.js with SigNoz offers several benefits. It provides real-time performance insights, allowing you to quickly identify and resolve issues. With comprehensive metrics on response times, error rates, and throughput, you can understand how your application behaves under different conditions. SigNoz also tracks user interactions to improve the overall user experience by identifying slow pages or problematic user flows. Additionally, it captures and logs errors, making it easier to diagnose and fix bugs. Insights into resource usage help optimize performance and reduce costs by pinpointing inefficient code or unnecessary resource consumption. Lastly, SigNoz aids in scalability analysis, ensuring your application can handle increased traffic effectively.

This guide will show you how to fully set up a Next.js project using the App Router with the ability to report errors to SigNoz. It covers both server and client components, so you can cherry-pick the steps based on your project setup.

Before reading this guide, we highly recommend reviewing the following:

  • Reliable Error Tracking
  • Next.js Instrumentation Pattern

Requirements

  • Follow the error infrastructure setup
  • Follow the instrumentation setup
  • Have a SigNoz Cloud subscription or a self-hosted instance
  • Be familiar with the SigNoz dashboard and query builder

Environment Variables Needed

  • NEXT_PUBLIC_SIGNOZ_ENDPOINT: The endpoint to send trace data to
  • NEXT_PUBLIC_NODE_ENV: Defines the current runtime environment

Project Folder Structure

Ensure your project matches the following structure:

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

Install OpenTelemetry Packages

We'll use OpenTelemetry to collect traces from our app:

npm install --save \
  @opentelemetry/api@^1.9.0 \
  @opentelemetry/auto-instrumentations-node@^0.56.0 \
  @opentelemetry/auto-instrumentations-web@^0.45.1 \
  @opentelemetry/context-zone@^2.0.0 \
  @opentelemetry/exporter-jaeger@^1.30.1 \
  @opentelemetry/exporter-metrics-otlp-http@^0.57.2 \
  @opentelemetry/exporter-trace-otlp-http@^0.57.2 \
  @opentelemetry/core@^1.30.1 \
  @opentelemetry/instrumentation@^0.57.2 \
  @opentelemetry/instrumentation-document-load@^0.44.1 \
  @opentelemetry/instrumentation-fetch@^0.57.2 \
  @opentelemetry/instrumentation-xml-http-request@^0.57.2 \
  @opentelemetry/resources@^1.30.1 \
  @opentelemetry/sdk-metrics@^1.30.1 \
  @opentelemetry/sdk-node@^0.57.2 \
  @opentelemetry/sdk-trace-base@^1.30.1 \
  @opentelemetry/sdk-trace-web@^1.30.1 \
  @opentelemetry/semantic-conventions@^1.30.0 \
  @opentelemetry/winston-transport@^0.10.0

These versions are verified for SigNoz. You can upgrade later if needed.

Server-Side Setup

Refer to the Next.js instrumentation docs.

instrumentation.node.ts

// src/instrumentation/instrumentation.node.ts

import process from "process";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { Resource } from "@opentelemetry/resources";
import { NodeSDK } from "@opentelemetry/sdk-node";
import {
  ATTR_SERVICE_NAME,
  SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,
} from "@opentelemetry/semantic-conventions";

const exporterOptions = (endpoint: "traces" | "metrics") => ({
  url: `${process.env.NEXT_PUBLIC_SIGNOZ_ENDPOINT}/${endpoint}`,
});

if (
  !process.env.NEXT_PUBLIC_NODE_ENV ||
  !process.env.NEXT_PUBLIC_SIGNOZ_ENDPOINT
) {
  throw new Error("Missing env variables for instrumentation.");
}

const resource = new Resource({
  [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: process.env.NEXT_PUBLIC_NODE_ENV,
  [ATTR_SERVICE_NAME]: "nextjs-apm-setup-demo",
});

const traceExporter = new OTLPTraceExporter(exporterOptions("traces"));

const sdk = new NodeSDK({
  traceExporter,
  instrumentations: [getNodeAutoInstrumentations()],
  resource,
});

sdk.start();

process.on("SIGTERM", () => {
  sdk
    .shutdown()
    .then(() => console.log("Tracing terminated"))
    .catch((err) => console.error("Error terminating tracing", err))
    .finally(() => process.exit(0));
});

instrumentation.ts

// src/instrumentation.ts

export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    await import("./instrumentation/instrumentation.node");
  }
}

Validation screenshots:



Client-Side Setup

Add global type declaration

// src/types/global.d.ts

import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";

declare global {
  interface Window {
    __OTEL_PROVIDER__?: WebTracerProvider;
  }
}

export {};

instrumentation.browser.ts

// src/instrumentation/instrumentation.browser.ts

import { propagation } from "@opentelemetry/api";
import { ZoneContextManager } from "@opentelemetry/context-zone";
import {
  CompositePropagator,
  W3CTraceContextPropagator,
} from "@opentelemetry/core";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
import { XMLHttpRequestInstrumentation } from "@opentelemetry/instrumentation-xml-http-request";
import { Resource } from "@opentelemetry/resources";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
import {
  ATTR_SERVICE_NAME,
  SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,
} from "@opentelemetry/semantic-conventions";

const appEnv = process.env.NEXT_PUBLIC_NODE_ENV || "development";
const sigNozEndpoint = process.env.NEXT_PUBLIC_SIGNOZ_ENDPOINT;

export function initBrowserTraceCollector() {
  if (
    typeof window === "undefined" ||
    !appEnv ||
    !sigNozEndpoint ||
    window.__OTEL_PROVIDER__
  ) {
    return;
  }

  const traceResource = new Resource({
    [ATTR_SERVICE_NAME]: "nextjs-apm-setup-demo-browser",
    [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: appEnv,
    application: "nextjs-apm-setup-demo-browser",
  });

  const traceExporter = new OTLPTraceExporter({
    url: `${sigNozEndpoint}/traces`,
    headers: {},
  });

  const traceProvider = new WebTracerProvider({ resource: traceResource });
  traceProvider.addSpanProcessor(new SimpleSpanProcessor(traceExporter));

  registerInstrumentations({
    instrumentations: [
      new DocumentLoadInstrumentation(),
      new FetchInstrumentation({
        clearTimingResources: true,
        propagateTraceHeaderCorsUrls: [/.*/],
      }),
      new XMLHttpRequestInstrumentation({
        clearTimingResources: true,
        propagateTraceHeaderCorsUrls: [/.*/],
      }),
    ],
  });

  traceProvider.register({
    contextManager: new ZoneContextManager(),
  });

  propagation.setGlobalPropagator(
    new CompositePropagator({
      propagators: [new W3CTraceContextPropagator()],
    })
  );

  window.__OTEL_PROVIDER__ = traceProvider;

  console.log("Browser OpenTelemetry initialized");
}

open-telemetry.tsx

// src/components/open-telemetry.tsx

"use client";

import { useEffect } from "react";
import { initBrowserTraceCollector } from "@instrumentation/instrumentation.browser";

export default function OpenTelemetry() {
  useEffect(() => {
    initBrowserTraceCollector();
  }, []);
  return null;
}

Add to layout

// src/app/layout.tsx

import "../styles/app.css";
import { PropsWithChildren } from "react";
import dynamic from "next/dynamic";

const ErrorTracer = dynamic(() => import("@components/error-tracer"), {
  ssr: false,
});
const OpenTelemetry = dynamic(() => import("@components/open-telemetry"), {
  ssr: false,
});

export default async function Layout({ children }: PropsWithChildren) {
  return (
    <html className="h-full" lang="en">
      <body className="min-h-full">
        <ErrorTracer />
        <OpenTelemetry />
        {children}
      </body>
    </html>
  );
}

report-error-to-signoz.ts

// src/utils/report-error-to-signoz.ts

import { SpanStatusCode, trace } from "@opentelemetry/api";

export function reportErrorToSignoz(error: Error): void {
  try {
    if (!window?.__OTEL_PROVIDER__) {
      console.warn("No tracer provider found.");
      return;
    }
    const tracer = trace.getTracer("Error Boundary");
    tracer.startActiveSpan("Error Boundary", (span) => {
      span.recordException(error);
      span.setAttribute("stackTrace", error.stack || "No stack trace");
      span.setAttribute("errorMessage", error.message);
      span.setAttribute("environment", "client");
      span.setStatus({ code: SpanStatusCode.ERROR });
      console.log("Reporting error to APM");
      span.end();
    });
  } catch (err) {
    console.error("Failed to report error:", err);
    console.error("Original error:", error);
  }
}

Update error-tracer.tsx

// src/components/error-tracer.tsx

"use client";

import { reportErrorToSignoz } from "@utils/report-error-to-signoz";
import { useEffect } from "react";

export default function ErrorTracer() {
  useEffect(() => {
    const handleError = (event: ErrorEvent) => {
      reportErrorToSignoz(event.error);
    };

    const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
      reportErrorToSignoz(event.reason);
    };

    window.addEventListener("error", handleError);
    window.addEventListener("unhandledrejection", handleUnhandledRejection);

    return () => {
      window.removeEventListener("error", handleError);
      window.removeEventListener("unhandledrejection", handleUnhandledRejection);
    };
  }, []);

  return null;
}

Validate

Refer to the reliable error tracking guide and test with a known error trigger:

Conclusion

This guide walks you through setting up full-stack error reporting and performance monitoring with SigNoz in a Next.js project. You now have end-to-end visibility from server to browser, helping you debug and optimize faster. For more, check out our reference repo, particularly the nextjs-error-reporting-setup-signoz 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
How to organize instrumentation for NextJS

How to organize instrumentation for NextJS

How to structure and organize your instrumentation logic in a Next.js project

Apr 8, 2025