Skip to the solution
HardSystem Design4 min read

How do you handle instrumentation and observability in Next.js (instrumentation.ts)?

1.0k views

The problem

Question presented to candidate: "How would you add tracing and error reporting to a Next.js app, and which hooks does Next.js give you for that?"

What a strong answer should cover:

  • instrumentation.ts at the project root exports register(), called once when a server instance starts, before it handles requests; use it to set up OpenTelemetry or an error SDK.
  • It also exports onRequestError(error, request, context), called for errors during rendering, Route Handlers, Server Actions and Proxy, with the path and route type.
  • The error digest matches the one sent to the browser, so client reports and server logs can be joined.
  • instrumentation-client.ts runs in the browser before the app becomes interactive; only its synchronous top-level code is guaranteed to run before hydration.
  • Next.js emits OpenTelemetry spans for rendering and fetch; @vercel/otel or the OpenTelemetry SDK exports them.

Clarifying questions expected:

  • "Which vendor or collector?" β€” OpenTelemetry keeps you vendor-neutral.
  • "Do you need to correlate browser errors with server logs?" β€” use the digest.

Code / implementation expected: Yes β€” both hooks, and client instrumentation.

  • nextjs
  • instrumentation
  • observability
  • opentelemetry

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

4 min read Β· 8 sections

Read the code

OpenTelemetry setup and error reporting
// instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    const { registerOTel } = await import("@vercel/otel");
    registerOTel({ serviceName: "shop-web" });
  }
}

export async function onRequestError(
  err: unknown,
  request: { path: string; method: string },
  context: { routePath: string; routeType: string },
) {
  const e = err as Error & { digest?: string };
  await fetch("https://errors.example.com/ingest", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ message: e.message, digest: e.digest, path: request.path, route: context.routePath, type: context.routeType }),
  });
}

// instrumentation-client.ts (browser)
performance.mark("app-init");
window.addEventListener("error", (event) => navigator.sendBeacon("/api/client-errors", String(event.message)));

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 103 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextHard

How do you do optimistic updates in Next.js (useOptimistic, useActionState)?

Continue