React Router v6

Learn how to instrument your React Router v6 application with Sentry.

Apply the following setup steps based on your routing method and create a custom error boundary to make sure Sentry automatically captures rendering errors:

    To instrument your React Router, update your Sentry.browserTracingIntegration to Sentry.reactRouterV6BrowserTracingIntegration within Sentry.init and provide the required React hooks and router functions. Then, wrap the router instance created by createBrowserRouter or createMemoryRouter with one of the following functions:

    Copied
    import React from "react";
    
    import { createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType, } from "react-router-dom";
    import * as Sentry from "@sentry/react"; Sentry.init({ dsn: "___PUBLIC_DSN___", integrations: [
    Sentry.reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes, }),
    ], tracesSampleRate: 1.0, });
    // Call this AFTER Sentry.init() const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6( createBrowserRouter, ); const router = sentryCreateBrowserRouter([ // your routes... ]);

    If you're using the <Routes /> component to define your routes, update your Sentry.browserTracingIntegration to Sentry.reactRouterV6BrowserTracingIntegration inside Sentry.init and provide the required React hooks and router functions. Then, 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: "___PUBLIC_DSN___", 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 wrapper is only needed at the top level of your app, unlike React Router v4/v5, which required wrapping every <Route /> you wanted parametrized.

    Available in @sentry/react version 7.12.1 and above.

    If you specify your route definitions as an object to the useRoutes hook, update your Sentry.browserTracingIntegration to Sentry.reactRouterV6BrowserTracingIntegration inside Sentry.init and provide the required React hooks and router functions. Then, use Sentry.wrapUseRoutesV6 to create a patched useRoutes hook that instruments your routes with Sentry.

    Copied
    import React from "react";
    
    import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType, useRoutes, } from "react-router-dom"; import { wrapUseRoutes } from "@sentry/react";
    Sentry.init({ dsn: "___PUBLIC_DSN___", integrations: [
    Sentry.reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes, }),
    ], tracesSampleRate: 1.0, });
    const useSentryRoutes = wrapUseRoutesV6(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, unlike React Router v4/v5, which required wrapping every <Route /> you wanted parametrized.

    Available since: v10.39.0

    When using patchRoutesOnNavigation to dynamically load route definitions, the full route hierarchy isn't available to Sentry until each route is navigated to. This can cause transactions to receive incomplete or wildcard names (for example, /users/* instead of /users/:userId).

    To ensure accurate transaction names, you can provide a static list of route patterns via the lazyRouteManifest option. When provided, Sentry uses this manifest as the primary source for determining transaction names without needing to wait for route modules to load.

    Make sure to keep the lazyRouteManifest array in sync with your route definitions: if you add, remove, or change routes in your app, update this list accordingly. Any route that doesn't match a pattern in the manifest will fall back to the default behavior, which may result in incomplete transaction names until the route is visited. You can also include non-lazy routes in the manifest for convenience or consistency.

    To use lazyRouteManifest, you need to set enableAsyncRouteHandlers: true in your reactRouterV6BrowserTracingIntegration configuration:

    Copied
    import React from "react";
    import {
      createRoutesFromChildren,
      matchRoutes,
      useLocation,
      useNavigationType,
    } from "react-router-dom";
    
    import * as Sentry from "@sentry/react";
    
    Sentry.init({
      dsn: "___PUBLIC_DSN___",
      integrations: [
        Sentry.reactRouterV6BrowserTracingIntegration({
          useEffect: React.useEffect,
          useLocation,
          useNavigationType,
          createRoutesFromChildren,
          matchRoutes,
    
    enableAsyncRouteHandlers: true, lazyRouteManifest: [ "/users", "/users/:userId", "/users/:userId/settings", "/dashboard", "/dashboard/analytics", ],
    }), ], tracesSampleRate: 1.0, });

    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 surface unless captured manually. If you don't have a custom error boundary in place, react-router will create a default one that "swallows" all errors.
    Hence, to capture these errors with Sentry in production, we strongly recommend to implement a custom error boundary.

    To send errors to Sentry while using a custom error boundary, use the Sentry.captureException method:

    Copied
    // router setup
    const sentryCreateBrowserRouter = wrapCreateBrowserRouterV6(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> ); }

    Was this helpful?
    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").