Skip to content

Split browserTracingIntegration into standalone, tree-shakeable integrations #26253

Open

Description

@logaretm

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)

Activity

  1. linear-code commented on Nov 10, 2026

    @linear-code
  2. self-assigned this
    on Nov 10, 2026
  3. sentry-junior commented on Nov 10, 2026

    @sentry-junior
    Contributor

    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) 2,976 / 860 (#26134)
    linkedTracesIntegration linkTraces and the previous-trace storage browserTracingIntegration.ts L637-638, linkPreviousTrace !== 'off' 1,433 / 441 (#26137)
    longTaskIntegration (long tasks + LoAF) startTrackingLongAnimationFrames / startTrackingLongTasks browserTracingIntegration.ts L436-444, the LoAF-or-long-task if/else in setup 1,205 / 380 (#26141)
    extras from #26143 span error handlers (registerSpanErrorInstrumentation, L434), HTTP timings (request.ts L174/L195), background tab detection (L736-737) 1,544 / 437 combined (#26143)

    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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions