Instrumentation API

Automatic tracing for loaders, actions, middleware, navigations, fetchers, lazy routes, and request handlers using React Router's instrumentation API.

React Router 7.9.5+ provides an instrumentation API 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.

Export unstable_instrumentations from your entry.server.tsx to enable automatic server-side tracing.

The createSentryServerInstrumentation() function creates spans for:

  • Request handlers (root HTTP server spans)
  • Loaders
  • Actions
  • Middleware
  • Lazy route loading
entry.server.tsx
Copied
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:

Copied
Sentry.createSentryServerInstrumentation({
  // Capture errors from loaders/actions automatically (default: true)
  captureErrors: true,
});

To enable the client-side instrumentation API, pass useInstrumentationAPI: true to reactRouterTracingIntegration() and provide the clientInstrumentation to HydratedRouter.

The client instrumentation creates spans for:

  • Navigations (including back/forward)
  • Fetchers
  • Client loaders
  • Client actions
  • Client middleware
  • Lazy route loading
entry.client.tsx
Copied
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,
    <StrictMode>
      <HydratedRouter
        unstable_instrumentations={[tracing.clientInstrumentation]}
      />
    </StrictMode>,
  );
});

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 migrating (manual wrappers):

Without the instrumentation API, each loader and action needs to be wrapped individually.

app/routes/users.$id.tsx
Copied
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 migrating (instrumentation API):

After adding the instrumentation export once in entry.server.tsx, all loaders and actions are traced automatically.

app/routes/users.$id.tsx
Copied
// 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);
}

Spans not appearing for loaders/actions

If you're not seeing spans for your loaders and actions:

  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
Duplicate spans when using manual wrappers

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. Enable debug mode to verify the manual wrappers are being skipped—they log a message when the instrumentation API is active
Was this helpful?
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").