ref(react, browser): make @sentry/react tree-shakeable by default#22168
ref(react, browser): make @sentry/react tree-shakeable by default#22168anonrig wants to merge 8 commits into
Conversation
Stop star-exporting the full @sentry/browser surface from @sentry/react. Bundlers that materialize a dynamic import() namespace (notably Rolldown when destructuring the result) were pulling optional features such as Session Replay, Feedback, AI instrumenters, and unused router integrations into the critical path even when apps never referenced them. Add @sentry/browser/core for the slim error-monitoring + tracing surface, move React router/Redux integrations to package subpaths, and re-export optional browser APIs via @sentry/react/optional-browser-api for framework SDKs that need the historical surface.
uiProfiler was part of the public @sentry/browser exports re-exported via
export * from @sentry/react. The curated browser-api list omitted it, which
would break import { uiProfiler } from '@sentry/react' (and framework SDKs
that re-export the React surface).
…eUrl uiProfiler was still missing from @sentry/react/optional-browser-api while browserProfilingIntegration was present, so optional-path profiling setups could not call uiProfiler. Also re-export getAbsoluteUrl, the last remaining public @sentry/browser value missing from the curated React surfaces.
Restore createReduxEnhancer on the Next.js client and Gatsby surfaces so index.types and historical import * usage keep working, re-export the FeatureFlagsIntegration type from optional-browser-api, and document the intentional @sentry/react root export break in the changelog and README. Co-Authored-By: Grok <noreply@x.ai>
…ypes Mirror nextjs/gatsby so dual-typed framework entries keep the historical optional browser surface after @sentry/react stopped star-exporting it. Also re-export tanstackRouterBrowserTracingIntegration for tanstackstart-react. Co-Authored-By: Grok <noreply@x.ai>
|
Automated fix: addressed CI failure in Build (@sentry/remix / @sentry/tanstackstart-react types). |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit cf92bd3. Configure here.
Explicitly re-export uiProfiler after dual export * from @sentry/react and optional-browser-api so ESM does not omit the conflicting name. Re-export createReduxEnhancer from remix client/cloudflare to match nextjs/gatsby. Co-Authored-By: Grok <noreply@x.ai>
Import replay from optional-browser-api and router helpers from package subpaths (reactrouterv6/v7/reactrouter/reactrouter.compat/tanstackrouter) instead of the @sentry/react root, which no longer re-exports them. Co-Authored-By: Grok <noreply@x.ai>
|
Automated fix: updated E2E apps for subpath/optional-browser-api imports. |
react-router-7-spa-streaming still called Sentry.spanStreamingIntegration after optional APIs left the @sentry/react root. Co-Authored-By: Grok <noreply@x.ai>
|
Hey @anonrig thanks for opening this PR! I understand the issue at hand and can see why the barrel-export style causes an issue with Rolldown at the moment (more on that below). If we assume that bundlers are not smart enough for tree shaking dynamic imports, then I agree that the current exports of the top level Sentry packages are far from ideal. However, we have to weigh the proposed changes against a major usability trade-off: The top-level export is convenient. Users don't need to look up which subpath export contains what. It's just there. Moreover, it ensures usage parity between our NPM package and CDN bundles, something that has always been important for our users. If we were to change this, we'd need to consider that it would be a breaking change, affecting the majority of our users. Ease of use of our SDKs is a priority for us. Bundle size, too. The Sentry JS SDKs are therefore designed to fully leverage tree shaking. We even provide advanced tree shaking options for users who are really bundle-size conscious. If you use the SDK as documented, without dynamic imports, every modern bundler (basically everything after Webpack 4 and esbuild) is able to tree-shake. We don't document dynamically importing the SDK because in all our testing so far, it had no noticeable advantage over a classic eager import. Especially for an integration like Since you're using Rolldown and it indeed currently can't tree-shake dynamic imports, @logaretm took a stab at fixing this bug directly in Rolldown: rolldown/rolldown#10213. Once this lands, there should be noticeably better tree shaking for dynamically imported modules. Our own overhead tests show little to no advantage when comparing an eagerly imported SDK setup with tracing to dynamically importing tracing: (Actually throttled slow 4g Network) (Lighthouse-Lantern simulated slow 4g speeds) For specific integrations we provide The PR proposes to limit the top-level export surface to a set of "core" APIs. While I fully understand why this is proposed, I think defining this "core" set so that everyone is happy is close to impossible and we'd have to draw an arbitrary line. More importantly, assuming bundlers cannot tree shake dynamically imported code, you'd still end up with a considerable amount of unused code in the core part. We're not dismissing the idea of lazy-loading parts of the SDK. In fact, it's on our to-do list to pick up after the next major version of the SDK lands. However, as we've seen, it requires careful consideration. I'm therefore closing this PR, since it is not a direction we'll take and we can't merge a breaking change at this time. Again, thank you for opening the PR! |



Stop star-exporting the full
@sentry/browsersurface from@sentry/reactso unused optional features are not forced into client bundles.Problem
Apps that both statically import from
@sentry/reactand dynamically load it (common for deferring tracing after hydration) can end up shipping the entire browser barrel on the critical path.With Rolldown (Vite 8), destructuring a dynamic import namespace is treated as using every export:
Because
@sentry/reactdidexport * from '@sentry/browser', that pulled Session Replay (~265KB), Feedback, AI instrumenters, feature-flag integrations, and unused React Router/Redux integrations into the main client chunk even when the app never referenced them.Measured on a production-like Rolldown graph (static init + idle dynamic import):
@sentry/replay/ rrwebSolution
@sentry/browser/core— slim entry for error monitoring, default integrations, and common tracing/filtering APIs (no Replay, Feedback, AI, feature flags, profiling, …).@sentry/reactroot — curated re-exports from@sentry/browser/coreonly (noexport *from the full browser package).@sentry/react/tanstackrouter@sentry/react/redux@sentry/react/reactrouter,reactrouterv3–v7,reactrouter.compat@sentry/react/optional-browser-api(Replay, Feedback, AI, …)@sentry/nextjs/@sentry/gatsbyre-exportoptional-browser-apisoimport * as Sentry from '@sentry/nextjs'keeps the historical surface.Migration
Optional / router APIs are no longer available from the
@sentry/reactroot:Recommended pattern for deferred tracing without reintroducing the fat namespace:
Test plan
packages/reactexport-surface unit test (exports.treeshake.test.ts)yarn build:dev:filter @sentry/reactsucceedsNote
Medium Risk
Public API break for anyone importing optional/router symbols from
@sentry/reactroot; framework re-exports limit impact for Next/Gatsby/Remix users but direct React apps need migration.Overview
Makes
@sentry/reacttree-shakeable by default so dynamicimport('@sentry/react')(especially with Rolldown/Vite 8 namespace destructuring) no longer drags Replay, Feedback, AI instrumenters, and router/Redux code into the main chunk.The root entry stops
export * from '@sentry/browser'and instead re-exports a curated surface from new@sentry/browser/core. Optional browser APIs move to@sentry/react/optional-browser-api; React Router, Redux, and TanStack helpers move to package subpaths (/tanstackrouter,/redux,/reactrouterv6, etc.) with separate Rollup entrypoints andpackage.jsonexports.Framework SDKs (
@sentry/nextjs,@sentry/gatsby,@sentry/remix,@sentry/tanstackstart-react) re-exportoptional-browser-api(and Redux/TanStack where needed) soimport * as Sentry from '@sentry/…'keeps the old symbols for those packages.Breaking for direct
@sentry/reactusers:replayIntegration, router integrations,createReduxEnhancer, etc. must be imported from@sentry/browser, subpaths, or dedicated packages. CHANGELOG, README, e2e fixtures, andexports.treeshake.test.tsdocument and enforce the new surface.Reviewed by Cursor Bugbot for commit fd30d7d. Bugbot is set up for automated code reviews on this repo. Configure here.