fix(ui): always render tabs in insights/frontend to avoid flash during first navigation #100915
+26
−15
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.