Summary
Several opt-in features shipped in the past year leave no distinctive fingerprint on events, so we have no way to measure adoption in our telemetry. Extend the marker-integration pattern already used by feedback/lazy.ts to cover them.
Background
The RN SDK already tracks feedback adoption by registering empty named integrations (MobileFeedback, AutoInjectMobileFeedback, etc.) whose names flow through to event.sdk.integrations via @sentry/core's prepareEvent (Hex table: sdk_integration_array).
Confirmed via telemetry:
sdk_features_array is empty for sentry.javascript.react-native% — @sentry/core has no setSdkFeature/addSdkFeature API and doesn't populate event.sdk.features. Not a viable channel today.
sdk_integration_array is the working channel; e.g., AppStart currently surfaces there (~9,983 projects).
Features to add markers for
| Marker name |
Feature |
Registration mechanism |
CaptureAppStartErrors |
Native pre-JS init via sentry.options.json (iOS RNSentrySDK.start, Android equivalent) — 8.0.0 |
Native records the flag on successful bootstrap; JS reads via the bridge at init and calls client.addIntegration({ name: 'CaptureAppStartErrors' }) |
StandaloneAppStart |
enableStandaloneAppStartTracing — 8.17.0 |
Register at Sentry.init when option is set |
ExtendedAppStart |
extendAppStart / finishExtendedAppStart / getExtendedAppStartSpan — Unreleased |
Register on first call |
MobileReplayNetworkDetails |
mobileReplayIntegration({ networkDetailAllowUrls }) — 8.15.0 |
Register at replay integration setup when networkDetailAllowUrls is non-empty |
MobileReplayNetworkBodies |
mobileReplayIntegration({ networkCaptureBodies: true }) — 8.15.0 |
Register at replay integration setup when option is true |
GlobalErrorBoundary |
Sentry.GlobalErrorBoundary / withGlobalErrorBoundary — 8.9.1 |
Lazy — register on component mount |
ExpoRouterErrorBoundary |
Sentry.wrapExpoRouterErrorBoundary manual wrap — 8.16.0 |
Lazy — register on first wrap call |
AutoInjectExpoRouterErrorBoundary |
Babel transform via autoWrapExpoRouterErrorBoundary: true — 8.17.0 |
Register from the wrap runtime when invoked from the Babel-injected call site (distinguish from manual wrap) |
NavigationContainer |
Sentry.NavigationContainer drop-in — 8.13.0 |
Lazy — register on component mount |
AppLoaded |
Sentry.appLoaded() — 8.7.0 |
Register on first call |
Two flavours of registration:
- Init-time (config-flag driven):
CaptureAppStartErrors, StandaloneAppStart, MobileReplayNetworkDetails, MobileReplayNetworkBodies — check the option at client / integration setup and register if set. Cluster in client.ts or the relevant integration file.
- Lazy on first use:
ExtendedAppStart, GlobalErrorBoundary, ExpoRouterErrorBoundary, AutoInjectExpoRouterErrorBoundary, NavigationContainer, AppLoaded — register on component mount / first API call, guarded by getClient()?.getIntegrationByName(...) for idempotency.
Precedents in the codebase:
- Lazy component-mount markers:
packages/core/src/js/feedback/lazy.ts
Why markers, not real integrations
For the component/API features the opt-in surface is the component/API itself; requiring users to also add integrations: [...] in Sentry.init would be a double opt-in and hurt DX for no functional benefit. CaptureAppStartErrors runs before JS exists, so a JS integration can't gate it; a bridged marker is the only honest signal. AutoInjectExpoRouterErrorBoundary is configured in Metro/Expo config, not Sentry.init, and doesn't map to a JS integration either. The two replay sub-options are gated by fields on the existing mobileReplayIntegration — a separate integration would be a double opt-in.
Notes on partial existing signals
StandaloneAppStart emits a top-level app.start transaction, distinguishable in the performance dataset — but projects on errors-only or with low tracing sample rates are invisible. The marker gives cross-event adoption.
MobileReplayNetworkDetails / MobileReplayNetworkBodies — the parent MobileReplay integration is already tracked, but the sub-options (headers vs. bodies) aren't distinguishable without the markers. Two markers give a clean funnel: replay → network details → bodies.
Acceptance criteria
Follow-up (out of scope)
Lower-value API-only opt-ins to consider in a second pass: pauseAppHangTracking/resumeAppHangTracking, onNativeLog, enableAutoConsoleLogs, top-level Sentry.setAttribute/setAttributes, beforeErrorSampling for mobileReplayIntegration, sentry-span-attributes prop.
Summary
Several opt-in features shipped in the past year leave no distinctive fingerprint on events, so we have no way to measure adoption in our telemetry. Extend the marker-integration pattern already used by
feedback/lazy.tsto cover them.Background
The RN SDK already tracks feedback adoption by registering empty named integrations (
MobileFeedback,AutoInjectMobileFeedback, etc.) whose names flow through toevent.sdk.integrationsvia@sentry/core'sprepareEvent(Hex table:sdk_integration_array).Confirmed via telemetry:
sdk_features_arrayis empty forsentry.javascript.react-native%—@sentry/corehas nosetSdkFeature/addSdkFeatureAPI and doesn't populateevent.sdk.features. Not a viable channel today.sdk_integration_arrayis the working channel; e.g.,AppStartcurrently surfaces there (~9,983 projects).Features to add markers for
CaptureAppStartErrorssentry.options.json(iOSRNSentrySDK.start, Android equivalent) — 8.0.0client.addIntegration({ name: 'CaptureAppStartErrors' })StandaloneAppStartenableStandaloneAppStartTracing— 8.17.0Sentry.initwhen option is setExtendedAppStartextendAppStart/finishExtendedAppStart/getExtendedAppStartSpan— UnreleasedMobileReplayNetworkDetailsmobileReplayIntegration({ networkDetailAllowUrls })— 8.15.0networkDetailAllowUrlsis non-emptyMobileReplayNetworkBodiesmobileReplayIntegration({ networkCaptureBodies: true })— 8.15.0GlobalErrorBoundarySentry.GlobalErrorBoundary/withGlobalErrorBoundary— 8.9.1ExpoRouterErrorBoundarySentry.wrapExpoRouterErrorBoundarymanual wrap — 8.16.0AutoInjectExpoRouterErrorBoundaryautoWrapExpoRouterErrorBoundary: true— 8.17.0NavigationContainerSentry.NavigationContainerdrop-in — 8.13.0AppLoadedSentry.appLoaded()— 8.7.0Two flavours of registration:
CaptureAppStartErrors,StandaloneAppStart,MobileReplayNetworkDetails,MobileReplayNetworkBodies— check the option at client / integration setup and register if set. Cluster inclient.tsor the relevant integration file.ExtendedAppStart,GlobalErrorBoundary,ExpoRouterErrorBoundary,AutoInjectExpoRouterErrorBoundary,NavigationContainer,AppLoaded— register on component mount / first API call, guarded bygetClient()?.getIntegrationByName(...)for idempotency.Precedents in the codebase:
packages/core/src/js/feedback/lazy.tsWhy markers, not real integrations
For the component/API features the opt-in surface is the component/API itself; requiring users to also add
integrations: [...]inSentry.initwould be a double opt-in and hurt DX for no functional benefit.CaptureAppStartErrorsruns before JS exists, so a JS integration can't gate it; a bridged marker is the only honest signal.AutoInjectExpoRouterErrorBoundaryis configured in Metro/Expo config, notSentry.init, and doesn't map to a JS integration either. The two replay sub-options are gated by fields on the existingmobileReplayIntegration— a separate integration would be a double opt-in.Notes on partial existing signals
StandaloneAppStartemits a top-levelapp.starttransaction, distinguishable in the performance dataset — but projects on errors-only or with low tracing sample rates are invisible. The marker gives cross-event adoption.MobileReplayNetworkDetails/MobileReplayNetworkBodies— the parentMobileReplayintegration is already tracked, but the sub-options (headers vs. bodies) aren't distinguishable without the markers. Two markers give a clean funnel: replay → network details → bodies.Acceptance criteria
@sentry/core/@sentry/reactCaptureAppStartErrorsAutoInjectExpoRouterErrorBoundarydistinguishable from manualExpoRouterErrorBoundary(same split precedent asMobileFeedbackvs.AutoInjectMobileFeedback)Follow-up (out of scope)
Lower-value API-only opt-ins to consider in a second pass:
pauseAppHangTracking/resumeAppHangTracking,onNativeLog,enableAutoConsoleLogs, top-levelSentry.setAttribute/setAttributes,beforeErrorSamplingformobileReplayIntegration,sentry-span-attributesprop.