tanstack-router-cache 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,503 @@
1
+ # Architecture
2
+
3
+ This page explains how `tanstack-router-cache` is built: runtime pieces, state shape, rendering flow, eviction, events, and memory behavior.
4
+
5
+ ## Quick map
6
+
7
+ `tanstack-router-cache` replaces the normal TanStack Router outlet area with a cache manager. The live route still comes from TanStack Router. Cacheable route trees are captured after they become ready, then rendered from a stored router snapshot when they should stay mounted.
8
+
9
+ ```mermaid
10
+ flowchart TD
11
+ router["TanStack Router state"]
12
+ provider["RouterCacheProvider"]
13
+ outlet["RouterCacheOutlet"]
14
+ manager["Cache manager"]
15
+ live["Live Outlet"]
16
+ cache["CachedRoutes map"]
17
+ cachedOutlet["CachedOutlet"]
18
+ snapshot["Router snapshot"]
19
+ activity["React Activity container"]
20
+ hooks["Hooks and debug API"]
21
+
22
+ provider --> outlet
23
+ router --> manager
24
+ outlet --> manager
25
+ manager --> live
26
+ manager --> cache
27
+ cache --> cachedOutlet
28
+ cachedOutlet --> snapshot
29
+ snapshot --> activity
30
+ manager --> hooks
31
+ ```
32
+
33
+ ## Runtime pieces
34
+
35
+ | Piece | Responsibility |
36
+ | --- | --- |
37
+ | `RouterCacheProvider` | Owns cache state, cache limits, scope resets, and errored-route tracking. |
38
+ | `RouterCacheOutlet` | Replaces TanStack Router's outlet for the route branch that can be cached. |
39
+ | Cache manager | Reads live router state, decides whether to render the live outlet or cached outlets, and synchronizes cache entries. |
40
+ | `CachedOutlet` | Renders a cached route from a stored router snapshot and match id. |
41
+ | `OffScreen` | Wraps cached route content in React `Activity` and marks the route as `visible` or `hidden`. |
42
+ | Event listener | Emits route activity and cached-navigation lifecycle events used by hooks. |
43
+ | Debug hook | Exposes development diagnostics on `window.__TANSTACK_ROUTER_CACHE_DEBUG__`. |
44
+ | Transient UI tracker | Tracks external UI added outside the route container and hides or restores it with the owning route. |
45
+
46
+ ## Route lifecycle
47
+
48
+ A route becomes cacheable only after the current match is resolved, successful, and marked with `staticData.routeCache: true`.
49
+
50
+ ```mermaid
51
+ stateDiagram-v2
52
+ [*] --> LiveRoute: normal TanStack render
53
+ LiveRoute --> WaitingForReady: routeCache true
54
+ WaitingForReady --> CachedReady: match success and snapshot stored
55
+ CachedReady --> VisibleCached: pathname is visible
56
+ VisibleCached --> HiddenCached: navigate away
57
+ HiddenCached --> VisibleCached: navigate back
58
+ HiddenCached --> Evicted: cache limit or manual invalidation
59
+ VisibleCached --> Evicted: route error or no longer cacheable
60
+ Evicted --> [*]
61
+ ```
62
+
63
+ In normal usage, a cached route alternates between `visible` and `hidden`. It leaves the cache when a limit evicts it, the app invalidates it, the route stops being cacheable, or an error boundary marks it as failed.
64
+
65
+ ## Provider state
66
+
67
+ The provider stores cached routes by normalized pathname. The same object is exposed publicly as `cachedRoutes`.
68
+
69
+ ```ts
70
+ type CachedRoutes = {
71
+ [normalizedPathname: string]: CachedRouteData;
72
+ };
73
+
74
+ type ErroredRouteCounts = Record<string, number>;
75
+ ```
76
+
77
+ ```mermaid
78
+ flowchart LR
79
+ provider["RouterCacheProvider"]
80
+ routes["CachedRoutes map"]
81
+ errors["ErroredRouteCounts"]
82
+ limits["Cache limits"]
83
+ scope["cacheScopeKey"]
84
+
85
+ provider --> routes
86
+ provider --> errors
87
+ provider --> limits
88
+ scope --> provider
89
+ ```
90
+
91
+ `ErroredRouteCounts` prevents failed cached views from being reused while an error fallback is mounted. It is count-based so repeated error-boundary hooks can retain and release the same pathname safely.
92
+
93
+ The provider exposes these operations to the cache manager and hooks:
94
+
95
+ | Operation | Purpose |
96
+ | --- | --- |
97
+ | Upsert cached route | Insert or update one cached route entry after normalizing the pathname. |
98
+ | Delete cached routes | Remove cached entries. Used by invalidation, errored routes, and cache manager cleanup. |
99
+ | Touch cached routes | Update `lastVisibleAt` when a cached route becomes visible. |
100
+ | Retain errored route | Mark a pathname as currently errored and remove its cached entry. |
101
+ | Release errored route | Release one error retain count for a pathname. |
102
+
103
+ ## Cached route data
104
+
105
+ Each cache entry is small. The large memory cost is the retained React tree, not this data object.
106
+
107
+ ```ts
108
+ type CachedRouteData = {
109
+ createdAt?: number;
110
+ href?: string;
111
+ lastVisibleAt?: number;
112
+ routeId?: string;
113
+ staticData: StaticDataRouteOption;
114
+ matchId?: string;
115
+ routerSnapshot?: RouterSnapshot;
116
+ ready?: boolean;
117
+ };
118
+ ```
119
+
120
+ ```mermaid
121
+ classDiagram
122
+ class CachedRouteData {
123
+ createdAt
124
+ href
125
+ lastVisibleAt
126
+ routeId
127
+ staticData
128
+ matchId
129
+ routerSnapshot
130
+ ready
131
+ }
132
+
133
+ class RouterSnapshot {
134
+ static stores
135
+ static matches
136
+ live navigate
137
+ live invalidate
138
+ live preloadRoute
139
+ }
140
+
141
+ CachedRouteData --> RouterSnapshot : uses
142
+ ```
143
+
144
+ | Field | Role |
145
+ | --- | --- |
146
+ | `createdAt` | First time the entry was stored. Used as an eviction fallback. |
147
+ | `href` | Full route href, including search and hash when available. Used for restoration. |
148
+ | `lastVisibleAt` | Last time the entry became visible. Primary eviction timestamp. |
149
+ | `routeId` | TanStack route id. Used by `maxEntriesPerRouteId`. |
150
+ | `staticData` | Route static data. The route is cacheable when `routeCache` is `true`. |
151
+ | `matchId` | Match id used to render the cached route with TanStack Router's `Match`. |
152
+ | `routerSnapshot` | Frozen router-like object used by the cached route tree. |
153
+ | `ready` | Marks that the route has a complete snapshot and can be rendered from cache. |
154
+
155
+ Pathnames are normalized by removing trailing slashes except for `/`, so `/customers/` and `/customers` share one cache key.
156
+
157
+ ## Router snapshot
158
+
159
+ Cached route trees still expect TanStack Router context. Instead of keeping every cached tree connected to the live router stores, the package creates a router snapshot when a route becomes ready.
160
+
161
+ ```mermaid
162
+ flowchart TD
163
+ liveRouter["Live router"]
164
+ matches["Current matches"]
165
+ location["Current location"]
166
+ staticStores["Static stores"]
167
+ liveMethods["Bound live methods"]
168
+ routerSnapshot["Router snapshot"]
169
+ cachedOutlet["CachedOutlet"]
170
+ match["Match by cached matchId"]
171
+
172
+ liveRouter --> matches
173
+ liveRouter --> location
174
+ matches --> staticStores
175
+ location --> staticStores
176
+ liveRouter --> liveMethods
177
+ staticStores --> routerSnapshot
178
+ liveMethods --> routerSnapshot
179
+ routerSnapshot --> cachedOutlet
180
+ cachedOutlet --> match
181
+ ```
182
+
183
+ The snapshot copies the current matches, location, resolved location, and match stores. It also keeps selected live router methods bound to the real router, including `navigate`, `invalidate`, `preloadRoute`, and location builders.
184
+
185
+ This gives hidden cached routes a stable route view while still allowing imperative router actions to call through to the real router.
186
+
187
+ `CachedOutlet` renders that snapshot like this:
188
+
189
+ ```tsx
190
+ <RouterContextProvider router={routerSnapshot}>
191
+ <Match matchId={matchId} />
192
+ </RouterContextProvider>
193
+ ```
194
+
195
+ ## Cache synchronization
196
+
197
+ On every relevant router state change, the cache manager checks the current route state and updates the cache.
198
+
199
+ ```mermaid
200
+ flowchart TD
201
+ start["Router state changed"]
202
+ read["Read pathname, href, matches, status"]
203
+ normalize["Normalize pathnames"]
204
+ staticData["Find deepest routeCache static data"]
205
+ errored{"Current route errored?"}
206
+ resolved{"Match resolved?"}
207
+ cacheable{"routeCache true?"}
208
+ ready{"Match successful?"}
209
+ write["Create or refresh cache entry"]
210
+ deleteEntry["Delete cache entry"]
211
+ wait["Wait for next router update"]
212
+ render["Render cached outlets and maybe live outlet"]
213
+
214
+ start --> read --> normalize --> staticData --> errored
215
+ errored -- yes --> deleteEntry --> render
216
+ errored -- no --> resolved
217
+ resolved -- no --> wait
218
+ resolved -- yes --> cacheable
219
+ cacheable -- no --> deleteEntry
220
+ cacheable -- yes --> ready
221
+ ready -- no --> wait
222
+ ready -- yes --> write --> render
223
+ deleteEntry --> render
224
+ ```
225
+
226
+ The current entry being written is protected during limit enforcement so the route that just became ready is not immediately evicted.
227
+
228
+ ## Visible pathname
229
+
230
+ The manager tracks three pathnames:
231
+
232
+ | Name | Meaning |
233
+ | --- | --- |
234
+ | `routerPathname` | Current router location pathname. |
235
+ | `resolvedPathname` | Resolved router location pathname. |
236
+ | `visiblePathname` | Cached pathname that should currently be shown. |
237
+
238
+ ```mermaid
239
+ flowchart TD
240
+ routerPathname["routerPathname"]
241
+ resolvedPathname["resolvedPathname"]
242
+ readyDestination{"Destination is ready cached route?"}
243
+ ancestor{"Router pathname is ancestor?"}
244
+ useRouter["visiblePathname = routerPathname"]
245
+ useResolved["visiblePathname = resolvedPathname"]
246
+
247
+ routerPathname --> readyDestination
248
+ resolvedPathname --> readyDestination
249
+ readyDestination -- yes --> useRouter
250
+ readyDestination -- no --> ancestor
251
+ ancestor -- yes --> useRouter
252
+ ancestor -- no --> useResolved
253
+ ```
254
+
255
+ Most of the time, `visiblePathname` is the resolved pathname. During some navigations, TanStack Router can temporarily expose a router pathname that differs from the resolved pathname. If the destination is already cached, the package can show it immediately.
256
+
257
+ ## Rendering model
258
+
259
+ Every ready cached entry is rendered through `OffScreen`.
260
+
261
+ ```tsx
262
+ <OffScreen mode={visiblePathname === pathname ? "visible" : "hidden"}>
263
+ <CachedOutlet matchId={route.matchId} routerSnapshot={route.routerSnapshot} />
264
+ </OffScreen>
265
+ ```
266
+
267
+ ```mermaid
268
+ flowchart TD
269
+ routes["CachedRoutes entries"]
270
+ entry["Ready cache entry"]
271
+ offscreen["OffScreen"]
272
+ activity["React Activity"]
273
+ mode{"Visible pathname matches entry?"}
274
+ visible["mode = visible"]
275
+ hidden["mode = hidden"]
276
+ dom["data-router-cache attributes"]
277
+
278
+ routes --> entry --> offscreen --> activity --> mode
279
+ mode -- yes --> visible --> dom
280
+ mode -- no --> hidden --> dom
281
+ ```
282
+
283
+ `OffScreen` uses React `Activity` with either `visible` or `hidden` mode. The route tree remains mounted in both modes. The wrapping element gets these attributes:
284
+
285
+ ```html
286
+ <div
287
+ data-router-cache-container="true"
288
+ data-router-cache-mode="hidden"
289
+ data-router-cache-pathname="/customers"
290
+ >
291
+ ...
292
+ </div>
293
+ ```
294
+
295
+ Those attributes are used by diagnostics and transient UI tracking.
296
+
297
+ ## Scroll and transient UI
298
+
299
+ The hidden route container is not enough for every UI primitive. Popovers, menus, dialogs, tooltips, and command palettes often render into portals outside the route container.
300
+
301
+ ```mermaid
302
+ sequenceDiagram
303
+ participant Route as Visible route
304
+ participant DOM as Document
305
+ participant Tracker as Transient UI tracker
306
+ participant Portal as External portal element
307
+
308
+ Route->>DOM: Opens menu, dialog, tooltip, or popover
309
+ DOM->>Tracker: MutationObserver sees added element
310
+ Tracker->>Tracker: Assign owner pathname
311
+ Route->>Tracker: Route becomes hidden
312
+ Tracker->>Portal: Dispatch hover exit and Escape
313
+ Tracker->>Portal: Set display none, aria-hidden, inert
314
+ Route->>Tracker: Route becomes visible
315
+ Tracker->>Portal: Restore previous DOM state
316
+ ```
317
+
318
+ External elements can opt out of this ownership behavior with:
319
+
320
+ ```html
321
+ <div data-router-cache-persistent-external="true">
322
+ ...
323
+ </div>
324
+ ```
325
+
326
+ The package also stores window scroll positions by pathname. When a cached route becomes visible, it restores the saved window scroll position after two animation frames.
327
+
328
+ ```mermaid
329
+ sequenceDiagram
330
+ participant Route as Cached route
331
+ participant Store as Scroll position map
332
+ participant Window as window
333
+
334
+ Route->>Store: Save scroll when hidden
335
+ Route->>Window: Become visible
336
+ Window-->>Route: Animation frame
337
+ Window-->>Route: Animation frame
338
+ Route->>Window: scrollTo saved x and y
339
+ ```
340
+
341
+ ## Eviction
342
+
343
+ The provider accepts two limits:
344
+
345
+ | Limit | Meaning |
346
+ | --- | --- |
347
+ | `maxEntries` | Maximum cached entries in the provider. |
348
+ | `maxEntriesPerRouteId` | Maximum cached entries for the same TanStack route id. |
349
+
350
+ Invalid, missing, `NaN`, and non-finite limits are normalized to `Infinity`. Negative values are normalized to `0`. `maxEntries={0}` disables caching and clears existing entries.
351
+
352
+ ```mermaid
353
+ flowchart TD
354
+ input["Next CachedRoutes map"]
355
+ disabled{"maxEntries is 0?"}
356
+ protect["Protect current pathname"]
357
+ perRoute["Apply maxEntriesPerRouteId"]
358
+ global["Apply maxEntries"]
359
+ sort["Sort evictable entries by lastVisibleAt, createdAt, pathname"]
360
+ delete["Delete selected entries"]
361
+ output["Bounded CachedRoutes map"]
362
+
363
+ input --> disabled
364
+ disabled -- yes --> delete
365
+ disabled -- no --> protect --> perRoute --> global --> sort --> delete --> output
366
+ ```
367
+
368
+ Limit enforcement runs in this order:
369
+
370
+ 1. Apply `maxEntriesPerRouteId`.
371
+ 2. Apply `maxEntries`.
372
+ 3. Keep protected keys, usually the route currently being written.
373
+ 4. Evict least recently visible entries first.
374
+ 5. If timestamps tie, sort by pathname for deterministic eviction.
375
+
376
+ The timestamp used for eviction is `lastVisibleAt`, then `createdAt`, then `0`.
377
+
378
+ ## Href restoration
379
+
380
+ The cache key is pathname-based, but a route can be cached with a fuller href that includes search params or a hash.
381
+
382
+ ```mermaid
383
+ flowchart TD
384
+ enter["Navigate to cached pathname"]
385
+ cachedHref{"Cached href exists?"}
386
+ bareHref{"Current href is bare pathname?"}
387
+ changed{"Cached href differs?"}
388
+ restore["navigate with replace true and resetScroll false"]
389
+ skip["Do nothing"]
390
+
391
+ enter --> cachedHref
392
+ cachedHref -- no --> skip
393
+ cachedHref -- yes --> bareHref
394
+ bareHref -- no --> skip
395
+ bareHref -- yes --> changed
396
+ changed -- no --> skip
397
+ changed -- yes --> restore
398
+ ```
399
+
400
+ When restoration is needed, the package navigates back to the cached href:
401
+
402
+ ```ts
403
+ router.navigate({
404
+ href: cachedHref,
405
+ replace: true,
406
+ resetScroll: false,
407
+ });
408
+ ```
409
+
410
+ This keeps cached list filters, tabs, anchors, or search state aligned with the retained route view.
411
+
412
+ ## Events and hooks
413
+
414
+ The package uses a singleton internal event bus.
415
+
416
+ ```mermaid
417
+ flowchart LR
418
+ manager["Cache manager"]
419
+ bus["Event bus"]
420
+ active["activeChange"]
421
+ navStart["cachedNavigationStart"]
422
+ navComplete["cachedNavigationComplete"]
423
+ navCancel["cachedNavigationCancel"]
424
+ hooks["Public hooks"]
425
+
426
+ manager --> bus
427
+ bus --> active
428
+ bus --> navStart
429
+ bus --> navComplete
430
+ bus --> navCancel
431
+ active --> hooks
432
+ navStart --> hooks
433
+ navComplete --> hooks
434
+ navCancel --> hooks
435
+ ```
436
+
437
+ | Event | Payload | Used by |
438
+ | --- | --- | --- |
439
+ | `activeChange` | `{ pathname, mode }` | `useRouteCacheActive`, `useRouteCacheEffect`, `useRouteCacheActivity`, `useRouterCache` refreshes. |
440
+ | `cachedNavigationStart` | `{ pathname, startedAt }` | `useRouteCacheNavigation`. |
441
+ | `cachedNavigationComplete` | `{ pathname, startedAt, visibleAt, paintedAt, duration }` | `useRouteCacheNavigation`. |
442
+ | `cachedNavigationCancel` | `{ pathname, startedAt }` | Clears pending cached navigation state. |
443
+
444
+ `cachedNavigationComplete` waits until the cached route is visible and then waits for two animation frames. This makes `duration` closer to "restored and painted" timing rather than just "state changed" timing.
445
+
446
+ ## Error handling
447
+
448
+ Cached errored routes are intentionally discarded.
449
+
450
+ ```mermaid
451
+ flowchart TD
452
+ route["Current route"]
453
+ routerError{"TanStack match status is error?"}
454
+ boundary["useRouteCacheErrorBoundary"]
455
+ retain["Retain errored pathname"]
456
+ deleteEntry["Delete cached entry"]
457
+ live["Render live route or error boundary"]
458
+ release["Release errored pathname on unmount"]
459
+
460
+ route --> routerError
461
+ routerError -- yes --> deleteEntry --> live
462
+ routerError -- no --> live
463
+ boundary --> retain --> deleteEntry
464
+ boundary --> release
465
+ ```
466
+
467
+ There are two error paths:
468
+
469
+ - If TanStack Router reports the current match status as `error`, the cache manager deletes that pathname's cached entry.
470
+ - If an app-level error fallback calls `useRouteCacheErrorBoundary`, the provider increments an errored count for that pathname and deletes its cached entry until the fallback unmounts.
471
+
472
+ While a route is errored, the cache manager bypasses its cached outlet and lets the live route or error boundary render.
473
+
474
+ ## Memory model
475
+
476
+ The package does not clone React component state. It keeps selected route trees mounted and hides them. Memory is bounded by the number and size of cached route trees that remain mounted.
477
+
478
+ ```mermaid
479
+ flowchart LR
480
+ optIn["routeCache opt in"]
481
+ limits["maxEntries and maxEntriesPerRouteId"]
482
+ scope["cacheScopeKey reset"]
483
+ manual["destroy, destroyAll, invalidateWhere"]
484
+ errors["error cleanup"]
485
+ memory["Retained mounted route trees"]
486
+
487
+ optIn --> memory
488
+ limits --> memory
489
+ scope --> memory
490
+ manual --> memory
491
+ errors --> memory
492
+ ```
493
+
494
+ The memory controls are:
495
+
496
+ - route opt-in through `staticData.routeCache`,
497
+ - `maxEntries` for global cache size,
498
+ - `maxEntriesPerRouteId` for dynamic routes,
499
+ - `cacheScopeKey` for tenant, user, workspace, or environment resets,
500
+ - `destroy`, `destroyAll`, and `invalidateWhere` for manual invalidation,
501
+ - automatic deletion when a route stops being cacheable or enters an error state.
502
+
503
+ Dynamic routes are the most important case to bound. For routes such as `/customers/$customerId`, every distinct pathname can retain a separate route tree unless `maxEntriesPerRouteId` or manual invalidation removes older entries.
@@ -0,0 +1,28 @@
1
+ # Cache Behavior
2
+
3
+ - Pathnames are normalized before being stored or removed.
4
+ - `maxEntries={0}` disables caching and clears existing cached routes.
5
+ - Hidden cached routes are rendered in off-screen containers and receive active-change events.
6
+ - Cached dynamic routes can grow memory use if every id is retained; use `maxEntriesPerRouteId` for those routes.
7
+ - If a cached destination is restored with an outdated href, the package navigates back to the cached href with `replace: true` and `resetScroll: false`.
8
+
9
+ ## Eviction
10
+
11
+ `maxEntries` limits the total number of cached route entries managed by one provider.
12
+
13
+ `maxEntriesPerRouteId` limits cached entries for the same TanStack route id. This is useful for dynamic routes where each pathname can produce a separate cached entry, such as `/customers/$customerId`.
14
+
15
+ When a limit is exceeded, the least recently visible cached route is evicted first. If timestamps tie, pathnames are used as a deterministic fallback.
16
+
17
+ ## Scope resets
18
+
19
+ Use `cacheScopeKey` when cached views must not survive a user, tenant, workspace, locale, or environment change.
20
+
21
+ ```tsx
22
+ <RouterCacheProvider cacheScopeKey={workspaceId}>
23
+ <RouterCacheOutlet />
24
+ </RouterCacheProvider>
25
+ ```
26
+
27
+ Changing the scope key clears existing cached route entries for that provider.
28
+
@@ -0,0 +1,41 @@
1
+ # Components
2
+
3
+ ## `RouterCacheProvider`
4
+
5
+ Owns the route cache and exposes cache state to the rest of the package.
6
+
7
+ ```tsx
8
+ <RouterCacheProvider
9
+ cacheScopeKey={tenantId}
10
+ defaultCachedRoutes={{}}
11
+ maxEntries={10}
12
+ maxEntriesPerRouteId={2}
13
+ >
14
+ <RouterCacheOutlet />
15
+ </RouterCacheProvider>
16
+ ```
17
+
18
+ | Prop | Type | Default | Description |
19
+ | --- | --- | --- | --- |
20
+ | `children` | `ReactNode` | Required | The outlet and surrounding UI that can use the cache. |
21
+ | `cacheScopeKey` | `string | number | null` | `"__default__"` | Resets the entire cache when the key changes. Use this for tenant, user, workspace, or environment changes. |
22
+ | `defaultCachedRoutes` | `CachedRoutes` | `{}` | Initial cache data. Most apps do not need this. Entries without `staticData.routeCache: true` are ignored. |
23
+ | `maxEntries` | `number` | `Infinity` | Maximum cached route entries across the provider. `0` disables caching. Non-finite or invalid values are treated as `Infinity`. |
24
+ | `maxEntriesPerRouteId` | `number` | `Infinity` | Maximum cached entries for the same TanStack route id. Useful for dynamic routes such as `/customers/$id`. |
25
+
26
+ When a cache limit is exceeded, the least recently visible cached route is evicted first. If timestamps tie, pathnames are used as a deterministic fallback.
27
+
28
+ ## `RouterCacheOutlet`
29
+
30
+ Renders the live TanStack Router outlet and any hidden cached route views.
31
+
32
+ ```tsx
33
+ <RouterCacheOutlet />
34
+ ```
35
+
36
+ | Prop | Type | Default | Description |
37
+ | --- | --- | --- | --- |
38
+ | `children` | `ReactNode` | `undefined` | Optional content rendered after the outlet manager. |
39
+
40
+ Use one `RouterCacheOutlet` inside a `RouterCacheProvider` for the route branch you want to cache.
41
+
@@ -0,0 +1,31 @@
1
+ # Debugging
2
+
3
+ `RouterCacheOutlet` exposes development diagnostics for the cache.
4
+
5
+ In non-production environments, diagnostics are available at:
6
+
7
+ ```ts
8
+ window.__TANSTACK_ROUTER_CACHE_DEBUG__
9
+ ```
10
+
11
+ ## Debug API
12
+
13
+ | Field | Type | Description |
14
+ | --- | --- | --- |
15
+ | `getSnapshot` | `() => RouterCacheDebugSnapshot` | Returns the last recorded snapshot. |
16
+ | `refresh` | `() => RouterCacheDebugSnapshot` | Recomputes and returns a snapshot. |
17
+ | `lastSnapshot` | `RouterCacheDebugSnapshot | undefined` | Most recent snapshot. |
18
+ | `setWarningThreshold` | `(nextThreshold?: number | null) => void` | Sets a development warning threshold for cached route count. |
19
+ | `warningThreshold` | `number | null | undefined` | Current warning threshold. |
20
+
21
+ ## Snapshot fields
22
+
23
+ | Field | Type | Description |
24
+ | --- | --- | --- |
25
+ | `totalCachedRouteCount` | `number` | Total cached route count. |
26
+ | `cachedRoutePathnames` | `string[]` | Cached route pathnames. |
27
+ | `dynamicLookingRouteCount` | `number` | Count of cached routes with id-like dynamic segments. |
28
+ | `dynamicLookingRoutePathnames` | `string[]` | Cached pathnames that look dynamic. |
29
+ | `hiddenCachedRouteCount` | `number` | Cached routes that are not currently visible. |
30
+ | `hiddenContainerCount` | `number` | Hidden DOM containers managed by the package. |
31
+ | `visiblePathname` | `string` | Current visible cached pathname. |