React Router
Learn about Sentry's React Router integration.
(Available in version 5.21.0 and above)
React Router support is included in the @sentry/react package since version 5.21.0.
Note
The React Router integration is designed to work with Sentry Tracing. Please see Set Up Tracing with React for more details on how to set up and install the SDK.
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 required React hooks and router functions:
useEffecthook fromreactuseLocationanduseNavigationTypehooks fromreact-router-domcreateRoutesFromChildrenandmatchRoutesfunctions fromreact-router-domorreact-routerpackages, depending on which package you're using.
Make sure you call Sentry.init, before you wrap your <Routes /> component or the useRoutes hook. Otherwise, the routing instrumentation may not work properly.
(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:
import React from "react";
import {
createBrowserRouter,
createRoutesFromChildren,
matchRoutes,
useLocation,
useNavigationType,
} 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([
// your routes...
]);
Note
You can instrument createMemoryRouter and createHashRouter using the wrapCreateBrowserRouter function.
When using react-router, errors thrown inside route elements will only be re-thrown in development mode while using strict mode. In production, these errors won't be surfaced unless manually captured. If you don't have a custom error boundary in place, react-router will create a default one that "swallows" all errors.
Note, that this only applies to render method and lifecycle errors since React doesn't need error boundaries to handle errors in event handlers.
To send errors to Sentry while using a custom error boundary, use the Sentry.captureException method:
// router setup
const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createBrowserRouter);
const router = sentryCreateBrowserRouter([
{
path: "/",
element: <YourLayout />,
children: [
{
path: "",
element: <Outlet />,
errorElement: <YourCustomRootErrorBoundary />,
children: [
// other routes ...
],
},
],
},
]);
// error boundary
import { useRouteError } from "react-router-dom";
import * as Sentry from "@sentry/react";
export function YourCustomRootErrorBoundary() {
const error = useRouteError() as Error;
React.useEffect(() => {
Sentry.captureException(error);
}, [error]);
return (
<div>
<h1>Ouch!</h1>
</div>
);
}
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:
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.
wrapUseRoutes should be called outside of a React component, as in the example below. It's also recommended that you assign the wrapped hook to a variable name starting with use, as per the React documentation.
import React from "react";
import {
createRoutesFromChildren,
matchRoutes,
useLocation,
useNavigationType,
useRoutes,
} from "react-router-dom";
import { wrapUseRoutes } 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 useSentryRoutes = wrapUseRoutes(useRoutes);
function App() {
return useSentryRoutes([
// your routes...
]);
}
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.
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.
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.
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,
});
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").