@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.
@@ -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
- type: core
9
- library: tanstack-router
10
- library_version: '1.166.2'
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.166.2 and `@tanstack/react-router` v1.166.2. APIs are stable. Splat routes use `$` (not `*`); the `*` compat alias will be removed in v2.
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: router-core/auth-and-guards
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
- type: sub-skill
10
- library: tanstack-router
11
- library_version: '1.166.2'
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 by direct POST regardless of which route renders the calling UI. An attacker doesn't have to load `/_authenticated/orders` — they can curl the RPC endpoint directly.
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: router-core/code-splitting
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
- type: sub-skill
9
- library: tanstack-router
10
- library_version: '1.166.2'
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: router-core/data-loading
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
- type: sub-skill
10
- library: tanstack-router
11
- library_version: '1.166.2'
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: () => fetch('/api/posts').then((r) => r.json()),
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 fetch('/api/posts', { method: 'POST', body: '...' })
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
- For synchronous invalidation (wait until loaders finish):
304
+ Use `await router.invalidate({ sync: true })` when the next step requires refreshed loader data.
288
305
 
289
- ```tsx
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 loaders run in the browser, use fetch or API calls
379
+ // CORRECT for an SPA use a client-safe API helper
365
380
  export const Route = createFileRoute('/posts')({
366
- loader: async () => {
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 unless you are using TanStack Start server functions.
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: router-core/navigation
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
- type: sub-skill
10
- library: tanstack-router
11
- library_version: '1.166.2'
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: router-core/not-found-and-errors
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
- type: sub-skill
9
- library: tanstack-router
10
- library_version: '1.166.2'
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: router-core/path-params
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
- type: sub-skill
9
- library: tanstack-router
10
- library_version: '1.166.2'
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: router-core/search-params
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
- type: sub-skill
9
- library: tanstack-router
10
- library_version: '1.166.2'
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: router-core/ssr
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
- type: sub-skill
11
- library: tanstack-router
12
- library_version: '1.166.2'
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: router-core/type-safety
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
- type: sub-skill
10
- library: tanstack-router
11
- library_version: '1.166.2'
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 on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
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 a preload strategy is set, this delays the preload by this many milliseconds.
680
- * If the user exits the link before this delay, the preload will be cancelled.
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
  /**