Skip to content

Latest commit

 

History

History
296 lines (227 loc) · 8.16 KB

File metadata and controls

296 lines (227 loc) · 8.16 KB
title description sdk categories
React Router Framework
React Router v7 is a framework for building full-stack web apps and websites. Learn how to set it up with Sentry.
sentry.javascript.react-router
javascript
browser
server
server-node

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.

If you are using React Router in library mode, you can follow the instructions in the React guide here.

Install

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

<OnboardingOptionButtons options={[ "error-monitoring", "performance", "session-replay", { id: 'profiling', checked: false } ]} />

npm install @sentry/react-router @sentry/profiling-node
yarn add @sentry/react-router @sentry/profiling-node
pnpm add @sentry/react-router @sentry/profiling-node
```bash {tabTitle:npm} npm install @sentry/react-router ```
yarn add @sentry/react-router
pnpm add @sentry/react-router

Configure

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 Sentry React 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(),
    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:

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";
import { nodeProfilingIntegration } from '@sentry/profiling-node';

Sentry.init({
  dsn: "___PUBLIC_DSN___",
  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:

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

- export default function handleRequest(
+ function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  routerContext: EntryContext,
  loadContext: AppLoadContext,
) {
  return new Promise((resolve, reject) => {
    // ...
  }
}

+ export default Sentry.sentryHandleRequest(handleRequest);

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

Source Maps Upload

Update vite.config.ts to include the sentryReactRouter plugin. Also add your SentryReactRouterBuildOptions config options to the Vite config (this is required for uploading source maps at the end of the build):

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

const sentryConfig: SentryReactRouterBuildOptions = {
  org: "___ORG_SLUG___",
  project: "___PROJECT_SLUG___",

  // An auth token is required for uploading source maps.
  authToken: "___ORG_AUTH_TOKEN___"
  // ...
};

export default defineConfig(config => {
  return {
+   plugins: [reactRouter(),sentryReactRouter(sentryConfig, config)],
+   sentryConfig, // Also pass the config here!
  };
});

Include the sentryOnBuildEnd hook in react-router.config.ts:

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

export default {
  ssr: true,
  buildEnd: async ({ viteConfig, reactRouterConfig, buildManifest }) => {
    // ...
    // Call this at the end of the hook
+   await sentryOnBuildEnd({ viteConfig, reactRouterConfig, buildManifest });
  },
} satisfies Config;

Verify

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.

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.