From 22bf82db3269b4cc4c2db5de5cdd8b54d9eb4157 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 27 Jan 2026 14:05:51 +0000 Subject: [PATCH 1/3] feat(react-router): Add Instrumentation API guide --- .../guides/react-router/features/index.mdx | 16 ++ .../features/instrumentation-api.mdx | 213 ++++++++++++++++++ .../javascript/guides/react-router/index.mdx | 2 +- .../guides/react-router/manual-setup.mdx | 9 +- 4 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 docs/platforms/javascript/guides/react-router/features/index.mdx create mode 100644 docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx diff --git a/docs/platforms/javascript/guides/react-router/features/index.mdx b/docs/platforms/javascript/guides/react-router/features/index.mdx new file mode 100644 index 0000000000000..f9f320c96131d --- /dev/null +++ b/docs/platforms/javascript/guides/react-router/features/index.mdx @@ -0,0 +1,16 @@ +--- +title: React Router Features +description: "Learn how Sentry's React Router SDK exposes features for first class integration with the framework." +sidebar_order: 4 +--- + + + +This SDK is currently in **beta**. Beta features are still in progress and may have bugs. Please reach out on +[GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback or concerns. + + + +The Sentry React Router SDK offers React Router-specific features for first class integration with the framework. + + diff --git a/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx b/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx new file mode 100644 index 0000000000000..acee038982fed --- /dev/null +++ b/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx @@ -0,0 +1,213 @@ +--- +title: Instrumentation API +description: "Automatic tracing for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers using React Router's instrumentation API." +sidebar_order: 10 +--- + + + +React Router's instrumentation API is experimental and uses the `unstable_instrumentations` name. The `unstable_` prefix indicates the API may change in minor releases. + + + +React Router 7.9.5+ provides an [instrumentation API](https://reactrouter.com/how-to/instrumentation) that enables automatic span creation for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers—without manual wrapper functions. Transaction names use parameterized route patterns (e.g., `/users/:id`), and errors are automatically captured with proper context. + +## Server-Side Setup + + + + + +Export `unstable_instrumentations` from your `entry.server.tsx` to enable automatic server-side tracing. + +The `createSentryServerInstrumentation()` function instruments: + +- **Request handlers**: Creates root HTTP server spans +- **Loaders**: Automatic loader span creation +- **Actions**: Automatic action span creation +- **Middleware**: Automatic middleware span creation +- **Lazy routes**: Lazy route loading spans + + + + +```tsx {tabTitle:Server} {filename:entry.server.tsx} +import * as Sentry from "@sentry/react-router"; +import { createReadableStreamFromReadable } from "@react-router/node"; +import { renderToPipeableStream } from "react-dom/server"; +import { ServerRouter } from "react-router"; + +export default Sentry.createSentryHandleRequest({ + ServerRouter, + renderToPipeableStream, + createReadableStreamFromReadable, +}); + +export const handleError = Sentry.createSentryHandleError(); + +// Enable automatic server-side instrumentation +export const unstable_instrumentations = [ + Sentry.createSentryServerInstrumentation(), +]; +``` + + + + + +You can optionally configure error capture behavior: + +```typescript +Sentry.createSentryServerInstrumentation({ + // Capture errors from loaders/actions automatically (default: true) + captureErrors: true, +}); +``` + +## Client-Side Setup + + + + + +To enable the client-side instrumentation API, pass `useInstrumentationAPI: true` to `reactRouterTracingIntegration()` and provide the `clientInstrumentation` to `HydratedRouter`. + +The client instrumentation covers: + +- **Navigations**: Programmatic navigation spans (including back/forward) +- **Fetchers**: Fetcher operation spans +- **Client loaders**: Client-side loader spans +- **Client actions**: Client-side action spans +- **Client middleware**: Client-side middleware spans +- **Lazy routes**: Lazy route loading spans + + + +`HydratedRouter` does not currently invoke client-side instrumentation hooks in Framework Mode. Client-side navigation tracking still works through the SDK's built-in instrumentation. The client-side setup shown here prepares your app for when React Router adds support. + + + + + + +```tsx {tabTitle:Client} {filename:entry.client.tsx} +import * as Sentry from "@sentry/react-router"; +import { startTransition, StrictMode } from "react"; +import { hydrateRoot } from "react-dom/client"; +import { HydratedRouter } from "react-router/dom"; + +const tracing = Sentry.reactRouterTracingIntegration({ + useInstrumentationAPI: true, +}); + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + integrations: [tracing], + tracesSampleRate: 1.0, +}); + +startTransition(() => { + hydrateRoot( + document, + + + + ); +}); +``` + + + + + +## Migrating from Manual Wrappers + +If you're using `wrapServerLoader` and `wrapServerAction`, you can migrate to the instrumentation API. The SDK automatically detects when the instrumentation API is active and skips span creation in manual wrappers, so you can migrate incrementally without duplicate spans. + + + + + +**Before** (manual wrappers): + +Each loader and action needs to be individually wrapped. + + + + +```tsx {filename:app/routes/users.$id.tsx} +import * as Sentry from "@sentry/react-router"; + +export const loader = Sentry.wrapServerLoader( + { name: "Load User" }, + async ({ params }) => { + const user = await getUser(params.id); + return { user }; + } +); + +export const action = Sentry.wrapServerAction( + { name: "Update User" }, + async ({ request }) => { + const formData = await request.formData(); + return updateUser(formData); + } +); +``` + + + + + + + +**After** (instrumentation API): + +Add the instrumentation export once in `entry.server.tsx`, and all loaders/actions are automatically traced. + + + + +```tsx {filename:app/routes/users.$id.tsx} +// No Sentry imports or wrappers needed! + +export async function loader({ params }) { + const user = await getUser(params.id); + return { user }; +} + +export async function action({ request }) { + const formData = await request.formData(); + return updateUser(formData); +} +``` + + + + + +## Troubleshooting + + + +If you're not seeing spans for your loaders and actions: + +1. **Check React Router version**: The instrumentation API requires React Router 7.9.5 or later +2. **Verify the export**: Ensure `unstable_instrumentations` is exported from `entry.server.tsx` +3. **Check sample rate**: Verify `tracesSampleRate` is set in your server configuration + + + + + +If you're seeing duplicate spans after adding the instrumentation API: + +The SDK automatically detects when the instrumentation API is active and skips span creation in manual wrappers. If you're still seeing duplicates: + +1. Update to the latest SDK version +2. Check that the instrumentation export is correctly configured +3. The manual wrappers (`wrapServerLoader`, `wrapServerAction`) will log a debug message when skipped + + diff --git a/docs/platforms/javascript/guides/react-router/index.mdx b/docs/platforms/javascript/guides/react-router/index.mdx index 555be5ede7c08..bc64bf7b183ed 100644 --- a/docs/platforms/javascript/guides/react-router/index.mdx +++ b/docs/platforms/javascript/guides/react-router/index.mdx @@ -116,7 +116,7 @@ Sentry.init({ // ___PRODUCT_OPTION_START___ performance // Registers and configures the Tracing integration, // which automatically instruments your application to monitor its - // performance, including custom Angular routing instrumentation + // performance, including custom React Router routing instrumentation Sentry.reactRouterTracingIntegration(), // ___PRODUCT_OPTION_END___ performance // ___PRODUCT_OPTION_START___ session-replay diff --git a/docs/platforms/javascript/guides/react-router/manual-setup.mdx b/docs/platforms/javascript/guides/react-router/manual-setup.mdx index 7e1e176b4af22..39d8d618c34b4 100644 --- a/docs/platforms/javascript/guides/react-router/manual-setup.mdx +++ b/docs/platforms/javascript/guides/react-router/manual-setup.mdx @@ -104,7 +104,7 @@ Initialize Sentry in your `entry.client.tsx` file: + // ___PRODUCT_OPTION_START___ performance + // Registers and configures the Tracing integration, + // which automatically instruments your application to monitor its -+ // performance, including custom Angular routing instrumentation ++ // performance, including custom React Router routing instrumentation + Sentry.reactRouterTracingIntegration(), + // ___PRODUCT_OPTION_END___ performance + // ___PRODUCT_OPTION_START___ session-replay @@ -131,7 +131,7 @@ Initialize Sentry in your `entry.client.tsx` file: + // We recommend adjusting this value in production + // Learn more at + // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#traces-sample-rate -+ tracesSampleRate: 1.0, // Capture 100% of the transactions ++ tracesSampleRate: 1.0, + + // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled + tracePropagationTargets: [/^\//, /^https:\/\/yourserver\.io\/api/], @@ -206,7 +206,10 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { - **Node 20:** Version \<20.19 - **Node 22:** Version \<22.12 -If you're on a different version, use our manual server wrappers. +If you're on a different version, you have two options: + +1. **Recommended**: Use the Instrumentation API (React Router 7.9.5+) for automatic tracing without Node version restrictions +2. **Alternative**: Use our manual server wrappers (shown below) For server loaders use `wrapServerLoader`: From e1d54f659053e2d7cc308d013a885807b12a8a0c Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 3 Feb 2026 18:14:47 +0000 Subject: [PATCH 2/3] Apply review suggestions --- .../features/instrumentation-api.mdx | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx b/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx index acee038982fed..c5bdb3632047c 100644 --- a/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx +++ b/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx @@ -10,7 +10,7 @@ React Router's instrumentation API is experimental and uses the `unstable_instru -React Router 7.9.5+ provides an [instrumentation API](https://reactrouter.com/how-to/instrumentation) that enables automatic span creation for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers—without manual wrapper functions. Transaction names use parameterized route patterns (e.g., `/users/:id`), and errors are automatically captured with proper context. +React Router 7.9.5+ provides an [instrumentation API](https://reactrouter.com/how-to/instrumentation) that enables automatic span creation for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers without the need for manual wrapper functions. Transaction names use parameterized route patterns, such as `/users/:id`, and errors are automatically captured with proper context. ## Server-Side Setup @@ -20,13 +20,13 @@ React Router 7.9.5+ provides an [instrumentation API](https://reactrouter.com/ho Export `unstable_instrumentations` from your `entry.server.tsx` to enable automatic server-side tracing. -The `createSentryServerInstrumentation()` function instruments: +The `createSentryServerInstrumentation()` function creates spans for: -- **Request handlers**: Creates root HTTP server spans -- **Loaders**: Automatic loader span creation -- **Actions**: Automatic action span creation -- **Middleware**: Automatic middleware span creation -- **Lazy routes**: Lazy route loading spans +- Request handlers (root HTTP server spans) +- Loaders +- Actions +- Middleware +- Lazy route loading @@ -53,10 +53,15 @@ export const unstable_instrumentations = [ - + + + You can optionally configure error capture behavior: + + + ```typescript Sentry.createSentryServerInstrumentation({ // Capture errors from loaders/actions automatically (default: true) @@ -64,6 +69,10 @@ Sentry.createSentryServerInstrumentation({ }); ``` + + + + ## Client-Side Setup @@ -72,18 +81,18 @@ Sentry.createSentryServerInstrumentation({ To enable the client-side instrumentation API, pass `useInstrumentationAPI: true` to `reactRouterTracingIntegration()` and provide the `clientInstrumentation` to `HydratedRouter`. -The client instrumentation covers: +The client instrumentation creates spans for: -- **Navigations**: Programmatic navigation spans (including back/forward) -- **Fetchers**: Fetcher operation spans -- **Client loaders**: Client-side loader spans -- **Client actions**: Client-side action spans -- **Client middleware**: Client-side middleware spans -- **Lazy routes**: Lazy route loading spans +- Navigations (including back/forward) +- Fetchers +- Client loaders +- Client actions +- Client middleware +- Lazy route loading - + -`HydratedRouter` does not currently invoke client-side instrumentation hooks in Framework Mode. Client-side navigation tracking still works through the SDK's built-in instrumentation. The client-side setup shown here prepares your app for when React Router adds support. +`HydratedRouter` doesn't currently invoke client-side instrumentation hooks when running in Framework Mode. As a result, only client-side navigation spans are captured through the SDK's built-in instrumentation. The client-side setup shown here prepares your app for when React Router adds support for invoking these hooks. @@ -130,9 +139,9 @@ If you're using `wrapServerLoader` and `wrapServerAction`, you can migrate to th -**Before** (manual wrappers): +**Before migrating** (manual wrappers): -Each loader and action needs to be individually wrapped. +Without the instrumentation API, each loader and action needs to be wrapped individually. @@ -163,15 +172,15 @@ export const action = Sentry.wrapServerAction( -**After** (instrumentation API): +**After migrating** (instrumentation API): -Add the instrumentation export once in `entry.server.tsx`, and all loaders/actions are automatically traced. +After adding the instrumentation export once in `entry.server.tsx`, all loaders and actions are traced automatically. ```tsx {filename:app/routes/users.$id.tsx} -// No Sentry imports or wrappers needed! +// No Sentry imports or wrappers needed export async function loader({ params }) { const user = await getUser(params.id); @@ -194,9 +203,9 @@ export async function action({ request }) { If you're not seeing spans for your loaders and actions: -1. **Check React Router version**: The instrumentation API requires React Router 7.9.5 or later -2. **Verify the export**: Ensure `unstable_instrumentations` is exported from `entry.server.tsx` -3. **Check sample rate**: Verify `tracesSampleRate` is set in your server configuration +1. Check that the React Router version is 7.9.5 or later +2. Make sure `unstable_instrumentations` is exported from `entry.server.tsx` +3. Verify `tracesSampleRate` is set in your server configuration @@ -208,6 +217,6 @@ The SDK automatically detects when the instrumentation API is active and skips s 1. Update to the latest SDK version 2. Check that the instrumentation export is correctly configured -3. The manual wrappers (`wrapServerLoader`, `wrapServerAction`) will log a debug message when skipped +3. Enable debug mode to verify the manual wrappers are being skipped—they log a message when the instrumentation API is active From 11bbe8214457245085f68728933d0f765c0fd10a Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 3 Feb 2026 18:46:14 +0000 Subject: [PATCH 3/3] Clarify which transactions use parameterized route patterns --- .../guides/react-router/features/instrumentation-api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx b/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx index c5bdb3632047c..f3241d51b7ecf 100644 --- a/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx +++ b/docs/platforms/javascript/guides/react-router/features/instrumentation-api.mdx @@ -10,7 +10,7 @@ React Router's instrumentation API is experimental and uses the `unstable_instru -React Router 7.9.5+ provides an [instrumentation API](https://reactrouter.com/how-to/instrumentation) that enables automatic span creation for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers without the need for manual wrapper functions. Transaction names use parameterized route patterns, such as `/users/:id`, and errors are automatically captured with proper context. +React Router 7.9.5+ provides an [instrumentation API](https://reactrouter.com/how-to/instrumentation) that enables automatic span creation for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers without the need for manual wrapper functions. Transaction names (for HTTP requests, pageloads, and navigations) use parameterized route patterns, such as `/users/:id`, and errors are automatically captured with proper context. ## Server-Side Setup