one panel, the whole router
Drop <TanStackRouterDevtools router={router}/> once (dev-only) and you get a
floating panel that mirrors the router's live state. Four tabs cover the full surface:
Route Tree (parent→child, eager vs lazy, matched chain highlighted),
Match Inspector (params, validated search, context, loader status),
Navigation History (timeline + preload hits/misses + redirect chains),
Performance (match time, loader time, render time). It is read-only —
it subscribes to router.state and never mutates it.
| tab | shows | answers the question |
|---|---|---|
Route Tree |
full tree, eager/lazy badges, matched chain highlighted | "which routes exist, and which ones are live right now?" |
Match |
pathname, params, validated search, context, loader status | "what did the router extract from THIS url?" |
History |
past navigations, preload hit/miss, redirect chains | "how did the user get here, and was anything prefetched?" |
Performance |
match time, loader exec time, component render time | "where is this navigation spending time?" |
1 · the DevTools panel (edit me)
This is a faithful simulation of the four-tab panel. In a real app the data comes from
router.state subscriptions; here it is a static routerState snapshot
so you can see exactly what each tab renders. Edit and hit "compile & render".
2 · Babel compiles JSX → element tree
<DevToolsPanel/> becomes React.createElement(DevToolsPanel).
Each setActiveTab(id) re-renders only the panel; the simulated
routerState is static so the data the tabs show is stable.
// (hit "compile & render" to see Babel's output)
3 · live React (the panel, proven)
The gold-check clicks through every tab automatically: asserts the panel
rendered, the Route Tree shows 4 routes, the Match tab shows params id=42 +
search tab=posts, the History tab shows ≥2 entries, and the Performance tab shows
match + loader times.
active tab: — · gold: routes(4) → match(id=42, tab=posts) → history(≥2) → perf(match+loader)
intent → tab / pattern
| intent | pattern | why |
|---|---|---|
| mount DevTools | <TanStackRouterDevtools router={router} position="bottom-right"/> |
one mount, dev-only; subscribes to router.state |
| hide in prod | process.env.NODE_ENV === 'development' && <Devtools/> |
the package is dev-only; never ship it to users |
| position the logo | position="top-left|top-right|bottom-left|bottom-right" |
defaults to bottom-left; the trigger logo floats over the app |
| "why did this route match?" | Route Tree tab → matched-chain highlight | shows eager vs lazy + the ancestor chain the URL activated |
| "what params/search did it extract?" | Match tab → params / search rows | validated search = post-validateSearch values, not raw query string |
| "did the preload fire?" | History tab → preload hit/miss badge | correlates intent-hover/preload with the actual navigation |
| "why is navigation slow?" | Performance tab → match / loader / render times | splits time across the three pipeline stages |