@hitch42/applet 0.1.0-beta.9 → 0.1.1-beta.10

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.
Files changed (164) hide show
  1. package/dist/_internal/client.js +120 -11
  2. package/dist/_internal/log-context.d.ts +34 -0
  3. package/dist/_internal/log-context.js +2 -0
  4. package/dist/_internal/server.js +112 -57
  5. package/dist/applet.js +12 -12
  6. package/dist/build-Bh_S5BUv.js +2 -0
  7. package/dist/{build-CAb26kMY.js → build-CEfFoIZZ.js} +1 -1
  8. package/dist/{ci-5qWl7lT3.js → ci-BKw14X2h.js} +14 -13
  9. package/dist/client.d.ts +2 -2
  10. package/dist/{contract-rP_eV9PK.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  11. package/dist/{deploy-D_OFJ6-Q.js → deploy-wGYjwXrx.js} +139 -88
  12. package/dist/{generate-CBPnHoqX.js → generate-BEIYs17x.js} +54 -22
  13. package/dist/generate-Dox-OYt6.js +2 -0
  14. package/dist/headers-DkNr9MLJ.js +17 -0
  15. package/dist/index.d.ts +3 -2
  16. package/dist/index.js +2 -1
  17. package/dist/{init-tfzwuYnk.js → init-6oGPM46R.js} +10 -15
  18. package/dist/{load-config-BYSxDVAl.js → load-config-xEdTXs0y.js} +2 -2
  19. package/dist/log-context-zu8U9Sjm.js +73 -0
  20. package/dist/{login-C7RKmmTe.js → login-CUx3TmFQ.js} +1 -1
  21. package/dist/{logs-DgjgAxj2.js → logs-DSjuEN0Q.js} +20 -22
  22. package/dist/{manifest-DD_SGkS6.d.ts → manifest-C6_F4bL8.d.ts} +5 -1
  23. package/dist/{manifest-Bp87Uz0a.js → manifest-CJSL9GuC.js} +20 -4
  24. package/dist/{project-D6XHllmC.js → project-BRZGd6xq.js} +197 -16
  25. package/dist/query-cache-CXmAyLv3.js +82 -0
  26. package/dist/react.d.ts +2 -1
  27. package/dist/react.js +59 -39
  28. package/dist/{schedules-dhU1yyFr.js → schedules-C8_Svr4h.js} +1 -1
  29. package/dist/server.d.ts +8 -3
  30. package/dist/server.js +29 -8
  31. package/dist/telephony.d.ts +24 -4
  32. package/dist/telephony.js +135 -16
  33. package/dist/{variables-Bwnr7oxf.js → variables-DT5RyHdH.js} +13 -13
  34. package/dist/vite.d.ts +3 -60
  35. package/dist/vite.js +50 -15
  36. package/dist/{workflow-engine-MogejNfF.js → workflow-engine-BOTzURKM.js} +1 -7
  37. package/dist/{workspace-XVtM12ZC.js → workspace-D0dnnlxL.js} +33 -38
  38. package/package.json +13 -6
  39. package/scaffold/.agents/skills/hitch-applets/SKILL.md +29 -3
  40. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +8 -0
  41. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  42. package/scaffold/.agents/skills/hitch-applets/references/records.md +2 -0
  43. package/scaffold/.agents/skills/hitch-data/SKILL.md +2 -1
  44. package/scaffold/.agents/skills/hitch-data/references/attributes.md +7 -1
  45. package/scaffold/.agents/skills/hitch-data/references/limits.md +13 -13
  46. package/scaffold/.agents/skills/hitch-theory/SKILL.md +3 -2
  47. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  48. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  49. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  50. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  51. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  52. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  53. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  54. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  55. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  56. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  57. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  58. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  59. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  60. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  103. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  104. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  105. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  106. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  107. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  108. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  109. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  110. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  111. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  112. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  113. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  114. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  115. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  116. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  133. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  134. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  135. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  136. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  137. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  138. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  139. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  140. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  141. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  142. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  143. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  144. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  145. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  146. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  148. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  149. package/scaffold/.github/workflows/hitch.yml +1 -1
  150. package/scaffold/AGENTS.md +2 -2
  151. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  152. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  153. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  154. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  155. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  156. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  157. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  158. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  159. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  160. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  161. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  162. package/scaffold/applet/src/client/styles/globals.css +2 -0
  163. package/dist/build-BfbTbt_r.js +0 -2
  164. package/dist/generate-BgepN4-b.js +0 -2
@@ -0,0 +1,493 @@
1
+ ---
2
+ name: type-safety
3
+ description: >-
4
+ Full type inference philosophy (never cast, never annotate inferred
5
+ values), Register module declaration, from narrowing on hooks and
6
+ Link, strict:false for shared components, getRouteApi for code-split
7
+ typed access, addChildren with object syntax for TS perf, LinkProps
8
+ and ValidateLinkOptions type utilities, as const satisfies pattern.
9
+ metadata:
10
+ type: sub-skill
11
+ library: tanstack-router
12
+ library_version: "1.171.15"
13
+ requires:
14
+ - router-core
15
+ sources:
16
+ - TanStack/router:docs/router/guide/type-safety.md
17
+ - TanStack/router:docs/router/guide/type-utilities.md
18
+ - TanStack/router:docs/router/guide/render-optimizations.md
19
+ ---
20
+
21
+ # Type Safety
22
+
23
+ TanStack Router is FULLY type-inferred. Params, search params, context, and loader data all flow through the route tree automatically. The **#1 AI agent mistake** is adding type annotations, casts, or generic parameters to values that are already inferred.
24
+
25
+ > **CRITICAL**: NEVER use `as Type`, explicit generic params, `satisfies` on hook returns, or type annotations on inferred values. Every cast masks real type errors and breaks the inference chain.
26
+ > **CRITICAL**: Do NOT confuse TanStack Router with Next.js or React Router. There is no `getServerSideProps`, no `useSearchParams()`, no `useLoaderData()` from `react-router-dom`.
27
+
28
+ ## The ONE Required Type Annotation: Register
29
+
30
+ Without this, top-level exports like `Link`, `useNavigate`, `useSearch` have no type safety.
31
+
32
+ ```tsx
33
+ // src/router.tsx
34
+ import { createRouter } from "@tanstack/react-router";
35
+ import { routeTree } from "./routeTree.gen";
36
+
37
+ const router = createRouter({ routeTree });
38
+
39
+ // THIS IS REQUIRED — the single type registration for the entire app
40
+ declare module "@tanstack/react-router" {
41
+ interface Register {
42
+ router: typeof router;
43
+ }
44
+ }
45
+
46
+ export default router;
47
+ ```
48
+
49
+ After registration, every `Link`, `useNavigate`, `useSearch`, `useParams` across the app is fully typed.
50
+
51
+ ## Types Flow Automatically
52
+
53
+ ### Route Hooks — No Annotation Needed
54
+
55
+ ```tsx
56
+ // src/routes/posts.$postId.tsx
57
+ import { createFileRoute } from "@tanstack/react-router";
58
+
59
+ export const Route = createFileRoute("/posts/$postId")({
60
+ validateSearch: (search: Record<string, unknown>) => ({
61
+ page: Number(search.page ?? 1),
62
+ }),
63
+ loader: async ({ params }) => {
64
+ // params.postId is already typed as string — do not annotate
65
+ const post = await fetchPost(params.postId);
66
+ return { post };
67
+ },
68
+ component: PostComponent,
69
+ });
70
+
71
+ function PostComponent() {
72
+ // ALL of these are fully inferred — do NOT add type annotations
73
+ const { postId } = Route.useParams();
74
+ // ^? string
75
+
76
+ const { page } = Route.useSearch();
77
+ // ^? number
78
+
79
+ const { post } = Route.useLoaderData();
80
+ // ^? { id: string; title: string; body: string }
81
+
82
+ return (
83
+ <div>
84
+ <h1>{post.title}</h1>
85
+ <p>Page {page}</p>
86
+ </div>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ### Context Flows Through the Tree
92
+
93
+ ```tsx
94
+ // src/routes/__root.tsx
95
+ import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
96
+
97
+ interface RouterContext {
98
+ auth: { userId: string; role: "admin" | "user" } | null;
99
+ }
100
+
101
+ // Note: createRootRouteWithContext is a FACTORY — call it TWICE: ()()
102
+ export const Route = createRootRouteWithContext<RouterContext>()({
103
+ component: () => <Outlet />,
104
+ });
105
+ ```
106
+
107
+ ```tsx
108
+ // src/routes/dashboard.tsx
109
+ import { createFileRoute, redirect } from "@tanstack/react-router";
110
+
111
+ export const Route = createFileRoute("/dashboard")({
112
+ beforeLoad: ({ context }) => {
113
+ // context.auth is already typed as { userId: string; role: 'admin' | 'user' } | null
114
+ // NO annotation needed
115
+ if (!context.auth) throw redirect({ to: "/login" });
116
+ return { user: context.auth };
117
+ },
118
+ loader: ({ context }) => {
119
+ // context.user is typed as { userId: string; role: 'admin' | 'user' }
120
+ // This was added by beforeLoad above — fully inferred
121
+ return fetchDashboard(context.user.userId);
122
+ },
123
+ component: DashboardComponent,
124
+ });
125
+
126
+ function DashboardComponent() {
127
+ const data = Route.useLoaderData();
128
+ const { user } = Route.useRouteContext();
129
+ return <h1>Welcome {user.userId}</h1>;
130
+ }
131
+ ```
132
+
133
+ ## Narrowing with `from`
134
+
135
+ Without `from`, hooks return a union of ALL routes' types — slow for TypeScript and imprecise.
136
+
137
+ ### On Hooks
138
+
139
+ ```tsx
140
+ import { useSearch, useParams, useNavigate } from "@tanstack/react-router";
141
+
142
+ function PostSidebar() {
143
+ // WRONG — search is a union of ALL routes' search params
144
+ const search = useSearch();
145
+
146
+ // CORRECT — search is narrowed to /posts/$postId's search params
147
+ const search = useSearch({ from: "/posts/$postId" });
148
+ // ^? { page: number }
149
+
150
+ // CORRECT — params narrowed to this route
151
+ const { postId } = useParams({ from: "/posts/$postId" });
152
+
153
+ // CORRECT — navigate narrowed for relative paths
154
+ const navigate = useNavigate({ from: "/posts/$postId" });
155
+ }
156
+ ```
157
+
158
+ ### On `Link`
159
+
160
+ ```tsx
161
+ import { Link } from '@tanstack/react-router'
162
+
163
+ // WRONG — search resolves to union of ALL routes' search params, slow TS check
164
+ <Link to=".." search={{ page: 0 }} />
165
+
166
+ // CORRECT — narrowed, fast TS check
167
+ <Link from="/posts/$postId" to=".." search={{ page: 0 }} />
168
+
169
+ // Also correct — Route.fullPath in route components
170
+ <Link from={Route.fullPath} to=".." search={{ page: 0 }} />
171
+ ```
172
+
173
+ ## Shared Components: `strict: false`
174
+
175
+ When a component is used across multiple routes, use `strict: false` instead of `from`:
176
+
177
+ ```tsx
178
+ import { useSearch } from "@tanstack/react-router";
179
+
180
+ function GlobalSearch() {
181
+ // Returns union of all routes' search params — no runtime error if route doesn't match
182
+ const search = useSearch({ strict: false });
183
+ return <span>Query: {search.q ?? ""}</span>;
184
+ }
185
+ ```
186
+
187
+ ## Code-Split Files: `getRouteApi`
188
+
189
+ Use `getRouteApi` instead of importing `Route` to avoid pulling route config into the lazy chunk:
190
+
191
+ ```tsx
192
+ // src/routes/posts.lazy.tsx
193
+ import { createLazyFileRoute, getRouteApi } from "@tanstack/react-router";
194
+
195
+ const routeApi = getRouteApi("/posts");
196
+
197
+ export const Route = createLazyFileRoute("/posts")({
198
+ component: PostsComponent,
199
+ });
200
+
201
+ function PostsComponent() {
202
+ const data = routeApi.useLoaderData();
203
+ const { page } = routeApi.useSearch();
204
+ return <div>Page {page}</div>;
205
+ }
206
+ ```
207
+
208
+ ## TypeScript Performance
209
+
210
+ ### Use Object Syntax for `addChildren` in Large Route Trees
211
+
212
+ ```tsx
213
+ // SLOWER — tuple syntax
214
+ const routeTree = rootRoute.addChildren([
215
+ postsRoute.addChildren([postRoute, postsIndexRoute]),
216
+ indexRoute,
217
+ ]);
218
+
219
+ // FASTER — object syntax (TS checks objects faster than large tuples)
220
+ const routeTree = rootRoute.addChildren({
221
+ postsRoute: postsRoute.addChildren({ postRoute, postsIndexRoute }),
222
+ indexRoute,
223
+ });
224
+ ```
225
+
226
+ With file-based routing the route tree is generated, so this is handled for you.
227
+
228
+ ### Avoid Returning Unused Inferred Types from Loaders
229
+
230
+ When using external caches like TanStack Query, don't let the router infer complex return types you never consume:
231
+
232
+ ```tsx
233
+ // SLOWER — TS infers the full ensureQueryData return type into the route tree
234
+ export const Route = createFileRoute("/posts/$postId")({
235
+ loader: ({ context: { queryClient }, params: { postId } }) =>
236
+ queryClient.ensureQueryData(postQueryOptions(postId)),
237
+ component: PostComponent,
238
+ });
239
+
240
+ // FASTER — void return, inference stays out of the route tree
241
+ export const Route = createFileRoute("/posts/$postId")({
242
+ loader: async ({ context: { queryClient }, params: { postId } }) => {
243
+ await queryClient.ensureQueryData(postQueryOptions(postId));
244
+ },
245
+ component: PostComponent,
246
+ });
247
+ ```
248
+
249
+ ### `as const satisfies` for Link Option Objects
250
+
251
+ Never use `LinkProps` as a variable type — it's an enormous union:
252
+
253
+ ```tsx
254
+ import type { LinkProps, RegisteredRouter } from "@tanstack/react-router";
255
+
256
+ // WRONG — LinkProps is a massive union, extremely slow TS check
257
+ const wrongProps: LinkProps = { to: "/posts" };
258
+
259
+ // CORRECT — infer a precise type, validate against LinkProps
260
+ const goodProps = { to: "/posts" } as const satisfies LinkProps;
261
+
262
+ // EVEN BETTER — narrow LinkProps with generic params
263
+ const narrowedProps = {
264
+ to: "/posts",
265
+ } as const satisfies LinkProps<RegisteredRouter, string, "/posts">;
266
+ ```
267
+
268
+ ### Type-Safe Link Option Arrays
269
+
270
+ ```tsx
271
+ import type { LinkProps } from "@tanstack/react-router";
272
+
273
+ export const navLinks = [
274
+ { to: "/posts" },
275
+ { to: "/posts/$postId", params: { postId: "1" } },
276
+ ] as const satisfies ReadonlyArray<LinkProps>;
277
+
278
+ // Use the precise inferred type, not LinkProps directly
279
+ export type NavLink = (typeof navLinks)[number];
280
+ ```
281
+
282
+ ## Type Utilities for Generic Components
283
+
284
+ ### `ValidateLinkOptions` — Type-Safe Link Props in Custom Components
285
+
286
+ ```tsx
287
+ import {
288
+ Link,
289
+ type RegisteredRouter,
290
+ type ValidateLinkOptions,
291
+ } from '@tanstack/react-router'
292
+
293
+ interface NavItemProps<
294
+ TRouter extends RegisteredRouter = RegisteredRouter,
295
+ TOptions = unknown,
296
+ > {
297
+ label: string
298
+ linkOptions: ValidateLinkOptions<TRouter, TOptions>
299
+ }
300
+
301
+ export function NavItem<TRouter extends RegisteredRouter, TOptions>(
302
+ props: NavItemProps<TRouter, TOptions>,
303
+ ): React.ReactNode
304
+ export function NavItem(props: NavItemProps): React.ReactNode {
305
+ return (
306
+ <li>
307
+ <Link {...props.linkOptions}>{props.label}</Link>
308
+ </li>
309
+ )
310
+ }
311
+
312
+ // Usage — fully type-safe
313
+ <NavItem label="Posts" linkOptions={{ to: '/posts' }} />
314
+ <NavItem label="Post" linkOptions={{ to: '/posts/$postId', params: { postId: '1' } }} />
315
+ ```
316
+
317
+ ### `ValidateNavigateOptions` and `ValidateRedirectOptions`
318
+
319
+ Same pattern as `ValidateLinkOptions` above, for `useNavigate` and `redirect`. Declare a generic public overload plus a non-generic implementation signature so the call site stays narrowed and the body works without casts:
320
+
321
+ ```tsx
322
+ import {
323
+ useNavigate,
324
+ type RegisteredRouter,
325
+ type ValidateNavigateOptions,
326
+ } from "@tanstack/react-router";
327
+
328
+ export function useDelayedNavigate<
329
+ TRouter extends RegisteredRouter = RegisteredRouter,
330
+ TOptions = unknown,
331
+ >(options: ValidateNavigateOptions<TRouter, TOptions>, delayMs: number): () => void;
332
+ export function useDelayedNavigate(options: ValidateNavigateOptions, delayMs: number) {
333
+ const navigate = useNavigate();
334
+ return () => {
335
+ setTimeout(() => navigate(options), delayMs);
336
+ };
337
+ }
338
+ ```
339
+
340
+ `ValidateRedirectOptions` works identically — declare a generic overload accepting `ValidateRedirectOptions<TRouter, TOptions>` and an impl signature accepting `ValidateRedirectOptions`, then call `redirect(options)` in the body.
341
+
342
+ ### Render Props for Maximum Performance
343
+
344
+ Instead of accepting `LinkProps`, invert control so `Link` is narrowed at the call site:
345
+
346
+ ```tsx
347
+ function Card(props: { title: string; renderLink: () => React.ReactNode }) {
348
+ return (
349
+ <div>
350
+ <h2>{props.title}</h2>
351
+ {props.renderLink()}
352
+ </div>
353
+ );
354
+ }
355
+
356
+ // Link narrowed to exactly /posts — no union check
357
+ <Card title="All Posts" renderLink={() => <Link to="/posts">View</Link>} />;
358
+ ```
359
+
360
+ ## Render Optimizations
361
+
362
+ ### Fine-Grained Selectors with `select`
363
+
364
+ ```tsx
365
+ function PostTitle() {
366
+ // Only re-renders when page changes, not when other search params change
367
+ const page = Route.useSearch({ select: ({ page }) => page });
368
+ return <span>Page {page}</span>;
369
+ }
370
+ ```
371
+
372
+ ### Structural Sharing
373
+
374
+ Preserve referential identity across re-renders for search params:
375
+
376
+ ```tsx
377
+ const router = createRouter({
378
+ routeTree,
379
+ defaultStructuralSharing: true, // Enable globally
380
+ });
381
+
382
+ // Or per-hook
383
+ const result = Route.useSearch({
384
+ select: (search) => ({ foo: search.foo, label: `Page ${search.foo}` }),
385
+ structuralSharing: true,
386
+ });
387
+ ```
388
+
389
+ Structural sharing only works with JSON-compatible data. TypeScript will error if you return class instances with `structuralSharing: true`.
390
+
391
+ ## Common Mistakes
392
+
393
+ ### 1. CRITICAL: Adding type annotations or casts to inferred values
394
+
395
+ ```tsx
396
+ // WRONG — casting masks real type errors
397
+ const search = useSearch({ from: "/posts" }) as { page: number };
398
+
399
+ // WRONG — unnecessary annotation
400
+ const params: { postId: string } = useParams({ from: "/posts/$postId" });
401
+
402
+ // WRONG — generic param on hook
403
+ const data = useLoaderData<{ posts: Post[] }>({ from: "/posts" });
404
+
405
+ // CORRECT — let inference work
406
+ const search = useSearch({ from: "/posts" });
407
+ const params = useParams({ from: "/posts/$postId" });
408
+ const data = useLoaderData({ from: "/posts" });
409
+ ```
410
+
411
+ ### 2. HIGH: Using un-narrowed `LinkProps` type
412
+
413
+ ```tsx
414
+ // WRONG — LinkProps is a massive union, causes severe TS slowdown
415
+ const myProps: LinkProps = { to: "/posts" };
416
+
417
+ // CORRECT — use as const satisfies for precise inference
418
+ const myProps = { to: "/posts" } as const satisfies LinkProps;
419
+ ```
420
+
421
+ ### 3. HIGH: Not narrowing `Link`/`useNavigate` with `from`
422
+
423
+ ```tsx
424
+ // WRONG — search is a union of ALL routes, TS check grows with route count
425
+ <Link to=".." search={{ page: 0 }} />
426
+
427
+ // CORRECT — narrowed, fast check
428
+ <Link from={Route.fullPath} to=".." search={{ page: 0 }} />
429
+ ```
430
+
431
+ ### 4. CRITICAL (cross-skill): Missing router type registration
432
+
433
+ ```tsx
434
+ // WRONG — Link/useNavigate have no autocomplete, all paths are untyped strings
435
+ const router = createRouter({ routeTree });
436
+ // (no declare module)
437
+
438
+ // CORRECT — always register
439
+ const router = createRouter({ routeTree });
440
+ declare module "@tanstack/react-router" {
441
+ interface Register {
442
+ router: typeof router;
443
+ }
444
+ }
445
+ ```
446
+
447
+ ### 5. CRITICAL (cross-skill): Wrong-framework imports and file structure
448
+
449
+ Wrong-framework code looks plausible (it's React) but breaks the build or produces conflicting `/` routes at runtime.
450
+
451
+ ```tsx
452
+ // WRONG — react-router-dom and next/* are different libraries
453
+ import { Link, useNavigate, useSearchParams } from "react-router-dom";
454
+ import Link from "next/link";
455
+ import { useRouter, useParams } from "next/navigation";
456
+
457
+ // CORRECT — all routing exports come from @tanstack/react-router
458
+ import {
459
+ Link,
460
+ Outlet,
461
+ useNavigate,
462
+ useRouter,
463
+ useLocation,
464
+ useParams,
465
+ redirect,
466
+ } from "@tanstack/react-router";
467
+ ```
468
+
469
+ ```tsx
470
+ // WRONG file structures + APIs:
471
+ // src/pages/*.tsx with getServerSideProps / getStaticProps (Next.js Pages Router)
472
+ // app/layout.tsx + app/page.tsx (Next.js App Router)
473
+ // _app/index.tsx, pages/_app.tsx, pages/_document.tsx (Next.js custom App)
474
+ // loader/action exports (Remix)
475
+
476
+ // CORRECT — TanStack file-based routing at src/routes/*.tsx
477
+ export const Route = createFileRoute('/posts')({
478
+ loader: async () => { ... },
479
+ validateSearch: zodValidator(schema),
480
+ component: PostsComponent,
481
+ })
482
+ const search = Route.useSearch()
483
+ ```
484
+
485
+ If a build error mentions `react-router-dom`, `next/`, `pages/_app`, or duplicate `/` routes, fix the import — don't paper over with type assertions.
486
+
487
+ ### 6. CRITICAL: Treating typecheck as proof of runtime schema propagation
488
+
489
+ 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.
490
+ Add a runtime assertion against the real handler or serialized response, such as `expect(await getOrder({ data: { id } })).toMatchObject({ totalCents: 2599 })`.
491
+ Then run the route-level test and production build. The type test remains necessary, but it is not the runtime contract test.
492
+
493
+ See also: router-core (Register setup), router-core/navigation (from narrowing), router-core/code-splitting (getRouteApi).