Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(react-router): Add Experimental React Server Components (RSC) instrumentation#18882

Open
onurtemizkan wants to merge 13 commits intodevelopfrom
onur/react-router-rsc-experimental
Open

feat(react-router): Add Experimental React Server Components (RSC) instrumentation#18882
onurtemizkan wants to merge 13 commits intodevelopfrom
onur/react-router-rsc-experimental

Conversation

@onurtemizkan
Copy link
Collaborator

@onurtemizkan onurtemizkan commented Jan 19, 2026

Resolves: #17337

Adds experimental instrumentation for React Router v7.9.0+ React Server Components (RSC).

React Router's RSC support is currently behind unstable_reactRouterRSC() and subject to change. These wrappers provide error capture and performance tracing for RSC server requests, server components, and server functions.

RSC Request Handlers

Added wrapMatchRSCServerRequest() and wrapRouteRSCServerRequest():

  • wrapMatchRSCServerRequest - Wraps RSC server matching with spans for generateResponse and loadServerAction
  • wrapRouteRSCServerRequest - Wraps SSR request handling with spans for fetchServer and renderHTML
// rsc-handler.ts
import {
  unstable_matchRSCServerRequest as matchRSCServerRequest,
  unstable_routeRSCServerRequest as routeRSCServerRequest,
} from "react-router";
import { wrapMatchRSCServerRequest, wrapRouteRSCServerRequest } from "@sentry/react-router";

const sentryMatchRSCServerRequest = wrapMatchRSCServerRequest(matchRSCServerRequest);
const sentryRouteRSCServerRequest = wrapRouteRSCServerRequest(routeRSCServerRequest);

Server Components

Added wrapServerComponent() for error instrumentation:

// routes/users.$id.tsx
import { wrapServerComponent } from "@sentry/react-router";

async function _UserPage({ params }: Route.ComponentProps) {
  const user = await getUser(params.id);
  return <UserProfile user={user} />;
}

export const ServerComponent = wrapServerComponent(_UserPage, {
  componentRoute: "/users/:id",
  componentType: "Page",
});

Redirect (3xx) and not-found (404) responses are not captured as errors.

Server Functions

Added wrapServerFunction() and wrapServerFunctions() for "use server" functions:

// actions.ts
"use server";
import { wrapServerFunction } from "@sentry/react-router";

async function _updateUser(formData: FormData) {
  await db.users.update(formData.get("id"), { name: formData.get("name") });
}

export const updateUser = wrapServerFunction("updateUser", _updateUser);

Needs docs PR

@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,038 - 8,903 +2%
GET With Sentry 1,711 19% 1,745 -2%
GET With Sentry (error only) 6,196 69% 6,222 -0%
POST Baseline 1,202 - 1,223 -2%
POST With Sentry 598 50% 590 +1%
POST With Sentry (error only) 1,058 88% 1,069 -1%
MYSQL Baseline 3,334 - 3,336 -0%
MYSQL With Sentry 513 15% 496 +3%
MYSQL With Sentry (error only) 2,722 82% 2,696 +1%

View base workflow run

@onurtemizkan onurtemizkan force-pushed the onur/react-router-rsc-experimental branch 2 times, most recently from 159fb80 to 07fc2d6 Compare January 23, 2026 15:42
@chargome
Copy link
Member

chargome commented Feb 2, 2026

@onurtemizkan sorry this one went under the radar. Can you resolve the conflict and ping me again?

@onurtemizkan onurtemizkan force-pushed the onur/react-router-rsc-experimental branch from 07fc2d6 to c94138a Compare February 4, 2026 00:53
@onurtemizkan onurtemizkan marked this pull request as ready for review February 4, 2026 00:53
@onurtemizkan
Copy link
Collaborator Author

@chargome, it's ready for review 👍

@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

Codecov Results 📊


Generated by Codecov Action

Copy link
Member

@chargome chargome left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM to me but is there a way we could already wrap the server components with vite? Also initing the Client in a hook might happen quite late – any ideas if the client will eventually support an entry file?

Comment on lines 4 to 8
/**
* WeakSet to track errors that have been captured to avoid double-capture.
* Uses WeakSet so errors are automatically removed when garbage collected.
*/
const CAPTURED_ERRORS = new WeakSet<object>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: Why do we need this in here? We have a dedupe integration that should take of this I think?

export { wrapServerFunction } from './wrapServerFunction';
export { wrapServerComponent } from './wrapServerComponent';

export type { ServerComponentContext, WrapServerFunctionOptions } from './types';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSC request handler wrappers not exported from package

Medium Severity

The PR description documents wrapMatchRSCServerRequest, wrapRouteRSCServerRequest, and wrapServerFunctions as public APIs with usage examples showing imports from @sentry/react-router. However, these functions are implemented in their respective files but not exported from packages/react-router/src/server/rsc/index.ts. Only wrapServerFunction and wrapServerComponent are exported. Users following the documentation will get import errors when trying to use the RSC request handler wrappers.

Additional Locations (1)

Fix in Cursor Fix in Web

@onurtemizkan onurtemizkan force-pushed the onur/react-router-rsc-experimental branch from 7954e55 to 141002a Compare February 6, 2026 16:17
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

} catch {
return false;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant isAlreadyCaptured duplicates captureException internal dedup

Low Severity

The isAlreadyCaptured function in responseUtils.ts duplicates dedup logic already internal to captureException. The core SDK's captureException calls checkOrSetAlreadyCaught (in packages/core/src/client.ts), which checks __sentry_captured__ and returns early if the error was already captured. This makes the pre-check in wrapServerComponent and wrapServerFunction redundant and introduces a maintenance risk if the core SDK's property name or logic changes. The PR reviewer @chargome also flagged this.

Additional Locations (1)

Fix in Cursor Fix in Web

}
return false;
},
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-instrumentation silently drops export * re-exports

Medium Severity

The recast.visit call in analyzeModule handles visitExportDefaultDeclaration and visitExportNamedDeclaration but has no visitExportAllDeclaration handler. When a "use server" file uses export * from './other-actions' alongside direct exports, the transform hook completely replaces the file content with generated wrapper code that only includes the directly declared exports. The export * re-exports are silently dropped, causing those functions to become unavailable at runtime.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support React Router Server Components

2 participants