@@ -83,42 +83,42 @@ npx react-router reveal
8383
8484Initialize the Sentry React SDK in your ` entry.client.tsx ` file:
8585
86- ``` tsx {filename: entry.client.tsx}
87- import * as Sentry from " @sentry/react-router" ;
88- import { startTransition , StrictMode } from " react" ;
89- import { hydrateRoot } from " react-dom/client" ;
90- import { HydratedRouter } from " react-router/dom" ;
91-
92- Sentry .init ({
93- dsn: " ___PUBLIC_DSN___" ,
94-
95- // Adds request headers and IP for users, for more info visit:
96- // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
97- sendDefaultPii: true ,
98-
99- integrations: [
100- // ___PRODUCT_OPTION_START___ performance
101- Sentry .browserTracingIntegration (),
102- // ___PRODUCT_OPTION_END___ performance
103- // ___PRODUCT_OPTION_START___ session-replay
104- Sentry .replayIntegration (),
105- // ___PRODUCT_OPTION_END___ session-replay
106- ],
107- // ___PRODUCT_OPTION_START___ performance
108-
109- tracesSampleRate: 1.0 , // Capture 100% of the transactions
110-
111- // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled
112- tracePropagationTargets: [/ ^ \/ / , / ^ https:\/\/ yourserver\. io\/ api/ ],
113- // ___PRODUCT_OPTION_END___ performance
114- // ___PRODUCT_OPTION_START___ session-replay
115-
116- // Capture Replay for 10% of all sessions,
117- // plus 100% of sessions with an error
118- replaysSessionSampleRate: 0.1 ,
119- replaysOnErrorSampleRate: 1.0 ,
120- // ___PRODUCT_OPTION_END___ session-replay
121- });
86+ ``` tsx {diff} { filename: entry.client.tsx}
87+ + import * as Sentry from " @sentry/react-router" ;
88+ import { startTransition , StrictMode } from " react" ;
89+ import { hydrateRoot } from " react-dom/client" ;
90+ import { HydratedRouter } from " react-router/dom" ;
91+
92+ + Sentry .init ({
93+ + dsn: " ___PUBLIC_DSN___" ,
94+ +
95+ + // Adds request headers and IP for users, for more info visit:
96+ + // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
97+ + sendDefaultPii: true ,
98+ +
99+ + integrations: [
100+ + // ___PRODUCT_OPTION_START___ performance
101+ + Sentry .browserTracingIntegration (),
102+ + // ___PRODUCT_OPTION_END___ performance
103+ + // ___PRODUCT_OPTION_START___ session-replay
104+ + Sentry .replayIntegration (),
105+ + // ___PRODUCT_OPTION_END___ session-replay
106+ + ],
107+ + // ___PRODUCT_OPTION_START___ performance
108+ +
109+ + tracesSampleRate: 1.0 , // Capture 100% of the transactions
110+ +
111+ + // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled
112+ + tracePropagationTargets: [/ ^ \/ / , / ^ https:\/\/ yourserver\. io\/ api/ ],
113+ + // ___PRODUCT_OPTION_END___ performance
114+ + // ___PRODUCT_OPTION_START___ session-replay
115+ +
116+ + // Capture Replay for 10% of all sessions,
117+ + // plus 100% of sessions with an error
118+ + replaysSessionSampleRate: 0.1 ,
119+ + replaysOnErrorSampleRate: 1.0 ,
120+ + // ___PRODUCT_OPTION_END___ session-replay
121+ + });
122122
123123startTransition (() => {
124124 hydrateRoot (
@@ -133,7 +133,7 @@ startTransition(() => {
133133Now, update your ` app/root.tsx ` file to report any unhandled errors from your error boundary:
134134
135135``` tsx {diff} {filename: app/root.tsx}
136- import * as Sentry from " @sentry/react-router" ;
136+ + import * as Sentry from " @sentry/react-router" ;
137137
138138export function ErrorBoundary({ error }: Route .ErrorBoundaryProps ) {
139139 let message = " Oops!" ;
@@ -199,40 +199,110 @@ Sentry.init({
199199});
200200```
201201
202- In your ` entry.server.tsx ` file, export the ` handleError ` function :
202+ Update your ` entry.server.tsx ` file:
203203
204204``` tsx {diff} {filename: entry.server.tsx}
205- import * as Sentry from " @sentry/react-router" ;
206- import { type HandleErrorFunction } from " react-router" ;
205+ + import * as Sentry from ' @sentry/react-router' ;
206+ import { createReadableStreamFromReadable } from ' @react-router/node' ;
207+ import { renderToPipeableStream } from ' react-dom/server' ;
208+ import { ServerRouter } from ' react-router' ;
209+ import { type HandleErrorFunction } from ' react-router' ;
210+
211+ + const handleRequest = Sentry .createSentryHandleRequest ({
212+ + ServerRouter ,
213+ + renderToPipeableStream ,
214+ + createReadableStreamFromReadable ,
215+ +});
216+
217+ export default handleRequest ;
207218
208219export const handleError: HandleErrorFunction = (error , { request }) => {
209- // React Router may abort some interrupted requests, report those
220+ // React Router may abort some interrupted requests, don't log those
210221 if (! request .signal .aborted ) {
211222+ Sentry .captureException (error );
212-
213- // make sure to still log the error so you can see it
223+ // optionally log the error so you can see it
214224 console .error (error );
215225 }
216226};
217227
218- - export default function handleRequest(
219- + function handleRequest (
220- request : Request ,
221- responseStatusCode : number ,
222- responseHeaders : Headers ,
223- routerContext : EntryContext ,
224- loadContext : AppLoadContext ,
225- ) {
226- return new Promise ((resolve , reject ) => {
227- // ...
228- }
229- }
230-
231- + export default Sentry .sentryHandleRequest (handleRequest );
232228
233229// ... rest of your server entry
234230```
235231
232+ <Expandable title = " Do you need to customize your handleRequest function?" >
233+ If you need to update the logic of your ` handleRequest ` function you'll need to include the provided Sentry helper functions (` getMetaTagTransformer ` and ` wrapSentryHandleRequest ` ) manually:
234+
235+ ``` tsx {1-4, 44-45, 69-70}
236+ import { getMetaTagTransformer , wrapSentryHandleRequest } from ' @sentry/react-router' ;
237+ // ... other imports
238+
239+ const handleRequest = function handleRequest(
240+ request : Request ,
241+ responseStatusCode : number ,
242+ responseHeaders : Headers ,
243+ routerContext : EntryContext ,
244+ _loadContext : AppLoadContext ,
245+ ): Promise <Response > {
246+ return new Promise ((resolve , reject ) => {
247+ let shellRendered = false ;
248+ const userAgent = request .headers .get (' user-agent' );
249+
250+ // Determine if we should use onAllReady or onShellReady
251+ const isBot = typeof userAgent === ' string' && botRegex .test (userAgent );
252+ const isSpaMode = !! (routerContext as { isSpaMode? : boolean }).isSpaMode ;
253+
254+ const readyOption = isBot || isSpaMode ? ' onAllReady' : ' onShellReady' ;
255+
256+ const { pipe, abort } = renderToPipeableStream (
257+ <ServerRouter context = { routerContext } url = { request .url } />,
258+ {
259+ [readyOption ]() {
260+ shellRendered = true ;
261+ const body = new PassThrough ();
262+
263+ const stream = createReadableStreamFromReadable (body );
264+
265+ responseHeaders .set (' Content-Type' , ' text/html' );
266+
267+ resolve (
268+ new Response (stream , {
269+ headers: responseHeaders ,
270+ status: responseStatusCode ,
271+ }),
272+ );
273+
274+ // this enables distributed tracing between client and server
275+ pipe (getMetaTagTransformer (body ));
276+ },
277+ onShellError(error : unknown ) {
278+ reject (error );
279+ },
280+ onError(error : unknown ) {
281+ // eslint-disable-next-line no-param-reassign
282+ responseStatusCode = 500 ;
283+ // Log streaming rendering errors from inside the shell. Don't log
284+ // errors encountered during initial shell rendering since they'll
285+ // reject and get logged in handleDocumentRequest.
286+ if (shellRendered ) {
287+ // eslint-disable-next-line no-console
288+ console .error (error );
289+ }
290+ },
291+ },
292+ );
293+
294+ // Abort the rendering stream after the `streamTimeout`
295+ setTimeout (abort , streamTimeout );
296+ });
297+ };
298+
299+ // wrap the default export
300+ export default wrapSentryHandleRequest (handleRequest );
301+
302+ // ... rest of your entry.server.ts file
303+ ```
304+ </Expandable >
305+
236306### Update Scripts
237307
238308Since 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.
0 commit comments