From 8f76c807ea50c2abb3e9f9f1ba5b2f62c639a3a2 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 17:22:16 -0400 Subject: [PATCH 1/8] ref(react, browser): make @sentry/react tree-shakeable by default 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. --- packages/browser/package.json | 21 ++- packages/browser/rollup.npm.config.mjs | 2 + packages/browser/src/core.ts | 56 ++++++ packages/gatsby/src/index.ts | 2 + packages/nextjs/src/client/index.ts | 4 + packages/react/README.md | 24 ++- packages/react/optional-browser-api.d.ts | 1 + packages/react/package.json | 85 ++++++++- packages/react/reactrouter.compat.d.ts | 1 + packages/react/reactrouter.d.ts | 1 + packages/react/reactrouterv3.d.ts | 1 + packages/react/reactrouterv6.d.ts | 1 + packages/react/reactrouterv7.d.ts | 1 + packages/react/redux.d.ts | 1 + packages/react/rollup.npm.config.mjs | 13 ++ packages/react/src/browser-api.ts | 162 ++++++++++++++++++ packages/react/src/index.ts | 57 +++--- packages/react/src/optional-browser-api.ts | 71 ++++++++ packages/react/src/tanstackrouter.ts | 17 ++ packages/react/tanstackrouter.d.ts | 1 + packages/react/test/exports.treeshake.test.ts | 50 ++++++ packages/react/test/reactrouterv4.test.tsx | 3 +- packages/react/test/reactrouterv5.test.tsx | 3 +- 23 files changed, 539 insertions(+), 39 deletions(-) create mode 100644 packages/browser/src/core.ts create mode 100644 packages/react/optional-browser-api.d.ts create mode 100644 packages/react/reactrouter.compat.d.ts create mode 100644 packages/react/reactrouter.d.ts create mode 100644 packages/react/reactrouterv3.d.ts create mode 100644 packages/react/reactrouterv6.d.ts create mode 100644 packages/react/reactrouterv7.d.ts create mode 100644 packages/react/redux.d.ts create mode 100644 packages/react/src/browser-api.ts create mode 100644 packages/react/src/optional-browser-api.ts create mode 100644 packages/react/tanstackrouter.d.ts create mode 100644 packages/react/test/exports.treeshake.test.ts diff --git a/packages/browser/package.json b/packages/browser/package.json index 18b3bad89dca..ebdd8328c5b1 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -32,12 +32,31 @@ "import": "./build/npm/esm/prod/index.js", "require": "./build/npm/cjs/prod/index.js" } + }, + "./core": { + "types": "./build/npm/types/core.d.ts", + "react-native": "./build/npm/esm/prod/core.js", + "development": { + "import": "./build/npm/esm/dev/core.js", + "require": "./build/npm/cjs/dev/core.js" + }, + "production": { + "import": "./build/npm/esm/prod/core.js", + "require": "./build/npm/cjs/prod/core.js" + }, + "default": { + "import": "./build/npm/esm/prod/core.js", + "require": "./build/npm/cjs/prod/core.js" + } } }, "typesVersions": { "<5.0": { "build/npm/types/index.d.ts": [ "build/npm/types-ts3.8/index.d.ts" + ], + "core": [ + "build/npm/types-ts3.8/core.d.ts" ] } }, @@ -69,7 +88,7 @@ "build:bundle:watch": "rollup -c rollup.bundle.config.mjs --watch", "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:tarball": "npm pack", - "circularDepCheck": "madge --circular src/index.ts", + "circularDepCheck": "madge --circular src/index.ts && madge --circular src/core.ts", "clean": "rimraf build coverage .rpt2_cache sentry-browser-*.tgz", "lint:fix": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --fix --type-aware", "lint": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --type-aware", diff --git a/packages/browser/rollup.npm.config.mjs b/packages/browser/rollup.npm.config.mjs index a9df26ae0bae..96403071be2a 100644 --- a/packages/browser/rollup.npm.config.mjs +++ b/packages/browser/rollup.npm.config.mjs @@ -4,6 +4,8 @@ export default makeNPMConfigVariants( makeBaseNPMConfig({ // packages with bundles have a different build directory structure hasBundles: true, + // `core` is a slim entry used by framework SDKs for tree-shakeable re-exports. + entrypoints: ['src/index.ts', 'src/core.ts'], packageSpecificConfig: { output: { // set exports to 'named' or 'auto' so that rollup doesn't warn diff --git a/packages/browser/src/core.ts b/packages/browser/src/core.ts new file mode 100644 index 000000000000..190fd72235b9 --- /dev/null +++ b/packages/browser/src/core.ts @@ -0,0 +1,56 @@ +/** + * Tree-shakeable core browser SDK surface (`@sentry/browser/core`). + * + * Includes error monitoring, default integrations, and the tracing APIs that + * almost every production app needs — but **not** optional heavy features + * (Session Replay, Feedback UI, AI instrumenters, feature-flag integrations, + * profiling, etc.). + * + * Framework SDKs re-export from this entry so that bundlers which materialize + * a full dynamic-import namespace (e.g. Rolldown when destructuring + * `await import('@sentry/react')`) cannot pull optional packages into the + * critical path. + */ +export * from './exports'; + +export { cultureContextIntegration } from './integrations/culturecontext'; +export { normalizeStringifyValue } from './normalizeStringifyValue'; + +// --- Tracing (commonly used; still tree-shakeable if unused) --- +export { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './tracing/request'; +export type { RequestInstrumentationOptions } from './tracing/request'; +export { + browserTracingIntegration, + isBotUserAgent, + startBrowserTracingNavigationSpan, + startBrowserTracingPageLoadSpan, +} from './tracing/browserTracingIntegration'; +export { reportPageLoaded } from './tracing/reportPageLoaded'; +export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; + +// --- Filtering / enrichment used by most production setups --- +export { + registerSpanErrorInstrumentation, + getActiveSpan, + getRootSpan, + startSpan, + startInactiveSpan, + startSpanManual, + withActiveSpan, + startNewTrace, + bindScopeToEmitter, + getSpanDescendants, + setMeasurement, + getSpanStatusFromHttpCode, + setHttpStatus, + makeMultiplexedTransport, + MULTIPLEXED_TRANSPORT_EXTRA_KEY, + moduleMetadataIntegration, + thirdPartyErrorFilterIntegration, + captureConsoleIntegration, + extraErrorDataIntegration, + rewriteFramesIntegration, + consoleLoggingIntegration, + logger, +} from '@sentry/core/browser'; +export type { Span } from '@sentry/core/browser'; diff --git a/packages/gatsby/src/index.ts b/packages/gatsby/src/index.ts index 2299b46b7d64..0dad2ca04d0e 100644 --- a/packages/gatsby/src/index.ts +++ b/packages/gatsby/src/index.ts @@ -2,5 +2,7 @@ // can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703 /* eslint-disable import/export */ export * from '@sentry/react'; +// Optional browser features are no longer star-exported through `@sentry/react`. +export * from '@sentry/react/optional-browser-api'; export { init } from './sdk'; diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index b7f9c482b816..3ed37d277f45 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -16,6 +16,10 @@ import { removeIsrSsgTraceMetaTags } from './routing/isrRoutingTracing'; import { applyTunnelRouteOption } from './tunnelRoute'; export * from '@sentry/react'; +// Optional browser features are no longer star-exported through `@sentry/react` +// (so the React entry tree-shakes under Rolldown dynamic imports). Re-export them +// here so `import * as Sentry from '@sentry/nextjs'` keeps the historical surface. +export * from '@sentry/react/optional-browser-api'; export * from '../common'; export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error'; diff --git a/packages/react/README.md b/packages/react/README.md index b8d9879aa231..29f1614bc8e8 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -12,8 +12,28 @@ ## General -This package is a wrapper around `@sentry/browser`, with added functionality related to React. All methods available in -`@sentry/browser` can be imported from `@sentry/react`. +This package is a wrapper around `@sentry/browser`, with added functionality related to React. + +The default `@sentry/react` entry is intentionally **tree-shakeable**: it re-exports core error monitoring and common +tracing APIs, but **not** optional heavy browser features (Session Replay, User Feedback UI, AI instrumenters, feature +flag integrations, …). Import those from `@sentry/browser` or the dedicated packages when you need them: + +```javascript +import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/browser'; +// or: import { replayIntegration } from '@sentry/replay'; + +Sentry.init({ + dsn: '__DSN__', + integrations: [replayIntegration()], +}); +``` + +Router-specific integrations are on subpaths so they can be code-split: + +```javascript +import { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; +``` To use this SDK, call `Sentry.init(options)` before you mount your React component. diff --git a/packages/react/optional-browser-api.d.ts b/packages/react/optional-browser-api.d.ts new file mode 100644 index 000000000000..30eebdb67d51 --- /dev/null +++ b/packages/react/optional-browser-api.d.ts @@ -0,0 +1 @@ +export * from './build/types/optional-browser-api'; diff --git a/packages/react/package.json b/packages/react/package.json index eed0a949a151..45dd4b3d6aa1 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -10,7 +10,8 @@ "node": ">=18" }, "files": [ - "/build" + "/build", + "/*.d.ts" ], "main": "build/cjs/index.js", "module": "build/esm/index.js", @@ -30,6 +31,86 @@ "types": "./build/types/index.d.ts", "default": "./build/cjs/index.js" } + }, + "./optional-browser-api": { + "import": { + "types": "./optional-browser-api.d.ts", + "default": "./build/esm/optional-browser-api.js" + }, + "require": { + "types": "./optional-browser-api.d.ts", + "default": "./build/cjs/optional-browser-api.js" + } + }, + "./tanstackrouter": { + "import": { + "types": "./tanstackrouter.d.ts", + "default": "./build/esm/tanstackrouter.js" + }, + "require": { + "types": "./tanstackrouter.d.ts", + "default": "./build/cjs/tanstackrouter.js" + } + }, + "./redux": { + "import": { + "types": "./redux.d.ts", + "default": "./build/esm/redux.js" + }, + "require": { + "types": "./redux.d.ts", + "default": "./build/cjs/redux.js" + } + }, + "./reactrouterv3": { + "import": { + "types": "./reactrouterv3.d.ts", + "default": "./build/esm/reactrouterv3.js" + }, + "require": { + "types": "./reactrouterv3.d.ts", + "default": "./build/cjs/reactrouterv3.js" + } + }, + "./reactrouter": { + "import": { + "types": "./reactrouter.d.ts", + "default": "./build/esm/reactrouter.js" + }, + "require": { + "types": "./reactrouter.d.ts", + "default": "./build/cjs/reactrouter.js" + } + }, + "./reactrouterv6": { + "import": { + "types": "./reactrouterv6.d.ts", + "default": "./build/esm/reactrouterv6.js" + }, + "require": { + "types": "./reactrouterv6.d.ts", + "default": "./build/cjs/reactrouterv6.js" + } + }, + "./reactrouterv7": { + "import": { + "types": "./reactrouterv7.d.ts", + "default": "./build/esm/reactrouterv7.js" + }, + "require": { + "types": "./reactrouterv7.d.ts", + "default": "./build/cjs/reactrouterv7.js" + } + }, + "./reactrouter.compat": { + "import": { + "types": "./reactrouter.compat.d.ts", + "default": "./build/esm/reactrouter.compat.js" + }, + "require": { + "types": "./reactrouter.compat.d.ts", + "default": "./build/cjs/reactrouter.compat.js" + } } }, "typesVersions": { @@ -82,7 +163,7 @@ "build:dev:watch": "yarn build:watch", "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:tarball": "npm pack", - "circularDepCheck": "madge --circular src/index.ts", + "circularDepCheck": "madge --circular src/index.ts && madge --circular src/tanstackrouter.ts && madge --circular src/redux.ts", "clean": "rimraf build coverage sentry-react-*.tgz", "lint:fix": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --fix --type-aware", "lint": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --type-aware", diff --git a/packages/react/reactrouter.compat.d.ts b/packages/react/reactrouter.compat.d.ts new file mode 100644 index 000000000000..ef0a756b14cc --- /dev/null +++ b/packages/react/reactrouter.compat.d.ts @@ -0,0 +1 @@ +export * from './build/types/reactrouter.compat'; diff --git a/packages/react/reactrouter.d.ts b/packages/react/reactrouter.d.ts new file mode 100644 index 000000000000..299f84a46ed4 --- /dev/null +++ b/packages/react/reactrouter.d.ts @@ -0,0 +1 @@ +export * from './build/types/reactrouter'; diff --git a/packages/react/reactrouterv3.d.ts b/packages/react/reactrouterv3.d.ts new file mode 100644 index 000000000000..517a29703bcd --- /dev/null +++ b/packages/react/reactrouterv3.d.ts @@ -0,0 +1 @@ +export * from './build/types/reactrouterv3'; diff --git a/packages/react/reactrouterv6.d.ts b/packages/react/reactrouterv6.d.ts new file mode 100644 index 000000000000..4613606f5a7b --- /dev/null +++ b/packages/react/reactrouterv6.d.ts @@ -0,0 +1 @@ +export * from './build/types/reactrouterv6'; diff --git a/packages/react/reactrouterv7.d.ts b/packages/react/reactrouterv7.d.ts new file mode 100644 index 000000000000..16e168e3bb19 --- /dev/null +++ b/packages/react/reactrouterv7.d.ts @@ -0,0 +1 @@ +export * from './build/types/reactrouterv7'; diff --git a/packages/react/redux.d.ts b/packages/react/redux.d.ts new file mode 100644 index 000000000000..fe5edaed00ff --- /dev/null +++ b/packages/react/redux.d.ts @@ -0,0 +1 @@ +export * from './build/types/redux'; diff --git a/packages/react/rollup.npm.config.mjs b/packages/react/rollup.npm.config.mjs index 66c3b16aba58..2bf245a95cb1 100644 --- a/packages/react/rollup.npm.config.mjs +++ b/packages/react/rollup.npm.config.mjs @@ -6,6 +6,19 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollu // https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html export default makeNPMConfigVariants( makeBaseNPMConfig({ + // Separate entrypoints for router/redux integrations so they can be imported + // via package subpaths without sitting on the main export list (tree-shaking). + entrypoints: [ + 'src/index.ts', + 'src/optional-browser-api.ts', + 'src/tanstackrouter.ts', + 'src/redux.ts', + 'src/reactrouterv3.ts', + 'src/reactrouter.tsx', + 'src/reactrouterv6.tsx', + 'src/reactrouterv7.tsx', + 'src/reactrouter.compat.tsx', + ], packageSpecificConfig: { external: ['react', 'react/jsx-runtime'], }, diff --git a/packages/react/src/browser-api.ts b/packages/react/src/browser-api.ts new file mode 100644 index 000000000000..f2866baa0466 --- /dev/null +++ b/packages/react/src/browser-api.ts @@ -0,0 +1,162 @@ +/** + * Browser APIs re-exported from `@sentry/react`. + * + * This is intentionally a **curated** surface rather than `export * from '@sentry/browser'`. + * + * Why: bundlers that treat a dynamic `import('@sentry/react')` namespace as using every + * export (Rolldown when the namespace is destructured) would otherwise pull optional + * `@sentry/browser` features into the critical path — Replay (~265KB), Feedback, AI + * instrumenters, feature-flag integrations, etc. — even when the app never references them. + * + * Optional features remain available from `@sentry/browser` (full entry), + * `@sentry/react/optional-browser-api`, or dedicated packages (`@sentry/replay`, …). + * + * Everything here is re-exported from `@sentry/browser/core` so this module never + * links the full browser barrel. + */ + +export type { + Breadcrumb, + BreadcrumbHint, + BrowserOptions, + CaptureContext, + Context, + Contexts, + ErrorEvent, + Event, + EventHint, + Exception, + ExclusiveEventHintOrCaptureContext, + Log, + LogSeverityLevel, + ReportDialogOptions, + RequestEventData, + RequestInstrumentationOptions, + SdkInfo, + Session, + SeverityLevel, + Span, + StackFrame, + Stacktrace, + Thread, + User, +} from '@sentry/browser/core'; + +export { + // Core client / scope APIs + addBreadcrumb, + addEventProcessor, + addIntegration, + BrowserClient, + captureEvent, + captureException, + captureFeedback, + captureMessage, + captureSession, + close, + continueTrace, + createTransport, + createUserFeedbackEnvelope, + endSession, + eventFromException, + eventFromMessage, + exceptionFromError, + flush, + forceLoad, + getClient, + getCurrentScope, + getDefaultIntegrations, + getGlobalScope, + getIsolationScope, + getTraceData, + isEnabled, + isInitialized, + lastEventId, + // eslint-disable-next-line typescript/no-deprecated + inboundFiltersIntegration, + eventFiltersIntegration, + functionToStringIntegration, + dedupeIntegration, + // Default browser integrations + breadcrumbsIntegration, + browserApiErrorsIntegration, + browserSessionIntegration, + cultureContextIntegration, + globalHandlersIntegration, + httpContextIntegration, + linkedErrorsIntegration, + // Transport / parsing + defaultStackLineParsers, + defaultStackParser, + chromeStackLineParser, + geckoStackLineParser, + opera10StackLineParser, + opera11StackLineParser, + winjsStackLineParser, + makeFetchTransport, + // Scope helpers + metrics, + onLoad, + parameterize, + Scope, + SDK_VERSION, + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, + setAttribute, + setAttributes, + setContext, + setConversationId, + setCurrentClient, + setExtra, + setExtras, + setTag, + setTags, + setUser, + showReportDialog, + spanToBaggageHeader, + spanToJSON, + spanToTraceHeader, + startSession, + suppressTracing, + updateSpanName, + withIsolationScope, + withScope, + withStreamedSpan, + WINDOW, + lazyLoadIntegration, + normalizeStringifyValue, + // Tracing + browserTracingIntegration, + defaultRequestInstrumentationOptions, + instrumentOutgoingRequests, + isBotUserAgent, + reportPageLoaded, + setActiveSpanInBrowser, + startBrowserTracingNavigationSpan, + startBrowserTracingPageLoadSpan, + bindScopeToEmitter, + getActiveSpan, + getRootSpan, + getSpanDescendants, + getSpanStatusFromHttpCode, + registerSpanErrorInstrumentation, + setHttpStatus, + setMeasurement, + startInactiveSpan, + startNewTrace, + startSpan, + startSpanManual, + withActiveSpan, + // Filtering / enrichment + thirdPartyErrorFilterIntegration, + moduleMetadataIntegration, + rewriteFramesIntegration, + captureConsoleIntegration, + extraErrorDataIntegration, + consoleLoggingIntegration, + logger, + makeMultiplexedTransport, + MULTIPLEXED_TRANSPORT_EXTRA_KEY, +} from '@sentry/browser/core'; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index a25f05e62a2f..7e2d9318709b 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,41 +1,34 @@ // import/export got a false positive, and affects most of our index barrel files // can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703 /* eslint-disable import/export */ -export * from '@sentry/browser'; + +/** + * @sentry/react public entry — tree-shakeable by default. + * + * Deliberately does **not** use `export * from '@sentry/browser'`. That star-export + * put every optional browser feature (Session Replay, Feedback UI, AI SDKs, feature + * flags, …) on this module's export list. Bundlers that materialize the full + * namespace for `import('@sentry/react')` (Rolldown when destructuring the result) + * then shipped hundreds of KB of unused code on the critical path. + * + * Optional browser features: import from `@sentry/browser` or the dedicated package. + * Router-specific integrations: import from a subpath, e.g. + * `@sentry/react/tanstackrouter`. + */ + +export * from './browser-api'; export { init } from './sdk'; export { captureReactException, reactErrorHandler } from './error'; export { Profiler, withProfiler, useProfiler } from './profiler'; export type { ErrorBoundaryProps, FallbackRender } from './errorboundary'; export { ErrorBoundary, withErrorBoundary } from './errorboundary'; -export { createReduxEnhancer } from './redux'; -export { reactRouterV3BrowserTracingIntegration } from './reactrouterv3'; -export { tanstackRouterBrowserTracingIntegration } from './tanstackrouter'; -export { - withSentryRouting, - reactRouterV4BrowserTracingIntegration, - reactRouterV5BrowserTracingIntegration, -} from './reactrouter'; -/* oxlint-disable typescript/no-deprecated -- Intentional re-exports for backwards compatibility */ -export { - reactRouterV6BrowserTracingIntegration, - withSentryReactRouterV6Routing, - wrapUseRoutesV6, - wrapCreateBrowserRouterV6, - wrapCreateMemoryRouterV6, -} from './reactrouterv6'; -export { - reactRouterV7BrowserTracingIntegration, - withSentryReactRouterV7Routing, - wrapCreateBrowserRouterV7, - wrapCreateMemoryRouterV7, - wrapUseRoutesV7, -} from './reactrouterv7'; -/* oxlint-enable typescript/no-deprecated */ -export { - reactRouterBrowserTracingIntegration, - wrapReactRouterRouting, - wrapCreateBrowserRouter, - wrapCreateMemoryRouter, - wrapUseRoutes, -} from './reactrouter.compat'; + +// Router / Redux integrations live on subpaths so they are not part of this +// entry's export list (see package.json "exports"). Re-exporting them here would +// re-introduce the tree-shaking hole for dynamic `import('@sentry/react')`. +// +// import { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; +// import { createReduxEnhancer } from '@sentry/react/redux'; +// import { reactRouterV6BrowserTracingIntegration } from '@sentry/react/reactrouterv6'; +// … diff --git a/packages/react/src/optional-browser-api.ts b/packages/react/src/optional-browser-api.ts new file mode 100644 index 000000000000..96810cb580f1 --- /dev/null +++ b/packages/react/src/optional-browser-api.ts @@ -0,0 +1,71 @@ +/** + * Optional `@sentry/browser` features that are **not** part of the default + * `@sentry/react` entry (for tree-shaking). + * + * Framework SDKs (`@sentry/nextjs`, `@sentry/gatsby`, …) re-export these so + * `import * as Sentry from '@sentry/nextjs'` keeps the historical surface. + * + * Application code may also import from here, or directly from `@sentry/browser` + * / dedicated packages (`@sentry/replay`, `@sentry/feedback`, …). + */ +export { + // Replay + getReplay, + replayCanvasIntegration, + replayIntegration, + // Feedback + feedbackAsyncIntegration, + feedbackIntegration, + feedbackSyncIntegration, + getFeedback, + sendFeedback, + // Optional integrations + browserProfilingIntegration, + contextLinesIntegration, + createConsolaReporter, + diagnoseSdkConnectivity, + elementTimingIntegration, + featureFlagsIntegration, + fetchStreamPerformanceIntegration, + graphqlClientIntegration, + httpClientIntegration, + makeBrowserOfflineTransport, + registerWebWorker, + reportingObserverIntegration, + spanStreamingIntegration, + spotlightBrowserIntegration, + supabaseIntegration, + instrumentSupabaseClient, + viewHierarchyIntegration, + webVitalsIntegration, + webWorkerIntegration, + zodErrorsIntegration, + // Feature flags + buildLaunchDarklyFlagUsedHandler, + growthbookIntegration, + launchDarklyIntegration, + openFeatureIntegration, + OpenFeatureIntegrationHook, + statsigIntegration, + unleashIntegration, + // AI instrumenters + createLangChainCallbackHandler, + instrumentAnthropicAiClient, + instrumentCreateReactAgent, + instrumentGoogleGenAIClient, + instrumentLangChainEmbeddings, + instrumentLangGraph, + instrumentOpenAiClient, +} from '@sentry/browser'; + +export type { + ReplayBreadcrumbFrame, + ReplayBreadcrumbFrameEvent, + ReplayEventType, + ReplayEventWithTime, + ReplayFrame, + ReplayFrameEvent, + ReplayOptionFrameEvent, + ReplaySpanFrame, + ReplaySpanFrameEvent, +} from '@sentry/browser'; diff --git a/packages/react/src/tanstackrouter.ts b/packages/react/src/tanstackrouter.ts index 42993182cd55..fffa8c6f90ed 100644 --- a/packages/react/src/tanstackrouter.ts +++ b/packages/react/src/tanstackrouter.ts @@ -25,8 +25,25 @@ interface TanstackRouterLocation { * * The minimum compatible version of `@tanstack/react-router` is `1.64.0`. * + * Import from `@sentry/react/tanstackrouter` (not the package root) so this code is not + * part of the main `@sentry/react` export list. That keeps dynamic + * `import('@sentry/react')` namespaces tree-shakeable. + * * @param router A TanStack Router `Router` instance that should be used for routing instrumentation. * @param options Sentry browser tracing configuration. + * + * @example + * ```ts + * import { init, addIntegration } from '@sentry/react'; + * import { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; + * + * init({ dsn: '...' }); + * + * // Safe to defer: only the tanstack subpath is loaded + * void import('@sentry/react/tanstackrouter').then(({ tanstackRouterBrowserTracingIntegration }) => { + * addIntegration(tanstackRouterBrowserTracingIntegration(router)); + * }); + * ``` */ export function tanstackRouterBrowserTracingIntegration( // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/react/tanstackrouter.d.ts b/packages/react/tanstackrouter.d.ts new file mode 100644 index 000000000000..fd91c8d1b102 --- /dev/null +++ b/packages/react/tanstackrouter.d.ts @@ -0,0 +1 @@ +export * from './build/types/tanstackrouter'; diff --git a/packages/react/test/exports.treeshake.test.ts b/packages/react/test/exports.treeshake.test.ts new file mode 100644 index 000000000000..f2ce092c3ef3 --- /dev/null +++ b/packages/react/test/exports.treeshake.test.ts @@ -0,0 +1,50 @@ +/** + * @vitest-environment node + * + * Guard the curated `@sentry/react` export surface. + * + * Optional heavy browser features must not appear on the package root so that + * bundlers which materialize the full module namespace for + * `import('@sentry/react')` (Rolldown + destructuring) cannot pull them into + * the critical path. + */ +import { describe, expect, it } from 'vitest'; +import * as SentryReact from '../src/index'; + +describe('@sentry/react export surface (tree-shaking)', () => { + it('exposes core error monitoring and tracing APIs', () => { + expect(typeof SentryReact.init).toBe('function'); + expect(typeof SentryReact.captureException).toBe('function'); + expect(typeof SentryReact.browserApiErrorsIntegration).toBe('function'); + expect(typeof SentryReact.breadcrumbsIntegration).toBe('function'); + expect(typeof SentryReact.browserTracingIntegration).toBe('function'); + expect(typeof SentryReact.thirdPartyErrorFilterIntegration).toBe('function'); + expect(typeof SentryReact.ErrorBoundary).toBe('function'); + expect(typeof SentryReact.reactErrorHandler).toBe('function'); + }); + + it('does not export optional heavy browser features from the root entry', () => { + const root = SentryReact as Record; + + // These live on `@sentry/browser` / dedicated packages / optional-browser-api + expect(root.replayIntegration).toBeUndefined(); + expect(root.getReplay).toBeUndefined(); + expect(root.feedbackIntegration).toBeUndefined(); + expect(root.getFeedback).toBeUndefined(); + expect(root.instrumentOpenAiClient).toBeUndefined(); + expect(root.instrumentAnthropicAiClient).toBeUndefined(); + expect(root.launchDarklyIntegration).toBeUndefined(); + expect(root.browserProfilingIntegration).toBeUndefined(); + expect(root.diagnoseSdkConnectivity).toBeUndefined(); + }); + + it('does not export router integrations from the root entry', () => { + const root = SentryReact as Record; + + // Import from `@sentry/react/tanstackrouter`, `/reactrouterv6`, etc. + expect(root.tanstackRouterBrowserTracingIntegration).toBeUndefined(); + expect(root.reactRouterV6BrowserTracingIntegration).toBeUndefined(); + expect(root.reactRouterV7BrowserTracingIntegration).toBeUndefined(); + expect(root.createReduxEnhancer).toBeUndefined(); + }); +}); diff --git a/packages/react/test/reactrouterv4.test.tsx b/packages/react/test/reactrouterv4.test.tsx index 779adfb31559..69f23c8c5f61 100644 --- a/packages/react/test/reactrouterv4.test.tsx +++ b/packages/react/test/reactrouterv4.test.tsx @@ -16,7 +16,8 @@ import { act } from 'react'; import { matchPath, Route, Router, Switch } from 'react-router-4'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { URL_TEMPLATE } from '@sentry/conventions/attributes'; -import { BrowserClient, reactRouterV4BrowserTracingIntegration, withSentryRouting } from '../src'; +import { BrowserClient } from '../src'; +import { reactRouterV4BrowserTracingIntegration, withSentryRouting } from '../src/reactrouter'; import type { RouteConfig } from '../src/reactrouter'; const mockStartBrowserTracingPageLoadSpan = vi.fn(); diff --git a/packages/react/test/reactrouterv5.test.tsx b/packages/react/test/reactrouterv5.test.tsx index 7841833fb6cc..7cfa8b41c88e 100644 --- a/packages/react/test/reactrouterv5.test.tsx +++ b/packages/react/test/reactrouterv5.test.tsx @@ -16,7 +16,8 @@ import { act } from 'react'; import { matchPath, Route, Router, Switch } from 'react-router-5'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { URL_TEMPLATE } from '@sentry/conventions/attributes'; -import { BrowserClient, reactRouterV5BrowserTracingIntegration, withSentryRouting } from '../src'; +import { BrowserClient } from '../src'; +import { reactRouterV5BrowserTracingIntegration, withSentryRouting } from '../src/reactrouter'; import type { RouteConfig } from '../src/reactrouter'; const mockStartBrowserTracingPageLoadSpan = vi.fn(); From 6aacba048cd7ccd3e7e4846140b4bfa7459c96f4 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 17:29:18 -0400 Subject: [PATCH 2/8] fix(react): restore uiProfiler on curated @sentry/react exports 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). --- packages/react/src/browser-api.ts | 4 ++++ packages/react/test/exports.treeshake.test.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/react/src/browser-api.ts b/packages/react/src/browser-api.ts index f2866baa0466..3921f58735c4 100644 --- a/packages/react/src/browser-api.ts +++ b/packages/react/src/browser-api.ts @@ -94,6 +94,10 @@ export { opera11StackLineParser, winjsStackLineParser, makeFetchTransport, + // Manual UI profiling API (pairs with browserProfilingIntegration from + // optional-browser-api / @sentry/browser). Was on the pre-curation public + // surface via export * from @sentry/browser. + uiProfiler, // Scope helpers metrics, onLoad, diff --git a/packages/react/test/exports.treeshake.test.ts b/packages/react/test/exports.treeshake.test.ts index f2ce092c3ef3..74f9b522770b 100644 --- a/packages/react/test/exports.treeshake.test.ts +++ b/packages/react/test/exports.treeshake.test.ts @@ -21,6 +21,10 @@ describe('@sentry/react export surface (tree-shaking)', () => { expect(typeof SentryReact.thirdPartyErrorFilterIntegration).toBe('function'); expect(typeof SentryReact.ErrorBoundary).toBe('function'); expect(typeof SentryReact.reactErrorHandler).toBe('function'); + // Public API previously re-exported via export * from @sentry/browser + expect(SentryReact.uiProfiler).toBeDefined(); + expect(typeof SentryReact.uiProfiler.startProfiler).toBe('function'); + expect(typeof SentryReact.uiProfiler.stopProfiler).toBe('function'); }); it('does not export optional heavy browser features from the root entry', () => { From d56b0e9f91ad9f0e03e516dc5f75e1ca2a7a1454 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 17:31:01 -0400 Subject: [PATCH 3/8] fix(react): restore uiProfiler on optional-browser-api and getAbsoluteUrl 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. --- packages/browser/src/core.ts | 1 + packages/react/src/browser-api.ts | 1 + packages/react/src/optional-browser-api.ts | 4 ++++ packages/react/test/exports.treeshake.test.ts | 7 +++++++ 4 files changed, 13 insertions(+) diff --git a/packages/browser/src/core.ts b/packages/browser/src/core.ts index 190fd72235b9..2c142cd1d6e7 100644 --- a/packages/browser/src/core.ts +++ b/packages/browser/src/core.ts @@ -15,6 +15,7 @@ export * from './exports'; export { cultureContextIntegration } from './integrations/culturecontext'; export { normalizeStringifyValue } from './normalizeStringifyValue'; +export { getAbsoluteUrl } from '@sentry/browser-utils'; // --- Tracing (commonly used; still tree-shakeable if unused) --- export { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './tracing/request'; diff --git a/packages/react/src/browser-api.ts b/packages/react/src/browser-api.ts index 3921f58735c4..610fa7a46f5a 100644 --- a/packages/react/src/browser-api.ts +++ b/packages/react/src/browser-api.ts @@ -98,6 +98,7 @@ export { // optional-browser-api / @sentry/browser). Was on the pre-curation public // surface via export * from @sentry/browser. uiProfiler, + getAbsoluteUrl, // Scope helpers metrics, onLoad, diff --git a/packages/react/src/optional-browser-api.ts b/packages/react/src/optional-browser-api.ts index 96810cb580f1..bca7f0b3b5a7 100644 --- a/packages/react/src/optional-browser-api.ts +++ b/packages/react/src/optional-browser-api.ts @@ -20,7 +20,11 @@ export { getFeedback, sendFeedback, // Optional integrations + // uiProfiler is also on the root surface; re-export here so + // `import { uiProfiler, browserProfilingIntegration } from '@sentry/react/optional-browser-api'` + // keeps working for manual UI profiling setups. browserProfilingIntegration, + uiProfiler, contextLinesIntegration, createConsolaReporter, diagnoseSdkConnectivity, diff --git a/packages/react/test/exports.treeshake.test.ts b/packages/react/test/exports.treeshake.test.ts index 74f9b522770b..2c43a5c81c1a 100644 --- a/packages/react/test/exports.treeshake.test.ts +++ b/packages/react/test/exports.treeshake.test.ts @@ -25,6 +25,7 @@ describe('@sentry/react export surface (tree-shaking)', () => { expect(SentryReact.uiProfiler).toBeDefined(); expect(typeof SentryReact.uiProfiler.startProfiler).toBe('function'); expect(typeof SentryReact.uiProfiler.stopProfiler).toBe('function'); + expect(typeof SentryReact.getAbsoluteUrl).toBe('function'); }); it('does not export optional heavy browser features from the root entry', () => { @@ -42,6 +43,12 @@ describe('@sentry/react export surface (tree-shaking)', () => { expect(root.diagnoseSdkConnectivity).toBeUndefined(); }); + it('re-exports uiProfiler from optional-browser-api with browserProfilingIntegration', async () => { + const optional = await import('../src/optional-browser-api'); + expect(optional.uiProfiler).toBeDefined(); + expect(typeof optional.browserProfilingIntegration).toBe('function'); + }); + it('does not export router integrations from the root entry', () => { const root = SentryReact as Record; From c618b01eb126cac1b6ad13032c7fc931d621a67a Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 17:57:57 -0400 Subject: [PATCH 4/8] fix(react, nextjs, gatsby): address PR review on tree-shakeable exports 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 --- CHANGELOG.md | 25 +++++++++++++++++++ packages/gatsby/src/index.ts | 5 +++- packages/nextjs/src/client/index.ts | 9 ++++--- packages/react/README.md | 13 ++++++++-- packages/react/src/optional-browser-api.ts | 1 + packages/react/test/exports.treeshake.test.ts | 4 +++ 6 files changed, 51 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff421e99bb3a..09bdf82ceb17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ ## Unreleased +### Important Changes + +- **ref(react, browser): make `@sentry/react` tree-shakeable by default ([#22168](https://github.com/getsentry/sentry-javascript/pull/22168))** + + The `@sentry/react` root entry no longer star-exports the full `@sentry/browser` surface. Optional/heavy features + (Session Replay, Feedback, AI instrumenters, feature-flag integrations, browser profiling, …) and React + Router/Redux helpers are off the root export list so bundlers that materialize a dynamic `import('@sentry/react')` + namespace (e.g. Rolldown when destructuring) do not pull them into the critical path. + + **Migration** — import optional browser APIs from `@sentry/browser`, `@sentry/react/optional-browser-api`, or a + dedicated package; import router/Redux helpers from package subpaths: + + ```ts + // Before + import { replayIntegration, tanstackRouterBrowserTracingIntegration, createReduxEnhancer } from '@sentry/react'; + + // After + import { replayIntegration } from '@sentry/browser'; // or @sentry/react/optional-browser-api / @sentry/replay + import { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; + import { createReduxEnhancer } from '@sentry/react/redux'; + ``` + + `@sentry/nextjs` and `@sentry/gatsby` re-export `optional-browser-api` and `createReduxEnhancer` so + `import * as Sentry from '@sentry/nextjs'` (and Gatsby) keeps the previous surface for those symbols. + - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott Work in this release was contributed by @martijnwalraven. Thank you for your contribution! diff --git a/packages/gatsby/src/index.ts b/packages/gatsby/src/index.ts index 0dad2ca04d0e..3f5680024fa8 100644 --- a/packages/gatsby/src/index.ts +++ b/packages/gatsby/src/index.ts @@ -2,7 +2,10 @@ // can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703 /* eslint-disable import/export */ export * from '@sentry/react'; -// Optional browser features are no longer star-exported through `@sentry/react`. +// Optional browser features and Redux are no longer star-exported through +// `@sentry/react`. Re-export them so `import * as Sentry from '@sentry/gatsby'` +// keeps the historical surface (including `createReduxEnhancer`). export * from '@sentry/react/optional-browser-api'; +export { createReduxEnhancer } from '@sentry/react/redux'; export { init } from './sdk'; diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 3ed37d277f45..f7723790cd5f 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -16,10 +16,13 @@ import { removeIsrSsgTraceMetaTags } from './routing/isrRoutingTracing'; import { applyTunnelRouteOption } from './tunnelRoute'; export * from '@sentry/react'; -// Optional browser features are no longer star-exported through `@sentry/react` -// (so the React entry tree-shakes under Rolldown dynamic imports). Re-export them -// here so `import * as Sentry from '@sentry/nextjs'` keeps the historical surface. +// Optional browser features and Redux are no longer star-exported through +// `@sentry/react` (so the React entry tree-shakes under Rolldown dynamic imports). +// Re-export them here so `import * as Sentry from '@sentry/nextjs'` keeps the +// historical surface (including `createReduxEnhancer`, which is dual-declared +// against the client SDK in `index.types.ts`). export * from '@sentry/react/optional-browser-api'; +export { createReduxEnhancer } from '@sentry/react/redux'; export * from '../common'; export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error'; diff --git a/packages/react/README.md b/packages/react/README.md index 29f1614bc8e8..4ea7b3eadb42 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -16,11 +16,12 @@ This package is a wrapper around `@sentry/browser`, with added functionality rel The default `@sentry/react` entry is intentionally **tree-shakeable**: it re-exports core error monitoring and common tracing APIs, but **not** optional heavy browser features (Session Replay, User Feedback UI, AI instrumenters, feature -flag integrations, …). Import those from `@sentry/browser` or the dedicated packages when you need them: +flag integrations, …) or router/Redux helpers. Import those from a dedicated entry when you need them: ```javascript import * as Sentry from '@sentry/react'; import { replayIntegration } from '@sentry/browser'; +// or: import { replayIntegration } from '@sentry/react/optional-browser-api'; // or: import { replayIntegration } from '@sentry/replay'; Sentry.init({ @@ -29,12 +30,20 @@ Sentry.init({ }); ``` -Router-specific integrations are on subpaths so they can be code-split: +Router and Redux integrations are on package subpaths so they can be code-split: ```javascript import { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; +import { createReduxEnhancer } from '@sentry/react/redux'; +import { reactRouterV6BrowserTracingIntegration } from '@sentry/react/reactrouterv6'; ``` +**Breaking change:** symbols that previously came from the `@sentry/react` root +(`replayIntegration`, `feedbackIntegration`, router helpers, `createReduxEnhancer`, …) must now be imported from +`@sentry/browser`, `@sentry/react/optional-browser-api`, or the matching subpath above. Framework SDKs +(`@sentry/nextjs`, `@sentry/gatsby`) still re-export the optional browser surface and `createReduxEnhancer` so +`import * as Sentry from '@sentry/nextjs'` keeps working for those symbols. + To use this SDK, call `Sentry.init(options)` before you mount your React component. ```javascript diff --git a/packages/react/src/optional-browser-api.ts b/packages/react/src/optional-browser-api.ts index bca7f0b3b5a7..82f41a0753d6 100644 --- a/packages/react/src/optional-browser-api.ts +++ b/packages/react/src/optional-browser-api.ts @@ -63,6 +63,7 @@ export { } from '@sentry/browser'; export type { + FeatureFlagsIntegration, ReplayBreadcrumbFrame, ReplayBreadcrumbFrameEvent, ReplayEventType, diff --git a/packages/react/test/exports.treeshake.test.ts b/packages/react/test/exports.treeshake.test.ts index 2c43a5c81c1a..9da465e8ae6d 100644 --- a/packages/react/test/exports.treeshake.test.ts +++ b/packages/react/test/exports.treeshake.test.ts @@ -47,6 +47,10 @@ describe('@sentry/react export surface (tree-shaking)', () => { const optional = await import('../src/optional-browser-api'); expect(optional.uiProfiler).toBeDefined(); expect(typeof optional.browserProfilingIntegration).toBe('function'); + expect(typeof optional.featureFlagsIntegration).toBe('function'); + // Type-only export is erased at runtime; the value companion is what consumers use + // with getIntegrationByName(...). + expect(optional).toHaveProperty('featureFlagsIntegration'); }); it('does not export router integrations from the root entry', () => { From cf92bd36f6eadb99d7db643f09792508ab75dc59 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 18:15:34 -0400 Subject: [PATCH 5/8] fix(remix, tanstackstart-react): re-export optional-browser-api for types 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 --- packages/remix/src/client/index.ts | 3 +++ packages/remix/src/cloudflare/index.ts | 3 +++ packages/tanstackstart-react/src/client/index.ts | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/packages/remix/src/client/index.ts b/packages/remix/src/client/index.ts index 65e6b9fbc184..57422935cf29 100644 --- a/packages/remix/src/client/index.ts +++ b/packages/remix/src/client/index.ts @@ -5,6 +5,9 @@ import { debug } from '@sentry/core'; import { DEBUG_BUILD } from '../utils/debug-build'; export * from '@sentry/react'; +// Optional browser features are no longer star-exported through `@sentry/react`. +// Re-export them so `import * as Sentry from '@sentry/remix'` keeps the historical surface. +export * from '@sentry/react/optional-browser-api'; export { init } from './sdk'; export { captureRemixErrorBoundaryError } from './errors'; diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts index ef2f89cee2a5..8a89771c7490 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -8,6 +8,9 @@ import { } from '../server/instrumentServer'; export * from '@sentry/react'; +// Optional browser features are no longer star-exported through `@sentry/react`. +// Re-export them so the cloudflare entry keeps the historical surface. +export * from '@sentry/react/optional-browser-api'; export { captureRemixErrorBoundaryError } from '../client/errors'; export { withSentry } from '../client/performance'; diff --git a/packages/tanstackstart-react/src/client/index.ts b/packages/tanstackstart-react/src/client/index.ts index 73c7e4cf7b24..93f45ed25a56 100644 --- a/packages/tanstackstart-react/src/client/index.ts +++ b/packages/tanstackstart-react/src/client/index.ts @@ -9,6 +9,12 @@ import type { import type { CreateSentryTunnelRouteOptions } from '../server/tunnelRoute'; export * from '@sentry/react'; +// Optional browser features and router helpers are no longer star-exported through +// `@sentry/react`. Re-export them so `import * as Sentry from '@sentry/tanstackstart-react'` +// keeps the historical surface (including `tanstackRouterBrowserTracingIntegration`, +// which is dual-declared against the client SDK in `index.types.ts`). +export * from '@sentry/react/optional-browser-api'; +export { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; export { init } from './sdk'; From 3fe022e01cd8063b03c2d4684d16cc273dd6f659 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 18:23:55 -0400 Subject: [PATCH 6/8] fix(react frameworks): keep uiProfiler and remix createReduxEnhancer 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 --- CHANGELOG.md | 5 +++-- packages/gatsby/src/index.ts | 3 +++ packages/nextjs/src/client/index.ts | 3 +++ packages/remix/src/client/index.ts | 9 +++++++-- packages/remix/src/cloudflare/index.ts | 8 ++++++-- packages/tanstackstart-react/src/client/index.ts | 3 +++ 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09bdf82ceb17..c8d963537e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,8 +24,9 @@ import { createReduxEnhancer } from '@sentry/react/redux'; ``` - `@sentry/nextjs` and `@sentry/gatsby` re-export `optional-browser-api` and `createReduxEnhancer` so - `import * as Sentry from '@sentry/nextjs'` (and Gatsby) keeps the previous surface for those symbols. + Framework SDKs (`@sentry/nextjs`, `@sentry/gatsby`, `@sentry/remix`, `@sentry/tanstackstart-react`) + re-export `optional-browser-api` (and `createReduxEnhancer` on nextjs/gatsby/remix) so + `import * as Sentry from '@sentry/…'` keeps the previous surface for those symbols. - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott diff --git a/packages/gatsby/src/index.ts b/packages/gatsby/src/index.ts index 3f5680024fa8..f2f0503a9ac1 100644 --- a/packages/gatsby/src/index.ts +++ b/packages/gatsby/src/index.ts @@ -6,6 +6,9 @@ export * from '@sentry/react'; // `@sentry/react`. Re-export them so `import * as Sentry from '@sentry/gatsby'` // keeps the historical surface (including `createReduxEnhancer`). export * from '@sentry/react/optional-browser-api'; +// `uiProfiler` is on both `@sentry/react` and `optional-browser-api`; dual +// `export *` would omit the name under ESM rules, so re-export it explicitly. +export { uiProfiler } from '@sentry/react'; export { createReduxEnhancer } from '@sentry/react/redux'; export { init } from './sdk'; diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index f7723790cd5f..410c11b7aa4e 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -22,6 +22,9 @@ export * from '@sentry/react'; // historical surface (including `createReduxEnhancer`, which is dual-declared // against the client SDK in `index.types.ts`). export * from '@sentry/react/optional-browser-api'; +// `uiProfiler` is on both `@sentry/react` and `optional-browser-api`; dual +// `export *` would omit the name under ESM rules, so re-export it explicitly. +export { uiProfiler } from '@sentry/react'; export { createReduxEnhancer } from '@sentry/react/redux'; export * from '../common'; export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error'; diff --git a/packages/remix/src/client/index.ts b/packages/remix/src/client/index.ts index 57422935cf29..e7e2f05e1d7f 100644 --- a/packages/remix/src/client/index.ts +++ b/packages/remix/src/client/index.ts @@ -5,9 +5,14 @@ import { debug } from '@sentry/core'; import { DEBUG_BUILD } from '../utils/debug-build'; export * from '@sentry/react'; -// Optional browser features are no longer star-exported through `@sentry/react`. -// Re-export them so `import * as Sentry from '@sentry/remix'` keeps the historical surface. +// Optional browser features and Redux are no longer star-exported through +// `@sentry/react`. Re-export them so `import * as Sentry from '@sentry/remix'` +// keeps the historical surface. export * from '@sentry/react/optional-browser-api'; +// `uiProfiler` is on both `@sentry/react` and `optional-browser-api`; dual +// `export *` would omit the name under ESM rules, so re-export it explicitly. +export { uiProfiler } from '@sentry/react'; +export { createReduxEnhancer } from '@sentry/react/redux'; export { init } from './sdk'; export { captureRemixErrorBoundaryError } from './errors'; diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts index 8a89771c7490..a2c14fb07151 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -8,9 +8,13 @@ import { } from '../server/instrumentServer'; export * from '@sentry/react'; -// Optional browser features are no longer star-exported through `@sentry/react`. -// Re-export them so the cloudflare entry keeps the historical surface. +// Optional browser features and Redux are no longer star-exported through +// `@sentry/react`. Re-export them so the cloudflare entry keeps the historical surface. export * from '@sentry/react/optional-browser-api'; +// `uiProfiler` is on both `@sentry/react` and `optional-browser-api`; dual +// `export *` would omit the name under ESM rules, so re-export it explicitly. +export { uiProfiler } from '@sentry/react'; +export { createReduxEnhancer } from '@sentry/react/redux'; export { captureRemixErrorBoundaryError } from '../client/errors'; export { withSentry } from '../client/performance'; diff --git a/packages/tanstackstart-react/src/client/index.ts b/packages/tanstackstart-react/src/client/index.ts index 93f45ed25a56..47cbe9a5347b 100644 --- a/packages/tanstackstart-react/src/client/index.ts +++ b/packages/tanstackstart-react/src/client/index.ts @@ -14,6 +14,9 @@ export * from '@sentry/react'; // keeps the historical surface (including `tanstackRouterBrowserTracingIntegration`, // which is dual-declared against the client SDK in `index.types.ts`). export * from '@sentry/react/optional-browser-api'; +// `uiProfiler` is on both `@sentry/react` and `optional-browser-api`; dual +// `export *` would omit the name under ESM rules, so re-export it explicitly. +export { uiProfiler } from '@sentry/react'; export { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; export { init } from './sdk'; From 365c3895c5a47cb48e40b08646ed3dda080df76c Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 20:09:03 -0400 Subject: [PATCH 7/8] test(e2e): update react apps for tree-shakeable @sentry/react exports 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 --- .../lighthouse-react/src/main.tsx | 3 ++- .../test-applications/react-17/src/index.tsx | 8 +++++--- .../react-create-browser-router/src/index.tsx | 8 +++++--- .../src/pages/LazyLoadedInnerRoute.tsx | 4 ++-- .../src/pages/LazyLoadedUser.tsx | 4 ++-- .../react-create-hash-router/src/index.tsx | 8 +++++--- .../react-create-memory-router/src/index.tsx | 8 +++++--- .../react-router-5/src/index.tsx | 8 +++++--- .../src/index.tsx | 8 +++++--- .../react-router-6-use-routes/src/index.tsx | 8 +++++--- .../react-router-6/src/index.tsx | 8 +++++--- .../react-router-7-cross-usage/src/index.tsx | 17 ++++++++++++----- .../react-router-7-lazy-routes/src/index.tsx | 5 +++-- .../react-router-7-spa-streaming/src/main.tsx | 8 +++++--- .../react-router-7-spa/src/main.tsx | 8 +++++--- .../react-router-8-cross-usage/src/index.tsx | 17 ++++++++++++----- .../react-router-8-spa/src/main.tsx | 8 +++++--- .../react-send-to-sentry/src/index.tsx | 8 +++++--- .../tanstack-router/src/main.tsx | 3 ++- 19 files changed, 95 insertions(+), 54 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/lighthouse-react/src/main.tsx b/dev-packages/e2e-tests/test-applications/lighthouse-react/src/main.tsx index 352c53c7208b..634ab96c4459 100644 --- a/dev-packages/e2e-tests/test-applications/lighthouse-react/src/main.tsx +++ b/dev-packages/e2e-tests/test-applications/lighthouse-react/src/main.tsx @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; import { createRoot } from 'react-dom/client'; import App from './App'; @@ -19,7 +20,7 @@ if (import.meta.env.MODE === 'tracing-replay') { dsn: import.meta.env.VITE_E2E_TEST_DSN as string | undefined, release: 'lighthouse-fixture', environment: 'qa', - integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + integrations: [Sentry.browserTracingIntegration(), replayIntegration()], tracesSampleRate: 1.0, replaysSessionSampleRate: 1.0, replaysOnErrorSampleRate: 1.0, diff --git a/dev-packages/e2e-tests/test-applications/react-17/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-17/src/index.tsx index ca6918c46106..11b72fbd7c57 100644 --- a/dev-packages/e2e-tests/test-applications/react-17/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-17/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, withSentryReactRouterV6Routing } from '@sentry/react/reactrouterv6'; import React from 'react'; import ReactDOM from 'react-dom'; import { @@ -13,13 +15,13 @@ import { import Index from './pages/Index'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -41,7 +43,7 @@ Sentry.init({ dataCollection: { userInfo: true }, }); -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +const SentryRoutes = withSentryReactRouterV6Routing(Routes); function App() { return ( diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx index c7ad16eebcf7..b5d78fd69af8 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, wrapCreateBrowserRouterV6 } from '@sentry/react/reactrouterv6'; import React, { lazy, Suspense } from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -12,13 +14,13 @@ import { import Index from './pages/Index'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ // environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -41,7 +43,7 @@ Sentry.init({ debug: !!process.env.DEBUG, }); -const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter); +const sentryCreateBrowserRouter = wrapCreateBrowserRouterV6(createBrowserRouter); const LazyLoadedUser = lazy(() => import('./pages/LazyLoadedUser')); const router = sentryCreateBrowserRouter( diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx index 915cb720974a..a9a433d30975 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx @@ -1,8 +1,8 @@ -import * as Sentry from '@sentry/react'; +import { withSentryReactRouterV6Routing } from '@sentry/react/reactrouterv6'; import * as React from 'react'; import { Route, Routes } from 'react-router-dom'; -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +const SentryRoutes = withSentryReactRouterV6Routing(Routes); const InnerRoute = () => ( diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx index 636f99d9c8cb..41c8ec6eb36a 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx @@ -1,8 +1,8 @@ -import * as Sentry from '@sentry/react'; +import { withSentryReactRouterV6Routing } from '@sentry/react/reactrouterv6'; import * as React from 'react'; import { Route, Routes } from 'react-router-dom'; -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +const SentryRoutes = withSentryReactRouterV6Routing(Routes); const InnerRoute = React.lazy(() => import('./LazyLoadedInnerRoute')); const LazyLoadedUser = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx index 86de5f20378d..a08a80ef17f5 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, wrapCreateBrowserRouterV6 } from '@sentry/react/reactrouterv6'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -13,13 +15,13 @@ import Index from './pages/Index'; import User from './pages/User'; import Group from './pages/Group'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ // environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -42,7 +44,7 @@ Sentry.init({ debug: !!process.env.DEBUG, }); -const sentryCreateHashRouter = Sentry.wrapCreateBrowserRouterV6(createHashRouter); +const sentryCreateHashRouter = wrapCreateBrowserRouterV6(createHashRouter); const router = sentryCreateHashRouter([ { diff --git a/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/index.tsx index f71572f9dc1f..5cdb770d4525 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, wrapCreateMemoryRouterV6 } from '@sentry/react/reactrouterv6'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -12,13 +14,13 @@ import { import Index from './pages/Index'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ // environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -41,7 +43,7 @@ Sentry.init({ debug: !!process.env.DEBUG, }); -const sentryCreateMemoryRouter = Sentry.wrapCreateMemoryRouterV6(createMemoryRouter); +const sentryCreateMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); const router = sentryCreateMemoryRouter( [ diff --git a/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx index cc9b307e620a..cfbfadd52d19 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV5BrowserTracingIntegration, withSentryRouting } from '@sentry/react/reactrouter'; import { createBrowserHistory } from 'history'; import React from 'react'; import ReactDOM from 'react-dom/client'; @@ -6,7 +8,7 @@ import { Route, Router, Switch } from 'react-router-dom'; import Index from './pages/Index'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); const history = createBrowserHistory(); @@ -15,7 +17,7 @@ Sentry.init({ dsn: process.env.REACT_APP_E2E_TEST_DSN || 'https://3b6c388182fb435097f41d181be2b2ba@o4504321058471936.ingest.sentry.io/4504321066008576', - integrations: [Sentry.reactRouterV5BrowserTracingIntegration({ history }), replay], + integrations: [reactRouterV5BrowserTracingIntegration({ history }), replay], // We recommend adjusting this value in production, or using tracesSampler // for finer control tracesSampleRate: 1.0, @@ -28,7 +30,7 @@ Sentry.init({ }); // Create Custom Sentry Route component -export const SentryRoute = Sentry.withSentryRouting(Route); +export const SentryRoute = withSentryRouting(Route); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/index.tsx index 581014169a78..2b994012c030 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, withSentryReactRouterV6Routing } from '@sentry/react/reactrouterv6'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -13,13 +15,13 @@ import { } from 'react-router-dom'; import Index from './pages/Index'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -41,7 +43,7 @@ Sentry.init({ tunnel: 'http://localhost:3031', }); -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +const SentryRoutes = withSentryReactRouterV6Routing(Routes); const DetailsRoutes = () => ( diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/index.tsx index 0ac33b9f6c5f..c4b9fef54d9e 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterBrowserTracingIntegration, wrapUseRoutes } from '@sentry/react/reactrouter.compat'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -12,13 +14,13 @@ import { import Index from './pages/Index'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterBrowserTracingIntegration({ + reactRouterBrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -39,7 +41,7 @@ Sentry.init({ tunnel: 'http://localhost:3031', // proxy server }); -const useSentryRoutes = Sentry.wrapUseRoutes(useRoutes); +const useSentryRoutes = wrapUseRoutes(useRoutes); function App() { return useSentryRoutes([ diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-6/src/index.tsx index 1430a44935b6..31009d222639 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, withSentryReactRouterV6Routing } from '@sentry/react/reactrouterv6'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -15,13 +17,13 @@ import Products from './pages/Products'; import SSE from './pages/SSE'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -44,7 +46,7 @@ Sentry.init({ dataCollection: { userInfo: true }, }); -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +const SentryRoutes = withSentryReactRouterV6Routing(Routes); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx index 5c381487587f..be2849dfac12 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx @@ -1,4 +1,11 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { + reactRouterBrowserTracingIntegration, + wrapReactRouterRouting, + wrapUseRoutes, + wrapCreateBrowserRouter, +} from '@sentry/react/reactrouter.compat'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -15,13 +22,13 @@ import { } from 'react-router-dom'; import Index from './pages/Index'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterBrowserTracingIntegration({ + reactRouterBrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -43,9 +50,9 @@ Sentry.init({ tunnel: 'http://localhost:3031', }); -const SentryRoutes = Sentry.wrapReactRouterRouting(Routes); -const sentryUseRoutes = Sentry.wrapUseRoutes(useRoutes); -const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createBrowserRouter); +const SentryRoutes = wrapReactRouterRouting(Routes); +const sentryUseRoutes = wrapUseRoutes(useRoutes); +const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createBrowserRouter); const DetailsRoutes = () => sentryUseRoutes([ diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/index.tsx index 4d47ddbf58bf..2dacb4c335ab 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/index.tsx @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/react'; +import { reactRouterV7BrowserTracingIntegration, wrapCreateBrowserRouterV7 } from '@sentry/react/reactrouterv7'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -81,7 +82,7 @@ Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV7BrowserTracingIntegration({ + reactRouterV7BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -102,7 +103,7 @@ Sentry.init({ tunnel: 'http://localhost:3031', }); -const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter); +const sentryCreateBrowserRouter = wrapCreateBrowserRouterV7(createBrowserRouter); const router = sentryCreateBrowserRouter( [ diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx index e55bd5f50bf3..1a70d1987d82 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV7BrowserTracingIntegration, withSentryReactRouterV7Routing } from '@sentry/react/reactrouterv7'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -14,13 +16,13 @@ import Index from './pages/Index'; import SSE from './pages/SSE'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV7BrowserTracingIntegration({ + reactRouterV7BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -43,7 +45,7 @@ Sentry.init({ dataCollection: { userInfo: true }, }); -const SentryRoutes = Sentry.withSentryReactRouterV7Routing(Routes); +const SentryRoutes = withSentryReactRouterV7Routing(Routes); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/main.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/main.tsx index 202de7be19b4..598ffc692d98 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/main.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/main.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterBrowserTracingIntegration, wrapReactRouterRouting } from '@sentry/react/reactrouter.compat'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -15,13 +17,13 @@ import Products from './pages/Products'; import SSE from './pages/SSE'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterBrowserTracingIntegration({ + reactRouterBrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -43,7 +45,7 @@ Sentry.init({ dataCollection: { userInfo: true }, }); -const SentryRoutes = Sentry.wrapReactRouterRouting(Routes); +const SentryRoutes = wrapReactRouterRouting(Routes); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-cross-usage/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-8-cross-usage/src/index.tsx index 46be7181b87c..c7888e90be73 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-cross-usage/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-8-cross-usage/src/index.tsx @@ -1,4 +1,11 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { + reactRouterBrowserTracingIntegration, + wrapReactRouterRouting, + wrapUseRoutes, + wrapCreateBrowserRouter, +} from '@sentry/react/reactrouter.compat'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -15,13 +22,13 @@ import { } from 'react-router'; import Index from './pages/Index'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterBrowserTracingIntegration({ + reactRouterBrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -43,9 +50,9 @@ Sentry.init({ tunnel: 'http://localhost:3031', }); -const SentryRoutes = Sentry.wrapReactRouterRouting(Routes); -const sentryUseRoutes = Sentry.wrapUseRoutes(useRoutes); -const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createBrowserRouter); +const SentryRoutes = wrapReactRouterRouting(Routes); +const sentryUseRoutes = wrapUseRoutes(useRoutes); +const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createBrowserRouter); const DetailsRoutes = () => sentryUseRoutes([ diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-spa/src/main.tsx b/dev-packages/e2e-tests/test-applications/react-router-8-spa/src/main.tsx index 202de7be19b4..598ffc692d98 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-spa/src/main.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-8-spa/src/main.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterBrowserTracingIntegration, wrapReactRouterRouting } from '@sentry/react/reactrouter.compat'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -15,13 +17,13 @@ import Products from './pages/Products'; import SSE from './pages/SSE'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterBrowserTracingIntegration({ + reactRouterBrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -43,7 +45,7 @@ Sentry.init({ dataCollection: { userInfo: true }, }); -const SentryRoutes = Sentry.wrapReactRouterRouting(Routes); +const SentryRoutes = wrapReactRouterRouting(Routes); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/index.tsx index 3a87a53ffdfa..609ca37cbaee 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/index.tsx @@ -1,4 +1,6 @@ import * as Sentry from '@sentry/react'; +import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { reactRouterV6BrowserTracingIntegration, withSentryReactRouterV6Routing } from '@sentry/react/reactrouterv6'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -13,13 +15,13 @@ import { import Index from './pages/Index'; import User from './pages/User'; -const replay = Sentry.replayIntegration(); +const replay = replayIntegration(); Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ + reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, @@ -59,7 +61,7 @@ Sentry.addEventProcessor(event => { return event; }); -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +const SentryRoutes = withSentryReactRouterV6Routing(Routes); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/dev-packages/e2e-tests/test-applications/tanstack-router/src/main.tsx b/dev-packages/e2e-tests/test-applications/tanstack-router/src/main.tsx index 3c2ed2905383..5088d3f91b6f 100644 --- a/dev-packages/e2e-tests/test-applications/tanstack-router/src/main.tsx +++ b/dev-packages/e2e-tests/test-applications/tanstack-router/src/main.tsx @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/react'; +import { tanstackRouterBrowserTracingIntegration } from '@sentry/react/tanstackrouter'; import { Link, Outlet, @@ -96,7 +97,7 @@ declare const __APP_DSN__: string; Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: __APP_DSN__, - integrations: [Sentry.tanstackRouterBrowserTracingIntegration(router)], + integrations: [tanstackRouterBrowserTracingIntegration(router)], // We recommend adjusting this value in production, or using tracesSampler // for finer control tracesSampleRate: 1.0, From fd30d7dc20f45891584e7026b90c0d34904a0a1a Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 9 Jul 2026 20:26:58 -0400 Subject: [PATCH 8/8] test(e2e): import spanStreamingIntegration from optional-browser-api react-router-7-spa-streaming still called Sentry.spanStreamingIntegration after optional APIs left the @sentry/react root. Co-Authored-By: Grok --- .../react-router-7-spa-streaming/src/main.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx index 1a70d1987d82..cf26f5c09797 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa-streaming/src/main.tsx @@ -1,5 +1,5 @@ import * as Sentry from '@sentry/react'; -import { replayIntegration } from '@sentry/react/optional-browser-api'; +import { replayIntegration, spanStreamingIntegration } from '@sentry/react/optional-browser-api'; import { reactRouterV7BrowserTracingIntegration, withSentryReactRouterV7Routing } from '@sentry/react/reactrouterv7'; import React from 'react'; import ReactDOM from 'react-dom/client'; @@ -30,7 +30,7 @@ Sentry.init({ matchRoutes, trackFetchStreamPerformance: true, }), - Sentry.spanStreamingIntegration(), + spanStreamingIntegration(), replay, ], // We recommend adjusting this value in production, or using tracesSampler