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
- Next.js 16 app (App Router) with
@sentry/nextjs 10.23.0, built with Turbopack.
- Define
__SENTRY_TRACING__: "false" via compiler.define (see above).
- 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.
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.excludeTracingrelies on webpack's DefinePlugin and we build with Turbopack, we set the flag via Next'scompiler.define:The text replacement itself works: in the output, the
typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__guard inside the clientinitbecomes statically false and thebrowserTracingIntegrationcall site is eliminated.Steps to Reproduce
@sentry/nextjs10.23.0, built with Turbopack.__SENTRY_TRACING__: "false"viacompiler.define(see above).Expected Result
No browser tracing code in the client bundle when
__SENTRY_TRACING__isfalse.Actual Result
18 tracing-related modules remain in the first-load bundle (
browserTracingIntegration,startBrowserTracingPageLoadSpan/startBrowserTracingNavigationSpanand their transitive deps — request instrumentation, span utils, web-vitals, etc.). Estimated residual weight: ~30–40 KB minified, ~10 KB gzipped.Root cause
client/index.tsunconditionally imports the constantINCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAMEfromclient/routing/appRouterRoutingInstrumentation.ts— it is used ininitregardless of the tracing flag (in 10.23.0 in theIncompleteTransactionFilterevent processor; on current master in the defaultignoreTransactions).That module in turn has top-level static imports of the tracing machinery:
So even though
__SENTRY_TRACING__ = falseremoves every call site ofappRouterInstrumentPageLoad/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_NAMEinto its own dependency-free module (e.g.client/routing/constants.ts), re-export it fromappRouterRoutingInstrumentation.tsfor compatibility, and import it from the new module inclient/index.ts. That decouples the always-needed constant from the tracing-only instrumentation code and lets bundlers dropappRouterRoutingInstrumentation.ts(and its@sentry/reacttracing imports) when__SENTRY_TRACING__isfalse.Happy to open a PR if this sounds right.