Skip to content

Commit 22f8e5a

Browse files
authored
fix(react): Do not send additional navigation span on pageload (#17799)
The React Router instrumentation created an additional `navigation` span on `pageload`. On initial load, the lazy route has the history action `POP` and state `idle`. This leads to the generation of a `navigation` span. I added a condition to early-return from `handleNavigation` if a page load is going on. Closes issue (in Linear) https://linear.app/getsentry/issue/FE-551/configure-react-router-for-fully-parameterized-routes-with-sentry
1 parent 0b4a7b1 commit 22f8e5a

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/tests/transactions.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,37 @@ test('Creates a pageload transaction with parameterized route', async ({ page })
2323
expect(event.contexts?.trace?.op).toBe('pageload');
2424
});
2525

26+
test('Does not create a navigation transaction on initial load to deep lazy route', async ({ page }) => {
27+
const navigationPromise = waitForTransaction('react-router-7-lazy-routes', async transactionEvent => {
28+
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
29+
});
30+
31+
const pageloadPromise = waitForTransaction('react-router-7-lazy-routes', async transactionEvent => {
32+
return (
33+
!!transactionEvent?.transaction &&
34+
transactionEvent.contexts?.trace?.op === 'pageload' &&
35+
transactionEvent.transaction === '/lazy/inner/:id/:anotherId/:someAnotherId'
36+
);
37+
});
38+
39+
await page.goto('/lazy/inner/1/2/3');
40+
41+
const pageloadEvent = await pageloadPromise;
42+
43+
expect(pageloadEvent.transaction).toBe('/lazy/inner/:id/:anotherId/:someAnotherId');
44+
45+
const lazyRouteContent = page.locator('id=innermost-lazy-route');
46+
await expect(lazyRouteContent).toBeVisible();
47+
48+
// "Race" between navigation transaction and a timeout to ensure no navigation transaction is created within the timeout period
49+
const result = await Promise.race([
50+
navigationPromise.then(() => 'navigation'),
51+
new Promise<'timeout'>(resolve => setTimeout(() => resolve('timeout'), 1500)),
52+
]);
53+
54+
expect(result).toBe('timeout');
55+
});
56+
2657
test('Creates a navigation transaction inside a lazy route', async ({ page }) => {
2758
const transactionPromise = waitForTransaction('react-router-7-lazy-routes', async transactionEvent => {
2859
return (

packages/react/src/reactrouter-compat-utils/instrumentation.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,13 @@ export function handleNavigation(opts: {
567567
return;
568568
}
569569

570+
// Avoid starting a navigation span on initial load when a pageload root span is active.
571+
// This commonly happens when lazy routes resolve during the first render and React Router emits a POP.
572+
const activeRootSpan = getActiveRootSpan();
573+
if (activeRootSpan && spanToJSON(activeRootSpan).op === 'pageload' && navigationType === 'POP') {
574+
return;
575+
}
576+
570577
if ((navigationType === 'PUSH' || navigationType === 'POP') && branches) {
571578
const [name, source] = resolveRouteNameAndSource(
572579
location,

0 commit comments

Comments
 (0)