From 57531f0ff145db2bfeee66f93975c8650b5f0047 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 11:49:23 +0200 Subject: [PATCH 1/4] feat(react-native): Document standalone app start tracing and extend app start API Adds a "Standalone App Start Tracing" section covering the experimental `enableStandaloneAppStartTracing` option, the dedicated `app.start` transaction, app-start sample rate via `tracesSampler`, and the `extendAppStart` / `getExtendedAppStartSpan` / `finishExtendedAppStart` extend API. --- .../automatic-instrumentation.mdx | 111 +++++++++++++++++- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx index 868331843c25c..2683dfddd2268 100644 --- a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx +++ b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx @@ -61,6 +61,113 @@ Sentry's App Start instrumentation aims to be as comprehensive and representativ +### Standalone App Start Tracing + + + +This feature is experimental and available since version `8.17.0`. + + + +By default, the SDK attaches app start data to your first navigation transaction, or to an `App Start` transaction with the `ui.load` operation when no [routing instrumentation](#enable-routing-instrumentation) is used. If no qualifying transaction is created, the app start data can be lost. + +With standalone app start tracing enabled, the SDK sends a dedicated `app.start` transaction instead. App starts get their own transaction — decoupled from navigation — which makes them easier to find, sample, and analyze independently, and lets the SDK capture app starts even when no screen transaction is created. + +To enable standalone app start tracing: + +```javascript +import * as Sentry from "@sentry/react-native"; + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + _experiments: { + enableStandaloneAppStartTracing: true, + }, +}); +``` + +The standalone transaction is named `App Start` and uses the `app.start` operation. The app start duration and type (`cold` or `warm`) are carried as `app.vitals.start` attributes on the transaction, and it includes the same breakdown spans as the attached version (JavaScript bundle execution and native initialization). + + + +Initialize the SDK as early as possible (and [wrap your root component](#wrap-your-root-component)) so the standalone app start transaction can be created and bounded to the app start window. This feature relies on the native app start data, so it is not available on the web. + + + +Standalone app start transactions are named `App Start`, so you can use a custom `tracesSampler` to set a dedicated sample rate for app starts without changing your overall sample rate: + +```javascript +import * as Sentry from "@sentry/react-native"; + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + _experiments: { + enableStandaloneAppStartTracing: true, + }, + tracesSampler: (samplingContext) => { + if (samplingContext.name === "App Start") { + return 1.0; + } + return 0.1; + }, +}); +``` + +{/* TODO: confirm the release version once the extend app start PR (#6392) ships. */} + +#### Extending the App Launch + + + +Available since version `8.18.0`. + + + +By default, the standalone app start transaction ends when your root component mounts (or when the JavaScript bundle finishes loading if you don't [wrap your root component](#wrap-your-root-component)). If your app performs additional work after that — such as loading remote config, restoring a session, or keeping the splash screen visible — you can extend the app start transaction to include that time by calling `Sentry.extendAppStart()`. + +Call `extendAppStart()` right after `Sentry.init()` and before the app start transaction is created (before your root component mounts), so the SDK doesn't automatically finish it. Use `getExtendedAppStartSpan()` to retrieve the extended app start span and add child spans that break down the extended launch period with `Sentry.startInactiveSpan({ parentSpan })`. Call `Sentry.finishExtendedAppStart()` when your app is fully ready. This adds an `Extended App Start` child span covering the time between the two calls. + +```javascript +import * as Sentry from "@sentry/react-native"; + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + _experiments: { + enableStandaloneAppStartTracing: true, + }, +}); + +Sentry.extendAppStart(); + +// Break the extended launch period down into child spans: +const parentSpan = Sentry.getExtendedAppStartSpan(); + +const configSpan = Sentry.startInactiveSpan({ + parentSpan, + op: "app.init", + name: "fetch remote config", +}); +await fetchRemoteConfig(); +configSpan.end(); + +// Later, when your app is fully ready: +await Sentry.finishExtendedAppStart(); +``` + + + +`extendAppStart()` must be called after `Sentry.init()` and before the app start transaction is created. If called after the transaction was already created, the SDK logs a warning and ignores the call. Calling it before `Sentry.init()` is also ignored. + +`getExtendedAppStartSpan()` returns the extended app start span, or a no-op span if `extendAppStart()` wasn't called, the SDK isn't started, or the app start transaction was already created. + +`finishExtendedAppStart()` is a no-op if there is no active extension. It returns a promise you can `await` before `Sentry.flush()` (for example, before a code push or Expo update) to make sure the app start transaction is queued. + +If `finishExtendedAppStart()` is never called, the extended app start automatically finishes after 30 seconds. In that case the transaction is still sent, but without an `app.vitals.start` measurement, so a hanging launch never reports a bogus ~30s app start. + +These APIs require `enableStandaloneAppStartTracing` to be enabled. + + + ### Slow and Frozen Frames Unresponsive UI and animation hitches annoy users and degrade the user experience. Two measurements to track these types of experiences are slow frames and frozen frames. If you want your app to run smoothly, you should try to avoid both. The SDK adds these two measurements for the transactions you capture. @@ -105,7 +212,6 @@ The tracing integration creates a child span for every `XMLHttpRequest` or `fetc To configure the automatic performance instrumentation, you will need to add the `ReactNativeTracing` integration yourself. We provide many options by default, so for the majority of apps you won't need to configure the integration yourself. - ```javascript import * as Sentry from "@sentry/react-native"; @@ -145,7 +251,7 @@ Sentry.init({ ...context, attributes: { ...context.attributes, - "custom": "value", + custom: "value", }, }; }, @@ -237,7 +343,6 @@ When bundling for production, React Native will minify class and function names If you want to use tracing without our automatic instrumentation, you can disable it by setting `enableAutoPerformanceTracing` in your Sentry options and removing the `ReactNativeTracing` integration, if you added it: - ```javascript import * as Sentry from "@sentry/react-native"; From f77f75fb5e0c4088d6cd9de13ffb37eaf663bfb9 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 12:58:17 +0200 Subject: [PATCH 2/4] Bump to the patch release --- .../tracing/instrumentation/automatic-instrumentation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx index 2683dfddd2268..5e3285e60535a 100644 --- a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx +++ b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx @@ -65,7 +65,7 @@ Sentry's App Start instrumentation aims to be as comprehensive and representativ -This feature is experimental and available since version `8.17.0`. +This feature is experimental and available since version `8.17.1`. From 6a05c1c9272fb47a59882ed148ff1738b9d0c029 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 13:14:04 +0200 Subject: [PATCH 3/4] Restore unrelated formatting reverted by prettier Keeps the PR diff limited to the new Standalone App Start Tracing section (prettier had reformatted pre-existing code samples in the same file). --- .../tracing/instrumentation/automatic-instrumentation.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx index 5e3285e60535a..d021608eb10a5 100644 --- a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx +++ b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx @@ -212,6 +212,7 @@ The tracing integration creates a child span for every `XMLHttpRequest` or `fetc To configure the automatic performance instrumentation, you will need to add the `ReactNativeTracing` integration yourself. We provide many options by default, so for the majority of apps you won't need to configure the integration yourself. + ```javascript import * as Sentry from "@sentry/react-native"; @@ -251,7 +252,7 @@ Sentry.init({ ...context, attributes: { ...context.attributes, - custom: "value", + "custom": "value", }, }; }, @@ -343,6 +344,7 @@ When bundling for production, React Native will minify class and function names If you want to use tracing without our automatic instrumentation, you can disable it by setting `enableAutoPerformanceTracing` in your Sentry options and removing the `ReactNativeTracing` integration, if you added it: + ```javascript import * as Sentry from "@sentry/react-native"; From 9cf8eb2294da0a536e8560f339d9bc6b3fa36ac9 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 13:16:06 +0200 Subject: [PATCH 4/4] Fix extend app start example: avoid top-level await (Metro) Metro doesn't support top-level await; wrap the async launch work in an async function so the example is copy-pasteable into an app entry file. --- .../automatic-instrumentation.mdx | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx index d021608eb10a5..996cfb829645d 100644 --- a/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx +++ b/docs/platforms/react-native/common/tracing/instrumentation/automatic-instrumentation.mdx @@ -137,21 +137,27 @@ Sentry.init({ }, }); +// Call extendAppStart() synchronously, right after init. Sentry.extendAppStart(); -// Break the extended launch period down into child spans: -const parentSpan = Sentry.getExtendedAppStartSpan(); - -const configSpan = Sentry.startInactiveSpan({ - parentSpan, - op: "app.init", - name: "fetch remote config", -}); -await fetchRemoteConfig(); -configSpan.end(); +// Metro doesn't support top-level await, so run the async launch work in a function. +async function prepareApp() { + // Break the extended launch period down into child spans: + const parentSpan = Sentry.getExtendedAppStartSpan(); + + const configSpan = Sentry.startInactiveSpan({ + parentSpan, + op: "app.init", + name: "fetch remote config", + }); + await fetchRemoteConfig(); + configSpan.end(); + + // When your app is fully ready: + await Sentry.finishExtendedAppStart(); +} -// Later, when your app is fully ready: -await Sentry.finishExtendedAppStart(); +prepareApp(); ```