Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Router browser tracing - Lazy imported routes with suspense start transaction spans with wrong path #15027

Open
3 tasks done
peteragarclover opened this issue Jan 16, 2025 · 2 comments · May be fixed by #15039
Open
3 tasks done
Assignees
Labels
Package: react Issues related to the Sentry React SDK

Comments

@peteragarclover
Copy link

peteragarclover commented Jan 16, 2025

Is there an existing issue for this?

How do you use Sentry?

Self-hosted/on-premise

Which SDK are you using?

@sentry/react

SDK Version

8.47.0

Framework Version

React 18.3.1

Link to Sentry event

No response

Reproduction Example/SDK Setup

Init sentry with React Router browser tracing integration.

Sentry.init({
  dsn,
  release,
  environment,
  attachStacktrace: true,
  tracePropagationTargets,
  integrations: [
    Sentry.reactRouterV7BrowserTracingIntegration({
      useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate,
});

Configure React Router with Sentry wrapper. But Lazy import routes, e.g. something like:

// user routes bundle loaded lazily
const UserRoutes = lazy(() => import("./UserRoutes"));

const Router: FC = () => {
  const { user } = useContext(UserContext);

  const sentryCreateBrowserRouter: typeof createBrowserRouter =
    Sentry.wrapCreateBrowserRouterV7(createBrowserRouter);

  const router = sentryCreateBrowserRouter(
    createRoutesFromElements(
      <>
        <Route path="login" element={<Login />} />
        <Route
          path="*"
          element={
            user ? (
              // All other routes for logged in users
              <Suspense fallback={<LoadingPage />}>
                <UserRoutes />
              </Suspense>
            ) : (
              // All other routes when not logged in, just navigate back to login page
              <Navigate to="/login" />
            )
          }
        />
      </>,
    ),
  );

Steps to Reproduce

  1. Configured React Router with Sentry Wrappers
  2. Lazy load routes
  3. Navigate to "/" (This initializes Sentry's route change subscriptions using routes available on initial page load)
  4. Navigate to lazy loaded routes, e.g. "/profile"

Expected Result

Navigation transaction span started using path "/profile"

Actual Result

Navigation transaction span started using path "/*"

This appears to be because the routes are only extracted once on mount:

_useEffect(
() => {
const routes = _createRoutesFromChildren(props.children) as RouteObject[];
if (isMountRenderPass.current) {
routes.forEach(route => {
const extractedChildRoutes = getChildRoutesRecursively(route);
extractedChildRoutes.forEach(r => {
allRoutes.add(r);
});
});
updatePageloadTransaction(getActiveRootSpan(), location, routes, undefined, undefined, Array.from(allRoutes));
isMountRenderPass.current = false;
} else {
handleNavigation({
location,
routes,
navigationType,
version,
allRoutes: Array.from(allRoutes),
});
}
},
// `props.children` is purposely not included in the dependency array, because we do not want to re-run this effect
// when the children change. We only want to start transactions when the location or navigation type change.
[location, navigationType],
);

And even though additional routes are lazy loaded, and the route name is correctly passed from the navigation event, when the name is normalized, it can't find the route, so falls back to the parent catch-all "/*"

const isInDescendantRoute = locationIsInsideDescendantRoute(location, allRoutes || routes);
if (isInDescendantRoute) {
name = prefixWithSlash(rebuildRoutePathFromAllRoutes(allRoutes || routes, location));
source = 'route';
}
if (!isInDescendantRoute || !name) {
[name, source] = getNormalizedName(routes, location, branches, basename);
}

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Jan 16, 2025
@github-actions github-actions bot added the Package: react Issues related to the Sentry React SDK label Jan 16, 2025
@s1gr1d
Copy link
Member

s1gr1d commented Jan 16, 2025

Hello, thanks for writing in and already trying to investigate this! We'll take a look at this.

However, if you are interested in contributing, feel free to let me know and open a PR! :)

Just for clarification: the first link to the code you posted is probably not the reason, as this is not the function which is used when setting up the routes with JSX. This would be this function:

export function createV6CompatibleWrapCreateBrowserRouter<

@onurtemizkan
Copy link
Collaborator

Thanks for the issue report @peteragarclover!

#15039 will add support for lazy-loaded components/pages on navigations.

However, adding support for lazy-loaded routes is a bit more tricky, as we are currently unable to access the lazy-loaded sub-Routes, without breaking lazy-loading.

I opened an issue to discuss how we can implement that - #15040

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: react Issues related to the Sentry React SDK
Projects
Status: No status
Development

Successfully merging a pull request may close this issue.

3 participants