Automatic Instrumentation
Learn what spans are captured after tracing is enabled.
Capturing spans requires that you first set up tracing in your app if you haven't already.
The Sentry SDK provides a BrowserTracing integration to add automatic instrumentation for monitoring the performance of browser applications.
Once you enable tracing, the SDK automatically captures performance data without additional code:
| What | Description | Metrics |
|---|---|---|
| Page loads | Full page load performance | LCP, CLS, TTFB |
| Navigations | Client-side route changes | Duration, Web Vitals |
| HTTP requests | All fetch/XHR calls | Duration, status, URL |
| User interactions | Clicks, inputs that trigger work | INP (responsiveness) |
| Long tasks | Main thread blocking > 50ms | Duration, attribution |
The BrowserTracing integration creates a new transaction for each page load and navigation event, and creates a child span for every XMLHttpRequest or fetch request that occurs while those transactions are open. Learn more about traces, transactions, and spans.
To enable tracing, include browserTracingIntegration in your SDK configuration options.
After configuration, you will see both pageload and navigation transactions in the Sentry UI.
// If you're using one of our framework SDK packages, like `@sentry/react`,
// substitute its name for `@sentry/browser` here
import * as Sentry from "@sentry/browser";
Sentry.init({
dsn: "___PUBLIC_DSN___",
integrations: [Sentry.browserTracingIntegration()],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});
By default, the browserTracingIntegration() will create a pageload span for when the page is initially loaded, as well as a navigation span for whenever the URL changes afterwards.
To make sure that spans are created correctly for a custom routing setup, you'll need to opt out of the default span creation by setting instrumentNavigation: false and instrumentPageLoad: false in the browserTracingIntegration() options. You can then manually create spans like this:
const client = Sentry.init({
integrations: [
Sentry.browserTracingIntegration({
// disable automatic span creation
instrumentNavigation: false,
instrumentPageLoad: false,
}),
],
});
// We start the pageload span as early as possible!
let pageLoadSpan = Sentry.startBrowserTracingPageLoadSpan(client, {
name: window.location.pathname,
attributes: {
[Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url",
},
});
// Somewhere, instrument your router like this:
myRouter.on("routeChange", (route) => {
// Make sure that the pageload span uses the route name
// After that, each route change should trigger a navigation span (which will automatically finish the previous one)
if (pageLoadSpan) {
pageLoadSpan.updateName(route.name);
pageLoadSpan.setAttribute(
Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
"route",
);
pageLoadSpan = undefined;
} else {
Sentry.startBrowserTracingNavigationSpan(client, {
op: "navigation",
name: route.name, // or what the name of the span should be
attributes: {
[Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
},
});
}
});
Most apps only need these options:
- Distributed Tracing Targets — Connect frontend and backend spans
- Customize Span Names — Normalize URLs or add context
- Filter Out Unwanted Requests — Exclude health checks, analytics
tracePropagationTargets
| Type | Array<string | RegExp> |
|---|---|
| Default | ['localhost', /^\/$/] |
Controls which outgoing requests include tracing headers (sentry-trace and baggage). Required for connecting frontend spans to backend spans.
By default, tracing headers are only attached to requests containing localhost or starting with /. Add your API domains to trace requests across services:
For example:
- A frontend application is served from
example.com. - A backend service is served from
api.example.com. - During development, the backend service is served from
localhost. - The frontend application makes API calls to the backend.
- Set the
tracePropagationTargetsoption to["localhost", /^https:\/\/api\.example\.com/]. - Now outgoing XHR/fetch requests to your backend service will get the
sentry-traceandbaggageheaders attached.
Sentry.init({
// ...
integrations: [Sentry.browserTracingIntegration()],
// Set `tracePropagationTargets` to control for which URLs trace propagation should be enabled
tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/],
});
Your server must allow these headers via CORS: Access-Control-Allow-Headers: sentry-trace, baggage
beforeStartSpan
| Type | (options: StartSpanOptions) => StartSpanOptions |
|---|
Modify span data before it's captured. Useful for adding context or normalizing URLs with dynamic segments:
One common use case is parameterizing transaction names. For both pageload and navigation transactions, the browserTracingIntegration uses the browser's window.location value to generate a transaction name. Using beforeStartSpan lets you modify the transaction name to make it more generic, so that for example, transactions named GET /users/12312012 and GET /users/11212012 can both be renamed to GET /users/:userid. That way they'll be grouped together.
Sentry.init({
// ...
integrations: [
Sentry.browserTracingIntegration({
beforeStartSpan: (context) => {
return {
...context,
// You could use your UI's routing library to find the matching
// route template here. We don't have one right now, so do some basic
// parameter replacements.
name: location.pathname
.replace(/\/[a-f0-9]{32}/g, "/<hash>")
.replace(/\/\d+/g, "/<digits>"),
};
},
}),
],
});
shouldCreateSpanForRequest
| Type | (url: string) => boolean |
|---|
Exclude requests from tracing, such as health checks or analytics pings:
Sentry.init({
// ...
integrations: [
Sentry.browserTracingIntegration({
shouldCreateSpanForRequest: (url) => {
// Do not create spans for outgoing requests to a `/health/` endpoint
return !url.match(/\/health\/?$/);
},
}),
],
});
enableInp
| Available since | 7.104.0 |
|---|---|
| Type | boolean |
| Default | true (See note) |
Automatically captures INP events to measure responsiveness. Results appear in the Web Vitals module.
Default: true in SDK 8.x+, false in 7.x.
INP replaces FID
As of SDK version 10.0.0, First Input Delay (FID) is no longer reported. Google deprecated FID in favor of INP, which provides a more comprehensive measure of responsiveness. If you have alerts or dashboards based on FID, update them to use INP instead.
Sentry.init({
// ...
integrations: [
Sentry.browserTracingIntegration({
enableInp: true,
}),
],
});
interactionsSampleRate
| Type | number |
|---|---|
| Default | 1.0 |
Sample rate for INP spans, applied on top of tracesSampleRate. For example, interactionsSampleRate: 0.5 with tracesSampleRate: 0.1 results in 5% of interactions captured.
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").