React Router

React Router (v7) is a framework for building full-stack web apps and websites. Learn how to set it up with Sentry.

Sentry captures data by using an SDK within your application’s runtime.

Copied
npm install @sentry/react @sentry/node @sentry/profiling-node

React Router exposes two hooks in your app folder (entry.client.tsx and entry.server.tsx). If you do not see these two files, expose them with the following command:

Copied
npx react-router reveal

Initialize the Sentry React SDK in your entry.client.tsx file:

entry.client.tsx
Copied
import * as Sentry from "@sentry/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { HydratedRouter } from "react-router/dom";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.replayIntegration(),
  ],

  tracesSampleRate: 1.0, //  Capture 100% of the transactions

  // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled
  tracePropagationTargets: [/^\//, /^https:\/\/yourserver\.io\/api/],

  // Capture Replay for 10% of all sessions,
  // plus 100% of sessions with an error
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
});

startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
      <HydratedRouter />
    </StrictMode>
  );
});

Now, update your app/root.tsx file to report any unhandled errors from your error boundary:

app/root.tsx
Copied
import * as Sentry from "@sentry/react";

export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
  let message = "Oops!";
  let details = "An unexpected error occurred.";
  let stack: string | undefined;

  if (isRouteErrorResponse(error)) {
    message = error.status === 404 ? "404" : "Error";
    details =
      error.status === 404
        ? "The requested page could not be found."
        : error.statusText || details;
  } else if (error && error instanceof Error) {
    // you only want to capture non 404-errors that reach the boundary
+   Sentry.captureException(error);
    if (import.meta.env.DEV) {
      details = error.message;
      stack = error.stack;
    }
  }

  return (
    <main>
      <h1>{message}</h1>
      <p>{details}</p>
      {stack && (
        <pre>
          <code>{stack}</code>
        </pre>
      )}
    </main>
  );
}
// ...

Create an instrument.server.mjs file in the root of your app:

instrument.server.mjs
Copied
import * as Sentry from "@sentry/node";
import { nodeProfilingIntegration } from '@sentry/profiling-node';

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [nodeProfilingIntegration()],
  tracesSampleRate: 1.0, // Capture 100% of the transactions
  profilesSampleRate: 1.0, // profile every transaction
});

In your entry.server.tsx file, export the handleError function:

entry.server.tsx
Copied
import * as Sentry from "@sentry/node";
import { type HandleErrorFunction } from "react-router";

export const handleError: HandleErrorFunction = (error, { request }) => {
  // React Router may abort some interrupted requests, report those
  if (!request.signal.aborted) {
+   Sentry.captureException(error);

    // make sure to still log the error so you can see it
    console.error(error);
  }
};
// ... rest of your server entry

Since React Router is running in ESM mode, you need to use the --import command line options to load our server-side instrumentation module before the application starts. Update the start and dev script to include the instrumentation file:

package.json
Copied
"scripts": {
  "dev": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev",
  "start": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js",
}

By default, React Router will minify your JavaScript and CSS files in production. This makes it difficult to debug errors. To make debugging easier, you can generate source maps and upload them to Sentry.

We recommend using Sentry's Vite plugin to upload sourcemaps.

Please refer to the Source Maps Documentation, for more information.

For more advanced configuration, you can use sentry-cli directly to upload source maps.

This snippet includes an intentional error, so you can test that everything is working as soon as you set it up.

Throw an error in a loader to verify that Sentry is working. After opening this route in your browser, you should see two errors in the Sentry issue stream, one captured from the server and one captured from the client.

error.tsx
Copied
import type { Route } from "./+types/example-page";

export async function loader() {
  throw new Error("some error thrown in a loader");
}

export default function ExamplePage() {
  return <div>Loading this page will throw an error</div>;
}

Learn more about manually capturing an error or message in our Usage documentation.

To view and resolve the recorded error, log into sentry.io and select your project. Clicking on the error's title will open a page where you can see detailed information and mark it as resolved.

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").