You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Split browserTracingIntegration into standalone, tree-shakeable integrations #26253
browserTracingIntegration() has several features that are on by default but only gated at runtime, so bundlers can't drop them.
#26134, #26137, #26141 and #26143 tried to fix this with build flags. We closed them because flags have their own costs:
We need to document them and add them to every bundler plugin.
They become a public API and thus must be maintained.
If we never document them and keep them a "secret" API, then every user pays those extra check bytes with no reason or way to set them.
Instead, we can split browserTracingIntegration into more integrations that it loads by default, like we did with webVitalsIntegration and several timing integrations. The default stays the same, and users who care about size can skip browserTracingIntegration and import only the parts they need.
Features from those PRs to cover:
XHR request instrumentation (#26134), for fetch-only apps
Linked traces (#26137). An early return inside linkTraces only saved ~600 B, so the split needs to happen at the call site.
Long task and LoAF tracking (#26141). They're alternatives in the same if/else, so one integration covering both makes sense.
Span error handlers, HTTP timings and background tab detection (#26143)
Thanks for picking this up. Here's a proposed shape for the features from the closed flag PRs, so we can agree on it before writing code.
browserTracingIntegration() stays the batteries-included default. Each feature below becomes its own integration, auto-registered in afterAllSetup the way webVitalsIntegration is today: only if the user hasn't added one, and only if its existing option isn't turned off. Defaults and the existing options don't change.
Proposed integration
What moves
Seam today (develop 6b42ee7)
Bytes dropped in our flag PRs (raw / br-11)
xhrTracingIntegration
XHR instrumentation and XHR span creation
request.ts L183 if (traceXHR) inside instrumentOutgoingRequests (called from browserTracingIntegration.ts L740)
All numbers come from esbuild 0.27 (minify, ESM, browser) on a tracing-only setup: BrowserClient + browserTracingIntegration(), with __SENTRY_DEBUG__: false. They measure the code that went away, so the net saving after the split is a bit lower, by the size of each new integration wrapper.
Notes on coupling:
XHR reads the same request options as fetch (tracePropagationTargets, shouldCreateSpanForRequest, onRequestSpanStart/End, enableHTTPTimings). The new integration would need those options as well, or read them from the client.
HTTP timings is called per request span from both the fetch and XHR callbacks, so it's the hardest to split. It would need a hook on request-span start. If that hook costs more than about 190 B br, it should stay in core.
Background tab saves only about 115 B br, so it's probably not worth a wrapper. I'd keep it in core unless you see it differently.
Span error handlers are global and self-contained, so they're easy to split.
One question on the lean path: as long as browserTracingIntegration imports these integrations to auto-register them, they stay in the bundle. Bundle size only drops if users import a lean entry point that doesn't pull them in. That's the "irreducible core" item in your list. Do you already have a name or shape in mind for it, for example a core integration that browserTracingIntegration wraps?
We'd start with XHR, since you named it. Does this shape work for you?
Activity
linear-code commentedon Nov 10, 2026
JS-4095
sentry-junior commentedon Nov 10, 2026
Thanks for picking this up. Here's a proposed shape for the features from the closed flag PRs, so we can agree on it before writing code.
browserTracingIntegration()stays the batteries-included default. Each feature below becomes its own integration, auto-registered inafterAllSetupthe waywebVitalsIntegrationis today: only if the user hasn't added one, and only if its existing option isn't turned off. Defaults and the existing options don't change.6b42ee7)xhrTracingIntegrationrequest.tsL183if (traceXHR)insideinstrumentOutgoingRequests(called frombrowserTracingIntegration.tsL740)linkedTracesIntegrationlinkTracesand the previous-trace storagebrowserTracingIntegration.tsL637-638,linkPreviousTrace !== 'off'longTaskIntegration(long tasks + LoAF)startTrackingLongAnimationFrames/startTrackingLongTasksbrowserTracingIntegration.tsL436-444, the LoAF-or-long-taskif/elseinsetupregisterSpanErrorInstrumentation, L434), HTTP timings (request.tsL174/L195), background tab detection (L736-737)All numbers come from esbuild 0.27 (minify, ESM, browser) on a tracing-only setup:
BrowserClient+browserTracingIntegration(), with__SENTRY_DEBUG__: false. They measure the code that went away, so the net saving after the split is a bit lower, by the size of each new integration wrapper.Notes on coupling:
tracePropagationTargets,shouldCreateSpanForRequest,onRequestSpanStart/End,enableHTTPTimings). The new integration would need those options as well, or read them from the client.One question on the lean path: as long as
browserTracingIntegrationimports these integrations to auto-register them, they stay in the bundle. Bundle size only drops if users import a lean entry point that doesn't pull them in. That's the "irreducible core" item in your list. Do you already have a name or shape in mind for it, for example a core integration thatbrowserTracingIntegrationwraps?We'd start with XHR, since you named it. Does this shape work for you?
docs(browser): Fix `enableLongAnimationFrame` default in JSDoc (#26256)