Skip to content

Conversation

jasonyuezhang
Copy link
Owner

In the current UI, wherever we use top-level tabs, we can see a full flash of an empty page when doing the first navigation, which is pretty annoying and not great UX:

Screen.Recording.2025-10-03.at.22.18.43.mov

The technical problem is that we lazy-load each route, but each route is also responsible for rendering its own tabs. That means when we navigate through the tabs, the tabs unmount.

This PR introduces a solution where we use nested layouts to render the tabs instead of having the tabs be part of each separate page.

That means the tabs will stay mounted while only its content lazy-loads, which leads to a lot better UX:

Screen.Recording.2025-10-03.at.22.17.17.mov

Technically, we render a layout.tsx file at the root of /frontend, which will render the <FrontendHeader> for all sub-routes plus an <Outlet> for the children.

The problem is that the layout must know which “child” it is going to render in order to select the current tab.
With useMatches, we can take a look at what renders at runtime, and then “inspect” the last match to see what child it is. We could just look at the url path segment but that’s a bit brittle, so I opted for using route handles.

Route handles are often used to build dynamic UI elements like breadcrumbs based on the route hierarchy. Here, we just use it to pass custom information from the route tree (where we define our routes) to the layout, so that it knows what to do. In this case, we pass the ModuleName of the rendered page so that we can pass that to the <FrontendHeader> component. This sadly isn’t type-safe (react-router doesn’t do type-safety like that), but I think this is a fine trade-off.


Copied from getsentry#100915
Original PR: getsentry#100915

Copy link

Refactor Frontend Tabs Rendering in Insights to Prevent Flashing on Navigation

This PR restructures how top-level tabs are rendered within the insights/frontend section of the Sentry UI to address a UI issue where navigating tabs caused a flash of empty content due to lazy loading and tabs being unmounted/remounted. The solution introduces a persistent layout component (layout.tsx) that uses React Router's nested layouts and route handle metadata to mount a shared FrontendHeader above all frontend sub-routes, keeping the tabs visible and only lazy-loading the route content. Multiple page components drop their individual tab rendering logic and instead rely on the layout for tab persistence. The routing tree is updated to consistently supply the necessary tab context via the handle field for all relevant frontend-insights pages, and related route/type-handling utilities are updated accordingly.

Key Changes

• Introduced new persistent FrontendLayout in static/app/views/insights/pages/frontend/layout.tsx that renders FrontendHeader based on route handle and keeps it mounted for all child routes via <Outlet>.
• Updated static/app/routes.tsx to use the new layout component for all /frontend-related insights pages, and set appropriate handle data for child routes to provide tab metadata.
• Removed per-page direct rendering of FrontendHeader from webVitalsLandingPage.tsx, frontendOverviewPage.tsx, resourcesLandingPage.tsx, httpLandingPage.tsx, and the sessions overview.tsx page, shifting responsibility to the shared layout.
• Extended the SentryRouteObject type in static/app/components/route.tsx to include a handle property for routing metadata.
• Adjusted route translation logic in static/app/utils/reactRouter6Compat/router.tsx to propagate route handle metadata correctly.

Affected Areas

insights/frontend page layouts and header rendering
static/app/routes.tsx (routing tree for insights pages)
static/app/views/insights/browser/webVitals/views/webVitalsLandingPage.tsx
static/app/views/insights/pages/frontend/frontendOverviewPage.tsx
static/app/views/insights/browser/resources/views/resourcesLandingPage.tsx
static/app/views/insights/http/views/httpLandingPage.tsx
static/app/views/insights/sessions/views/overview.tsx
static/app/components/route.tsx
static/app/utils/reactRouter6Compat/router.tsx

This summary was automatically generated by @propel-code-bot

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.

2 participants