Warning
This SDK is considered
This package is a wrapper around @sentry/node
for the server and @sentry/browser
for the client side.
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
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>
);
}
// ...
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
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",
}
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;