Skip to content

__SENTRY_TRACING__=false doesn't remove browser tracing from the Next.js client bundle: tracing imports are kept alive by INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME #23482

Description

@kevbarns

Is there an existing issue for this?

How do you use Sentry?

Self-hosted/on-premise

Which SDK are you using?

@sentry/nextjs

SDK Version

10.23.0

Framework Version

Next.js 16 (Turbopack)

Link to Sentry event

No response

Reproduction Example/SDK Setup

Since bundleSizeOptimizations.excludeTracing relies on webpack's DefinePlugin and we build with Turbopack, we set the flag via Next's compiler.define:

// next.config.mjs
const nextConfig = {
  compiler: {
    define: {
      __SENTRY_TRACING__: "false",
    },
  },
}

The text replacement itself works: in the output, the typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__ guard inside the client init becomes statically false and the browserTracingIntegration call site is eliminated.

Steps to Reproduce

  1. Next.js 16 app (App Router) with @sentry/nextjs 10.23.0, built with Turbopack.
  2. Define __SENTRY_TRACING__: "false" via compiler.define (see above).
  3. Build and inspect the client first-load chunks (e.g. with a bundle analyzer).

Expected Result

No browser tracing code in the client bundle when __SENTRY_TRACING__ is false.

Actual Result

18 tracing-related modules remain in the first-load bundle (browserTracingIntegration, startBrowserTracingPageLoadSpan/startBrowserTracingNavigationSpan and their transitive deps — request instrumentation, span utils, web-vitals, etc.). Estimated residual weight: ~30–40 KB minified, ~10 KB gzipped.

Root cause

client/index.ts unconditionally imports the constant INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME from client/routing/appRouterRoutingInstrumentation.ts — it is used in init regardless of the tracing flag (in 10.23.0 in the IncompleteTransactionFilter event processor; on current master in the default ignoreTransactions).

That module in turn has top-level static imports of the tracing machinery:

// client/routing/appRouterRoutingInstrumentation.ts
import { WINDOW, startBrowserTracingNavigationSpan, startBrowserTracingPageLoadSpan } from '@sentry/react';

So even though __SENTRY_TRACING__ = false removes every call site of appRouterInstrumentPageLoad / appRouterInstrumentNavigation, the module stays in the graph because of the constant, and its static imports pull the whole browser tracing implementation back in. Module-level DCE can't help here — the constant is live, and the tracing imports are colocated in the same module.

Suggested fix

Move INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME into its own dependency-free module (e.g. client/routing/constants.ts), re-export it from appRouterRoutingInstrumentation.ts for compatibility, and import it from the new module in client/index.ts. That decouples the always-needed constant from the tracing-only instrumentation code and lets bundlers drop appRouterRoutingInstrumentation.ts (and its @sentry/react tracing imports) when __SENTRY_TRACING__ is false.

Happy to open a PR if this sounds right.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status
    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions