Automatic Instrumentation
Learn what transactions are captured after tracing is enabled in the Sentry React Native SDK.
@sentry/react-native provides automatic performance instrumentation out of the box when tracing is enabled.
To make the most out of our automatic instrumentation, you should:
Wrap your root component with Sentry to access the most Performance features.
App.jsexport default Sentry.wrap(App);
export default Sentry.wrap(App);
When no routing instrumentation is used, a transaction for App Start is automatically captured. However, that transaction stops being sent when one of the routing integrations below is added. Instead, the App Start information is included as a span in a transaction captured by the routing instrumentation.
We currently provide three routing instrumentations out of the box to instrument route changes for:
- Custom Navigation to add the custom navigation library integration
- Custom Instrumentation to add custom performance data to your application
Sentry offers the following automatic instrumentation features.
The App Start Instrumentation provides insight into how long your application takes to launch. It tracks the length of time from the earliest native process initialization until the React Native root component mounts.
If you don't wrap your root component with Sentry, the App Start measurement will finish when the JavaScript code is initialized instead of when the first component mount.
The SDK differentiates between a cold and a warm start, but doesn't track hot starts or resumes. The measurements are available under measurements.app_start_warm and measurements.app_start_cold.
Cold and warm start are Mobile Vitals, which you can learn about in the full documentation.
Sentry's App Start instrumentation aims to be as comprehensive and representative of the user experience as possible, and adheres to guidelines by the platform vendors. For this reason, App Starts reported by Sentry might be longer than what you see in other tools. Read more on the Apple App Start docs and Android App Start docs.
This feature is experimental and available since version 8.17.2.
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 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:
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
_experiments: {
enableStandaloneAppStartTracing: true,
},
});
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) 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:
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;
},
});
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;
},
});
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). 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.
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
_experiments: {
enableStandaloneAppStartTracing: true,
},
});
// Call extendAppStart() synchronously, right after init.
Sentry.extendAppStart();
// 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();
}
prepareApp();
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
_experiments: {
enableStandaloneAppStartTracing: true,
},
});
// Call extendAppStart() synchronously, right after init.
Sentry.extendAppStart();
// 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();
}
prepareApp();
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.
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.
Slow and frozen frames are Mobile Vitals, which you can learn about in the full documentation.
React Native mobile apps will not report Web Vitals. These values depend on APIs provided by browsers, and are not available in this context.
Sentry uses the androidx.core library for detecting slow and frozen frames. This is necessary to produce accurate results across all Android OS versions.
We check for availability at runtime, so if you're not using androidx.core, you can remove it from Sentry's transitive dependencies.
api ('io.sentry:sentry-android:8.56.0') {
exclude group: 'androidx.core', module: 'core'
}
api ('io.sentry:sentry-android:8.56.0') {
exclude group: 'androidx.core', module: 'core'
}
Note that if you remove this transitive dependency, slow and frozen frames won't be reported.
A stall is when the JavaScript event loop takes longer than expected to complete. A stall in your JavaScript code will not just make your UI unresponsive, but also slow down the logic that is contained within JavaScript. This slows everything down, creating a bad experience for your users.
We track stalls that occur in your React Native app during a transaction and provide you with these values:
- Longest Stall Time: The time, in milliseconds, of the longest event loop stall.
- Total Stall Time: The total combined time, in milliseconds, of all stalls.
- Stall Count: The total number of stalls that occurred during the transaction.
The tracing integration creates a child span for every XMLHttpRequest or fetch request on the JavaScript layer that occurs while those transactions are open. Learn more about traces, transactions, and spans.
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.
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
integrations: [Sentry.reactNativeTracingIntegration()],
});
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
integrations: [Sentry.reactNativeTracingIntegration()],
});
The default value of tracePropagationTargets is [/.*/] for mobile and ['localhost', /^\//] for web. The React Native SDK will attach the sentry-trace header to all outgoing XHR/fetch requests on mobile. On web, trace data is only attached to outgoing requests that contain localhost in their URL or requests whose URL starts with a '/' (for example GET /api/v1/users).
beforeStartSpan is called at the start of every pageload or navigation span, and is passed an object containing data about the span which will be started. With beforeStartSpan you can modify that data.
Sentry.init({
dsn: "___PUBLIC_DSN___",
integrations: [
Sentry.reactNativeTracingIntegration({
beforeStartSpan: (context) => {
return {
...context,
attributes: {
...context.attributes,
custom: "value",
},
};
},
}),
],
});
Sentry.init({
dsn: "___PUBLIC_DSN___",
integrations: [
Sentry.reactNativeTracingIntegration({
beforeStartSpan: (context) => {
return {
...context,
attributes: {
...context.attributes,
custom: "value",
},
};
},
}),
],
});
This function can be used to filter out unwanted spans such as XHRs running health checks or something similar. If this function isn't specified, spans will be created for all requests.
Sentry.init({
// ...
integrations: [
Sentry.reactNativeTracingIntegration({
shouldCreateSpanForRequest: (url) => {
// Do not create spans for outgoing requests to a `/health/` endpoint
return !url.match(/\/health\/?$/);
},
}),
],
});
Sentry.init({
// ...
integrations: [
Sentry.reactNativeTracingIntegration({
shouldCreateSpanForRequest: (url) => {
// Do not create spans for outgoing requests to a `/health/` endpoint
return !url.match(/\/health\/?$/);
},
}),
],
});
The amount of idle time, measured in ms, you have to wait for the transaction to finish if there are no unfinished spans. The transaction will use the end timestamp of the last finished span as the endtime for the transaction.
The default is 1_000.
The maximum duration of the transaction, measured in ms. If the transaction duration hits the finalTimeout value, it will be done.
The default is 60_0000.
Currently, by default, the React Native SDK will only create child spans for fetch/XHR transactions out of the box. This means once you are done setting up your routing instrumentation, you will either see just a few fetch/XHR child spans or no children at all. To find out how to customize instrumentation your app, review our Custom Instrumentation.
We export the React Profiler from our React Native SDK as well. Learn more in React Component Tracking.
After you instrument your app's routing, if you wrap a component that renders on one of the routes with withProfiler, you will be able to track the component's lifecycle as a child span of the route transaction.
import * as Sentry from "@sentry/react-native";
// withProfiler HOC
const SomeComponent = () => {
// ...
};
export default Sentry.withProfiler(SomeComponent);
import * as Sentry from "@sentry/react-native";
// withProfiler HOC
const SomeComponent = () => {
// ...
};
export default Sentry.withProfiler(SomeComponent);
// Profiler parent
const SomeComponent = () => {
return (
<Sentry.Profiler name="SomeChild">
<SomeChild />
</Sentry.Profiler>
);
};
// useProfiler hook
const SomeComponent = () => {
Sentry.useProfiler("SomeComponent");
return (
//...
)
}
When bundling for production, React Native will minify class and function names to reduce the bundle size. This means that you won't get the full original component names in your Profiler spans and instead you will see minified names. Check out our troubleshooting guide for minified production bundles documentation to solve this.
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:
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
enableAutoPerformanceTracing: false,
});
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "___PUBLIC_DSN___",
enableAutoPerformanceTracing: false,
});
The UI instrumentation captures transactions and adds breadcrumbs for touch interactions. Gesture support using React Native Gesture Handler is also available with the sentryTraceGesture wrapper. Learn more about User Interaction Instrumentation.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").