Skip to content

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,035 - 8,944 +1%
GET With Sentry 1,712 19% 1,786 -4%
GET With Sentry (error only) 6,020 67% 6,223 -3%
POST Baseline 1,205 - 1,206 -0%
POST With Sentry 569 47% 597 -5%
POST With Sentry (error only) 1,063 88% 1,064 -0%
MYSQL Baseline 3,296 - 3,304 -0%
MYSQL With Sentry 425 13% 513 -17%
MYSQL With Sentry (error only) 2,678 81% 2,667 +0%

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?

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

3 participants