@tanstack/router-core 1.171.16-pre.0 → 1.171.16
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.
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +5 -3
- package/dist/cjs/load-client.cjs +169 -246
- package/dist/cjs/load-client.cjs.map +1 -1
- package/dist/cjs/load-client.d.cts +10 -39
- package/dist/cjs/router.cjs +42 -22
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +14 -10
- package/dist/esm/link.d.ts +5 -3
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/load-client.d.ts +10 -39
- package/dist/esm/load-client.js +171 -247
- package/dist/esm/load-client.js.map +1 -1
- package/dist/esm/router.d.ts +14 -10
- package/dist/esm/router.js +44 -23
- package/dist/esm/router.js.map +1 -1
- package/package.json +1 -1
- package/skills/router-core/SKILL.md +23 -4
- package/skills/router-core/auth-and-guards/SKILL.md +10 -10
- package/skills/router-core/code-splitting/SKILL.md +5 -4
- package/skills/router-core/data-loading/SKILL.md +30 -18
- package/skills/router-core/navigation/SKILL.md +5 -4
- package/skills/router-core/not-found-and-errors/SKILL.md +5 -4
- package/skills/router-core/path-params/SKILL.md +5 -4
- package/skills/router-core/search-params/SKILL.md +5 -4
- package/skills/router-core/ssr/SKILL.md +5 -4
- package/skills/router-core/type-safety/SKILL.md +11 -4
- package/src/link.ts +5 -3
- package/src/load-client.ts +582 -632
- package/src/router.ts +87 -44
|
@@ -5,9 +5,10 @@ description: >-
|
|
|
5
5
|
createRouter, createRoute, createRootRoute, createRootRouteWithContext,
|
|
6
6
|
addChildren, Register type declaration, route matching, route sorting,
|
|
7
7
|
file naming conventions. Entry point for all router skills.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
metadata:
|
|
9
|
+
type: core
|
|
10
|
+
library: tanstack-router
|
|
11
|
+
library_version: '1.171.15'
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
# TanStack Router Core
|
|
@@ -18,6 +19,8 @@ TanStack Router is a type-safe router for React and Solid with built-in SWR cach
|
|
|
18
19
|
|
|
19
20
|
> **CRITICAL**: TanStack Router is CLIENT-FIRST. Loaders run on the client by default, NOT server-only like Remix/Next.js. Do not confuse TanStack Router APIs with Next.js or React Router.
|
|
20
21
|
|
|
22
|
+
Use this entry skill to choose one primary sub-skill. Do not load the full catalog. Load a second sub-skill only when the task crosses a real boundary, such as an authenticated loader that needs both `auth-and-guards` and `data-loading`.
|
|
23
|
+
|
|
21
24
|
## Sub-Skills
|
|
22
25
|
|
|
23
26
|
| Task | Sub-Skill |
|
|
@@ -66,6 +69,22 @@ Need server-side rendering?
|
|
|
66
69
|
→ router-core/ssr
|
|
67
70
|
```
|
|
68
71
|
|
|
72
|
+
## Cross-Cutting Completion Checks
|
|
73
|
+
|
|
74
|
+
For route refactors:
|
|
75
|
+
|
|
76
|
+
1. Rename or move the route file; do not hand-edit the generated `createFileRoute` path.
|
|
77
|
+
2. Regenerate `routeTree.gen.ts` with the configured Router plugin or CLI.
|
|
78
|
+
3. Update links, redirects, `from` narrowing, params, and tests that reference the old route.
|
|
79
|
+
4. Run type tests and a production build. A typecheck alone does not prove route generation or bundling works.
|
|
80
|
+
|
|
81
|
+
For response schema changes:
|
|
82
|
+
|
|
83
|
+
1. Update the source model and shared validation schema.
|
|
84
|
+
2. Update the server function or API serializer so the field exists at runtime.
|
|
85
|
+
3. Update loader and component consumers without casts.
|
|
86
|
+
4. Assert the actual response payload in a unit or integration test. Typechecking cannot catch a serializer that omits the new field.
|
|
87
|
+
|
|
69
88
|
## Minimal Working Example
|
|
70
89
|
|
|
71
90
|
```tsx
|
|
@@ -136,4 +155,4 @@ The plugin auto-generates this string. If you rename a route file, the plugin up
|
|
|
136
155
|
|
|
137
156
|
## Version Note
|
|
138
157
|
|
|
139
|
-
This skill targets `@tanstack/router-core` v1.
|
|
158
|
+
This skill targets `@tanstack/router-core` v1.171.15. Splat routes use `$` (not `*`); the `*` compat alias will be removed in v2.
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: auth-and-guards
|
|
3
3
|
description: >-
|
|
4
4
|
Route protection with beforeLoad, redirect()/throw redirect(),
|
|
5
5
|
isRedirect helper, authenticated layout routes (_authenticated),
|
|
6
6
|
non-redirect auth (inline login), RBAC with roles and permissions,
|
|
7
7
|
auth provider integration (Auth0, Clerk, Supabase), router context
|
|
8
8
|
for auth state.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
metadata:
|
|
10
|
+
type: sub-skill
|
|
11
|
+
library: tanstack-router
|
|
12
|
+
library_version: '1.171.15'
|
|
12
13
|
requires:
|
|
13
14
|
- router-core
|
|
14
|
-
- router-core/data-loading
|
|
15
15
|
sources:
|
|
16
16
|
- TanStack/router:docs/router/guide/authenticated-routes.md
|
|
17
17
|
- TanStack/router:docs/router/how-to/setup-authentication.md
|
|
@@ -377,7 +377,7 @@ export const Route = createFileRoute('/_authenticated')({
|
|
|
377
377
|
|
|
378
378
|
### CRITICAL: Route guards do not protect server functions
|
|
379
379
|
|
|
380
|
-
A `beforeLoad` redirect protects the **route's UI**, not the **server functions** declared on it. `createServerFn` produces an RPC endpoint reachable
|
|
380
|
+
A `beforeLoad` redirect protects the **route's UI**, not the **server functions** declared on it. `createServerFn` produces an RPC endpoint reachable directly with its declared HTTP method regardless of which route renders the calling UI. An attacker doesn't have to load `/_authenticated/orders` — they can call this GET RPC endpoint directly.
|
|
381
381
|
|
|
382
382
|
```tsx
|
|
383
383
|
// WRONG — handler has no auth check; the route guard doesn't help
|
|
@@ -410,6 +410,10 @@ const getMyOrders = createServerFn({ method: 'GET' })
|
|
|
410
410
|
|
|
411
411
|
Rule of thumb: every `createServerFn`, server route, or API endpoint that touches user data needs `authMiddleware` (or an equivalent in-handler check). The route guard is for the page experience; the endpoint guard is for the data. See [start-core/auth-server-primitives](../../../../start-client-core/skills/start-core/auth-server-primitives/SKILL.md) for the full session/middleware pattern.
|
|
412
412
|
|
|
413
|
+
### CRITICAL: The anonymous destination can still disclose protected data
|
|
414
|
+
|
|
415
|
+
Protect the entire anonymous response, not only the API call. A public login or unauthorized page still leaks data if its title, copy, search params, or serialized loader state names the protected user, tenant, record, or resource. Test a direct anonymous request and follow redirects. Assert that the handler rejects before reading private data, no protected loader runs, the final HTML and serialized state contain no protected identity, and the redirect contains only a sanitized relative return URL.
|
|
416
|
+
|
|
413
417
|
### HIGH: Auth check in component instead of beforeLoad
|
|
414
418
|
|
|
415
419
|
Component-level auth checks cause a **flash of protected content** before the redirect:
|
|
@@ -435,8 +439,6 @@ export const Route = createFileRoute('/_authenticated/dashboard')({
|
|
|
435
439
|
})
|
|
436
440
|
```
|
|
437
441
|
|
|
438
|
-
`beforeLoad` runs before any component rendering and before the loader. It completely prevents the flash.
|
|
439
|
-
|
|
440
442
|
### HIGH: Not re-throwing redirects in try/catch
|
|
441
443
|
|
|
442
444
|
`redirect()` works by throwing. If `beforeLoad` has a try/catch, the redirect gets swallowed:
|
|
@@ -490,8 +492,6 @@ export const Route = createFileRoute('/_authenticated')({
|
|
|
490
492
|
|
|
491
493
|
Place protected routes as children of the `_authenticated` layout route. Public routes (login, home, etc.) live outside it.
|
|
492
494
|
|
|
493
|
-
---
|
|
494
|
-
|
|
495
495
|
## Cross-References
|
|
496
496
|
|
|
497
497
|
- See also: **router-core/data-loading/SKILL.md** — `beforeLoad` runs before `loader`; auth context flows into loader via route context
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: code-splitting
|
|
3
3
|
description: >-
|
|
4
4
|
Automatic code splitting (autoCodeSplitting), .lazy.tsx convention,
|
|
5
5
|
createLazyFileRoute, createLazyRoute, lazyRouteComponent, getRouteApi
|
|
6
6
|
for typed hooks in split files, codeSplitGroupings per-route override,
|
|
7
7
|
splitBehavior programmatic config, critical vs non-critical properties.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
metadata:
|
|
9
|
+
type: sub-skill
|
|
10
|
+
library: tanstack-router
|
|
11
|
+
library_version: '1.171.15'
|
|
11
12
|
requires:
|
|
12
13
|
- router-core
|
|
13
14
|
sources:
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: data-loading
|
|
3
3
|
description: >-
|
|
4
4
|
Route loader option, loaderDeps for cache keys, staleTime/gcTime/
|
|
5
5
|
defaultPreloadStaleTime SWR caching, pendingComponent/pendingMs/
|
|
6
6
|
pendingMinMs, errorComponent/onError/onCatch, beforeLoad, router
|
|
7
7
|
context and createRootRouteWithContext DI pattern, router.invalidate,
|
|
8
8
|
Await component, deferred data loading with unawaited promises.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
metadata:
|
|
10
|
+
type: sub-skill
|
|
11
|
+
library: tanstack-router
|
|
12
|
+
library_version: '1.171.15'
|
|
12
13
|
requires:
|
|
13
14
|
- router-core
|
|
14
15
|
sources:
|
|
@@ -216,13 +217,29 @@ Route-level context via `beforeLoad`:
|
|
|
216
217
|
|
|
217
218
|
```tsx
|
|
218
219
|
export const Route = createFileRoute('/posts')({
|
|
219
|
-
beforeLoad: () => ({
|
|
220
|
-
fetchPosts:
|
|
220
|
+
beforeLoad: ({ context }) => ({
|
|
221
|
+
fetchPosts: context.fetchPosts,
|
|
221
222
|
}),
|
|
222
223
|
loader: ({ context: { fetchPosts } }) => fetchPosts(),
|
|
223
224
|
})
|
|
224
225
|
```
|
|
225
226
|
|
|
227
|
+
Keep the implementation SSR-safe when the router is used by TanStack Start. A relative `fetch('/api/posts')` works in a browser event handler, but Node and many server runtimes require an absolute URL during SSR. For app-internal data in Start, call a server function from the loader:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
231
|
+
|
|
232
|
+
const getPosts = createServerFn({ method: 'GET' }).handler(() => {
|
|
233
|
+
return db.posts.findMany()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
export const Route = createFileRoute('/posts')({
|
|
237
|
+
loader: () => getPosts(),
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Use a server route plus an origin-derived absolute URL only when the HTTP boundary itself is required. Do not hard-code the production origin.
|
|
242
|
+
|
|
226
243
|
### Deferred Data Loading
|
|
227
244
|
|
|
228
245
|
Return unawaited promises from the loader for non-critical data. Use the `Await` component to render them:
|
|
@@ -276,19 +293,17 @@ function AddPostButton() {
|
|
|
276
293
|
const router = useRouter()
|
|
277
294
|
|
|
278
295
|
const handleAdd = async () => {
|
|
279
|
-
await
|
|
280
|
-
router.invalidate()
|
|
296
|
+
await createPost({ title: 'New post' })
|
|
297
|
+
await router.invalidate({ sync: true })
|
|
281
298
|
}
|
|
282
299
|
|
|
283
300
|
return <button onClick={handleAdd}>Add Post</button>
|
|
284
301
|
}
|
|
285
302
|
```
|
|
286
303
|
|
|
287
|
-
|
|
304
|
+
Use `await router.invalidate({ sync: true })` when the next step requires refreshed loader data.
|
|
288
305
|
|
|
289
|
-
|
|
290
|
-
await router.invalidate({ sync: true })
|
|
291
|
-
```
|
|
306
|
+
Treat the mutation and invalidation as one workflow. The mutation must persist before invalidation starts, and the loader must read from the same authoritative store. Verify create, update, and delete through the rendered route, including a fresh reload; local component state can hide a stale loader or non-persistent write.
|
|
292
307
|
|
|
293
308
|
### Error Handling
|
|
294
309
|
|
|
@@ -361,16 +376,13 @@ export const Route = createFileRoute('/posts')({
|
|
|
361
376
|
},
|
|
362
377
|
})
|
|
363
378
|
|
|
364
|
-
// CORRECT
|
|
379
|
+
// CORRECT for an SPA — use a client-safe API helper
|
|
365
380
|
export const Route = createFileRoute('/posts')({
|
|
366
|
-
loader:
|
|
367
|
-
const res = await fetch('/api/posts')
|
|
368
|
-
return res.json()
|
|
369
|
-
},
|
|
381
|
+
loader: () => fetchPosts(),
|
|
370
382
|
})
|
|
371
383
|
```
|
|
372
384
|
|
|
373
|
-
Do NOT put database queries, filesystem access, or server-only code in loaders
|
|
385
|
+
Do NOT put database queries, filesystem access, or server-only code directly in loaders. In TanStack Start, put that work in a server function and call the function from the loader. Do not use a relative `fetch('/api/...')` in an SSR loader.
|
|
374
386
|
|
|
375
387
|
### MEDIUM: Not understanding staleTime default is 0
|
|
376
388
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: navigation
|
|
3
3
|
description: >-
|
|
4
4
|
Link component, useNavigate, Navigate component, router.navigate,
|
|
5
5
|
ToOptions/NavigateOptions/LinkOptions, from/to relative navigation,
|
|
6
6
|
activeOptions/activeProps, preloading (intent/viewport/render),
|
|
7
7
|
preloadDelay, navigation blocking (useBlocker, Block), createLink,
|
|
8
8
|
linkOptions helper, scroll restoration, MatchRoute.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
metadata:
|
|
10
|
+
type: sub-skill
|
|
11
|
+
library: tanstack-router
|
|
12
|
+
library_version: '1.171.15'
|
|
12
13
|
requires:
|
|
13
14
|
- router-core
|
|
14
15
|
sources:
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: not-found-and-errors
|
|
3
3
|
description: >-
|
|
4
4
|
notFound() function, notFoundComponent, defaultNotFoundComponent,
|
|
5
5
|
notFoundMode (fuzzy/root), errorComponent, CatchBoundary,
|
|
6
6
|
CatchNotFound, isNotFound, NotFoundRoute (deprecated), route
|
|
7
7
|
masking (mask option, createRouteMask, unmaskOnReload).
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
metadata:
|
|
9
|
+
type: sub-skill
|
|
10
|
+
library: tanstack-router
|
|
11
|
+
library_version: '1.171.15'
|
|
11
12
|
requires:
|
|
12
13
|
- router-core
|
|
13
14
|
sources:
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: path-params
|
|
3
3
|
description: >-
|
|
4
4
|
Dynamic path segments ($paramName), splat routes ($ / _splat),
|
|
5
5
|
optional params ({-$paramName}), prefix/suffix patterns ({$param}.ext),
|
|
6
6
|
useParams, params.parse/stringify, pathParamsAllowedCharacters,
|
|
7
7
|
i18n locale patterns.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
metadata:
|
|
9
|
+
type: sub-skill
|
|
10
|
+
library: tanstack-router
|
|
11
|
+
library_version: '1.171.15'
|
|
11
12
|
requires:
|
|
12
13
|
- router-core
|
|
13
14
|
sources:
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: search-params
|
|
3
3
|
description: >-
|
|
4
4
|
validateSearch, search param validation with Zod/Valibot/ArkType adapters,
|
|
5
5
|
fallback(), search middlewares (retainSearchParams, stripSearchParams),
|
|
6
6
|
custom serialization (parseSearch, stringifySearch), search param
|
|
7
7
|
inheritance, loaderDeps for cache keys, reading and writing search params.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
metadata:
|
|
9
|
+
type: sub-skill
|
|
10
|
+
library: tanstack-router
|
|
11
|
+
library_version: '1.171.15'
|
|
11
12
|
requires:
|
|
12
13
|
- router-core
|
|
13
14
|
sources:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: ssr
|
|
3
3
|
description: >-
|
|
4
4
|
Non-streaming and streaming SSR, RouterClient/RouterServer,
|
|
5
5
|
renderRouterToString/renderRouterToStream, createRequestHandler,
|
|
@@ -7,9 +7,10 @@ description: >-
|
|
|
7
7
|
components, head route option (meta/links/styles/scripts),
|
|
8
8
|
ScriptOnce, automatic loader dehydration/hydration, memory
|
|
9
9
|
history on server, data serialization, document head management.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
metadata:
|
|
11
|
+
type: sub-skill
|
|
12
|
+
library: tanstack-router
|
|
13
|
+
library_version: '1.171.15'
|
|
13
14
|
requires:
|
|
14
15
|
- router-core
|
|
15
16
|
- router-core/data-loading
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: type-safety
|
|
3
3
|
description: >-
|
|
4
4
|
Full type inference philosophy (never cast, never annotate inferred
|
|
5
5
|
values), Register module declaration, from narrowing on hooks and
|
|
6
6
|
Link, strict:false for shared components, getRouteApi for code-split
|
|
7
7
|
typed access, addChildren with object syntax for TS perf, LinkProps
|
|
8
8
|
and ValidateLinkOptions type utilities, as const satisfies pattern.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
metadata:
|
|
10
|
+
type: sub-skill
|
|
11
|
+
library: tanstack-router
|
|
12
|
+
library_version: '1.171.15'
|
|
12
13
|
requires:
|
|
13
14
|
- router-core
|
|
14
15
|
sources:
|
|
@@ -489,4 +490,10 @@ const search = Route.useSearch()
|
|
|
489
490
|
|
|
490
491
|
If a build error mentions `react-router-dom`, `next/`, `pages/_app`, or duplicate `/` routes, fix the import — don't paper over with type assertions.
|
|
491
492
|
|
|
493
|
+
### 6. CRITICAL: Treating typecheck as proof of runtime schema propagation
|
|
494
|
+
|
|
495
|
+
Types can say a field exists while a database projection, API serializer, or server function omits it. When adding or renaming a field, trace the value through storage, validation, handler output, loader data, and rendered UI. Do not cast the response to the desired type.
|
|
496
|
+
Add a runtime assertion against the real handler or serialized response, such as `expect(await getOrder({ data: { id } })).toMatchObject({ totalCents: 2599 })`.
|
|
497
|
+
Then run the route-level test and production build. The type test remains necessary, but it is not the runtime contract test.
|
|
498
|
+
|
|
492
499
|
See also: router-core (Register setup), router-core/navigation (from narrowing), router-core/code-splitting (getRouteApi).
|
package/src/link.ts
CHANGED
|
@@ -671,13 +671,15 @@ export interface LinkOptionsProps {
|
|
|
671
671
|
/**
|
|
672
672
|
* The preloading strategy for this link
|
|
673
673
|
* - `false` - No preloading
|
|
674
|
-
* - `'intent'` - Preload the linked route
|
|
674
|
+
* - `'intent'` - Preload the linked route when the user focuses, hovers over, or touches the link
|
|
675
675
|
* - `'viewport'` - Preload the linked route when it enters the viewport
|
|
676
|
+
* - `'render'` - Preload the linked route as soon as it renders
|
|
676
677
|
*/
|
|
677
678
|
preload?: false | 'intent' | 'viewport' | 'render'
|
|
678
679
|
/**
|
|
679
|
-
* When
|
|
680
|
-
*
|
|
680
|
+
* When the intent preload strategy is set, this delays focus and hover
|
|
681
|
+
* preloading by this many milliseconds. Touch intent preloads immediately.
|
|
682
|
+
* If focus or hover exits before this delay, the preload will be cancelled.
|
|
681
683
|
*/
|
|
682
684
|
preloadDelay?: number
|
|
683
685
|
/**
|