Skip to content

Files

react-router

Sentry

Official Sentry SDK for React Router Framework (EXPERIMENTAL)

npm version npm dm npm dt

Warning

This SDK is considered ⚠️ experimental and in an alpha state. It may experience breaking changes. Please reach out on GitHub if you have any feedback or concerns. This SDK is for React Router (framework). If you're using React Router (library) see our React SDK here.

Links

General

This package is a wrapper around @sentry/node for the server and @sentry/browser for the client side.

Manual Setup

Expose Hooks

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:

npx react-router reveal

Client-Side Setup

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

import * as Sentry from '@sentry/react-router';
import { startTransition, StrictMode } from 'react';
import { hydrateRoot } from 'react-dom/client';
import { HydratedRouter } from 'react-router/dom';

Sentry.init({
  dsn: '___PUBLIC_DSN___',
  integrations: [Sentry.browserTracingIntegration()],

  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/],
});

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

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

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

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>
  );
}
// ...

Server-Side Setup

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

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

Sentry.init({
  dsn: '___PUBLIC_DSN___',
  tracesSampleRate: 1.0, // Capture 100% of the transactions
});

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

import * as Sentry from '@sentry/react-router';
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

Update Scripts

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:

"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",
}

Build-time Config

Update your vite.config.ts file to include the sentryReactRouter plugin and also add your config options to the vite config (this is required for uploading sourcemaps at the end of the build):

import { reactRouter } from '@react-router/dev/vite';
import { sentryReactRouter } from '@sentry/react-router';
import { defineConfig } from 'vite';

const sentryConfig = {
  authToken: '...',
  org: '...',
  project: '...',
  // rest of your config
};

export default defineConfig(config => {
  return {
    plugins: [reactRouter(), sentryReactRouter(sentryConfig, config)],
    sentryConfig,
  };
});

Next, in your react-router.config.ts file, include the sentryOnBuildEnd hook:

import type { Config } from '@react-router/dev/config';
import { sentryOnBuildEnd } from '@sentry/react-router';

export default {
  ssr: true,
  buildEnd: sentryOnBuildEnd,
} satisfies Config;