React Router

(Available in version 5.21.0 and above)

React Router support is included in the @sentry/react package since version 5.21.0.

The React Router instrumentation uses the React Router library to create pageload/navigation transactions to ensure you collect meaningful performance data about the health of your page loads and associated requests.

We support integrations for React Router 3, 4, 5, and 6.

(Available in version 7 and above)

To use React Router v6 with Sentry:

Add Sentry.reactRouterV6BrowserTracingIntegration instead of the regular Sentry.browserTracingIntegration and provide the functions it needs to enable performance tracing:

  • useEffect hook from react
  • useLocation and useNavigationType hooks from react-router-dom
  • createRoutesFromChildren and matchRoutes functions from react-router-dom or react-router packages.

(Available in version 7.21.0 and above)

If you choose to create your router instance with createBrowserRouter from the react-router-dom package, you can use Sentry.wrapCreateBrowserRouter to wrap it with the instrumentation:

Copied
import { createBrowserRouter } from "react-router-dom";
import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV6BrowserTracingIntegration({
      useEffect: React.useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 1.0,
});

const sentryCreateBrowserRouter =
  Sentry.wrapCreateBrowserRouter(createBrowserRouter);

const router = sentryCreateBrowserRouter([
  // ...
]);

If you're using the <Routes /> component from react-router-dom to define your routes, wrap Routes using Sentry.withSentryReactRouterV6Routing. This creates a higher order component, which will enable Sentry to reach your router context. You can also use Sentry.withSentryReactRouterV6Routing for Routes inside BrowserRouter. MemoryRouter, and HashRouter components:

Copied
import React from "react";
import ReactDOM from "react-dom";
import {
  Routes,
  Route,
  BrowserRouter,
  useLocation,
  useNavigationType,
  createRoutesFromChildren,
  matchRoutes,
} from "react-router-dom";
import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV6BrowserTracingIntegration({
      useEffect: React.useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 1.0,
});

const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);

ReactDOM.render(
  <BrowserRouter>
    <SentryRoutes>
      <Route path="/" element={<div>Home</div>} />
    </SentryRoutes>
  </BrowserRouter>
);

This is only needed at the top level of your app, rather than how v4/v5 required wrapping every <Route/> you wanted parametrized.

(Available in version 7.12.1 and above)

If you specify your route definitions as an object to the useRoutes hook, use Sentry.wrapUseRoutes to create a patched useRoutes hook that instruments your routes with Sentry.

Copied
import {
  createRoutesFromChildren,
  matchRoutes,
  useLocation,
  useNavigationType,
  useRoutes,
} from "react-router-dom";
import { wrapUseRoutes } from "@sentry/react";
import { useEffect } from "react";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV6BrowserTracingIntegration({
      useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 1.0,
});

const useSentryRoutes = wrapUseRoutes(useRoutes);

function App() {
  return useSentryRoutes([
    // ...
  ]);
}

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

Now, Sentry should generate pageload/navigation transactions with parameterized transaction names (for example, /teams/:teamid/user/:userid), where applicable. This is only needed at the top level of your app, rather than how v4/v5 required wrapping every <Route/> you wanted parametrized.

Make sure you use a Router component combined with createBrowserHistory (or equivalent).

To get parameterized transaction names (for example, /teams/:teamid/user/:userid instead of /teams/123/user/345), you must explicitly set the routes you want parameterized. That's because there is no static route config that the SDK can use in React Router v4/v5.

We recommend you use the withSentryRouting higher order component to create a SentryRoute component that will update the match path on render.

Copied
import {Route, Router, Switch } from 'react-router-dom';
import { createBrowserHistory } from 'history';

import * as Sentry from '@sentry/react';

// Create Custom Sentry Route component
const SentryRoute = Sentry.withSentryRouting(Route);

const history = createBrowserHistory();

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV5BrowserTracingIntegration({ history }),
    // or
    Sentry.reactRouterV4BrowserTracingIntegration({ history }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

render() {
  return (
    <Router history={history}>
      <Switch>
        <SentryRoute path="/users/:userid" component={() => <div>UserId</div>} />
        <SentryRoute path="/users" component={() => <div>Users</div>} />
        <SentryRoute path="/" component={() => <div>Home</div>} />
      </Switch>
    </Router>
  );
}

If you don't want to wrap individual routes, you can still specify parameterized routes manually by passing an array of route config objects, per react-router-config, to the instrumentation function call. You'll also need to provide the matchPath function exported from the react-router-dom or react-router packages.

Copied
import { Route, Router, Switch, matchPath } from 'react-router-dom';
import { createBrowserHistory } from 'history';

import * as Sentry from '@sentry/react';

const history = createBrowserHistory();

// Array of Route Config Objects
// Make sure the order of the routes is correct. The longest url under the same parent should be placed first and in decreasing order.
const routes = [{ path: '/users/:userid' }, { path: '/users' }, { path: '/' }];

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV5BrowserTracingIntegration({
      history,
      routes,
      matchPath
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

// In your App render:
render() {
  return (
    <Router history={history}>
      <Switch>
         <Route path="/users/:userid" component={() => <div>UserId</div>} />
         <Route path="/users" component={() => <div>Users</div>} />
         <Route path="/" component={() => <div>Home</div>} />
      </Switch>
    </Router>
  );
}

To use the router integration, import and set a custom routing instrumentation and pass it the history, your routes and a match function. React Router v3 support is maintained for React Router >= 3.2.0 and < 4.0.0.

Copied
import * as Router from "react-router";

import * as Sentry from "@sentry/react";

// Routes looks like this:
const routes = (
  <Route path="/">
    <Route path="organizations/">
      <Route path=":orgid" component={() => <div>OrgId</div>} />
      <Route path=":orgid/v1/:teamid" component={() => <div>Team</div>} />
    </Route>
  </Route>
);

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV3BrowserTracingIntegration({
      history: Router.browserHistory,
      // Must be Plain Routes.
      routes: Router.createRoutes(routes),
      match: Router.match,
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

Help improve this content
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").