@hitch42/applet 0.1.1-beta.8 → 0.1.1-beta.9

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 (167) 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.d.ts +1 -1
  5. package/dist/_internal/server.js +112 -57
  6. package/dist/applet.js +12 -12
  7. package/dist/build-Bh_S5BUv.js +2 -0
  8. package/dist/{build-DANx9Hgg.js → build-CEfFoIZZ.js} +1 -1
  9. package/dist/{ci-5qWl7lT3.js → ci-BKw14X2h.js} +14 -13
  10. package/dist/client.d.ts +3 -3
  11. package/dist/{contract-rP_eV9PK.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  12. package/dist/{deploy-MQWLG2EA.js → deploy-wGYjwXrx.js} +139 -88
  13. package/dist/{generate-BxYHdoAQ.js → generate-BEIYs17x.js} +65 -28
  14. package/dist/generate-Dox-OYt6.js +2 -0
  15. package/dist/headers-DkNr9MLJ.js +17 -0
  16. package/dist/index.d.ts +23 -22
  17. package/dist/index.js +2 -1
  18. package/dist/{init-jDEgYIXg.js → init-6oGPM46R.js} +15 -17
  19. package/dist/{load-config-BYSxDVAl.js → load-config-xEdTXs0y.js} +2 -2
  20. package/dist/log-context-zu8U9Sjm.js +73 -0
  21. package/dist/{login-C7RKmmTe.js → login-CUx3TmFQ.js} +1 -1
  22. package/dist/{logs-DgjgAxj2.js → logs-DSjuEN0Q.js} +20 -22
  23. package/dist/{manifest-DD_SGkS6.d.ts → manifest-C6_F4bL8.d.ts} +5 -1
  24. package/dist/{manifest-Bp87Uz0a.js → manifest-CJSL9GuC.js} +20 -4
  25. package/dist/{project-CbYPtpPq.js → project-BRZGd6xq.js} +199 -18
  26. package/dist/query-cache-CXmAyLv3.js +82 -0
  27. package/dist/react.d.ts +3 -2
  28. package/dist/react.js +98 -80
  29. package/dist/{schedules-dhU1yyFr.js → schedules-C8_Svr4h.js} +1 -1
  30. package/dist/server.d.ts +9 -4
  31. package/dist/server.js +29 -8
  32. package/dist/{telephony-protocol-DRgiYjqQ.d.ts → telephony-protocol-D-nf3Gca.d.ts} +15 -13
  33. package/dist/telephony.d.ts +24 -4
  34. package/dist/telephony.js +135 -16
  35. package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
  36. package/dist/{variables-Bwnr7oxf.js → variables-DT5RyHdH.js} +13 -13
  37. package/dist/vite.d.ts +3 -60
  38. package/dist/vite.js +50 -15
  39. package/dist/{workflow-engine-MogejNfF.js → workflow-engine-BOTzURKM.js} +1 -7
  40. package/dist/{workspace-XVtM12ZC.js → workspace-D0dnnlxL.js} +33 -38
  41. package/package.json +13 -5
  42. package/scaffold/.agents/skills/hitch-applets/SKILL.md +30 -4
  43. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +8 -0
  44. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  45. package/scaffold/.agents/skills/hitch-applets/references/records.md +23 -1
  46. package/scaffold/.agents/skills/hitch-data/SKILL.md +2 -1
  47. package/scaffold/.agents/skills/hitch-data/references/attributes.md +7 -1
  48. package/scaffold/.agents/skills/hitch-data/references/limits.md +13 -13
  49. package/scaffold/.agents/skills/hitch-theory/SKILL.md +3 -2
  50. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  51. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  52. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  53. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  54. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  55. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  56. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  57. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  58. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  59. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  60. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  106. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  107. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  108. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  109. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  110. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  111. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  112. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  113. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  114. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  115. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  116. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  136. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  137. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  138. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  139. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  140. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  141. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  142. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  143. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  144. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  145. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  146. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  148. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  149. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  150. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  151. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  152. package/scaffold/.github/workflows/hitch.yml +1 -1
  153. package/scaffold/AGENTS.md +2 -2
  154. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  155. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  156. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  157. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  158. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  159. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  160. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  161. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  162. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  163. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  164. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  165. package/scaffold/applet/src/client/styles/globals.css +2 -0
  166. package/dist/build-CFCKKMn7.js +0 -2
  167. package/dist/generate-CQ07Hdpo.js +0 -2
@@ -0,0 +1,490 @@
1
+ ---
2
+ name: data-loading
3
+ description: >-
4
+ Route loader option, loaderDeps for cache keys, staleTime/gcTime/
5
+ defaultPreloadStaleTime SWR caching, pendingComponent/pendingMs/
6
+ pendingMinMs, errorComponent/onError/onCatch, beforeLoad, router
7
+ context and createRootRouteWithContext DI pattern, router.invalidate,
8
+ Await component, deferred data loading with unawaited promises.
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/data-loading.md
17
+ - TanStack/router:docs/router/guide/deferred-data-loading.md
18
+ - TanStack/router:docs/router/guide/router-context.md
19
+ - TanStack/router:docs/router/guide/data-mutations.md
20
+ ---
21
+
22
+ # Data Loading
23
+
24
+ ## Setup
25
+
26
+ Basic loader returning data, consumed via `useLoaderData`:
27
+
28
+ ```tsx
29
+ // src/routes/posts.tsx
30
+ import { createFileRoute } from "@tanstack/react-router";
31
+
32
+ export const Route = createFileRoute("/posts")({
33
+ loader: () => fetchPosts(),
34
+ component: PostsComponent,
35
+ });
36
+
37
+ function PostsComponent() {
38
+ const posts = Route.useLoaderData();
39
+ return (
40
+ <ul>
41
+ {posts.map((post) => (
42
+ <li key={post.id}>{post.title}</li>
43
+ ))}
44
+ </ul>
45
+ );
46
+ }
47
+ ```
48
+
49
+ In code-split components, use `getRouteApi` instead of importing Route:
50
+
51
+ ```tsx
52
+ import { getRouteApi } from "@tanstack/react-router";
53
+
54
+ const routeApi = getRouteApi("/posts");
55
+
56
+ function PostsComponent() {
57
+ const posts = routeApi.useLoaderData();
58
+ return <ul>{/* ... */}</ul>;
59
+ }
60
+ ```
61
+
62
+ ## Route Loading Lifecycle
63
+
64
+ The router executes this sequence on every URL/history update:
65
+
66
+ 1. **Route Matching** (top-down)
67
+ - `route.params.parse`
68
+ - `route.validateSearch`
69
+ 2. **Route Pre-Loading** (serial)
70
+ - `route.beforeLoad`
71
+ - `route.onError` → `route.errorComponent`
72
+ 3. **Route Loading** (parallel)
73
+ - `route.component.preload?`
74
+ - `route.loader`
75
+ - `route.pendingComponent` (optional)
76
+ - `route.component`
77
+ - `route.onError` → `route.errorComponent`
78
+
79
+ Key: `beforeLoad` runs before `loader`. `beforeLoad` for a parent runs before its children's `beforeLoad`. Throwing in `beforeLoad` prevents all children from loading.
80
+
81
+ ## Core Patterns
82
+
83
+ ### loaderDeps for Search-Param-Driven Cache Keys
84
+
85
+ Loaders don't receive search params directly. Use `loaderDeps` to declare which search params affect the cache key:
86
+
87
+ ```tsx
88
+ // src/routes/posts.tsx
89
+ import { createFileRoute } from "@tanstack/react-router";
90
+
91
+ export const Route = createFileRoute("/posts")({
92
+ validateSearch: (search) => ({
93
+ offset: Number(search.offset) || 0,
94
+ limit: Number(search.limit) || 10,
95
+ }),
96
+ loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),
97
+ loader: ({ deps: { offset, limit } }) => fetchPosts({ offset, limit }),
98
+ });
99
+ ```
100
+
101
+ When deps change, the route reloads regardless of `staleTime`.
102
+
103
+ ### SWR Caching Configuration
104
+
105
+ TanStack Router has built-in Stale-While-Revalidate caching keyed on the route's parsed pathname + `loaderDeps`.
106
+
107
+ Defaults:
108
+
109
+ - **`staleTime`: 0** — data is always considered stale, reloads in background on re-match
110
+ - **`preloadStaleTime`: 30 seconds** — preloaded data won't be refetched for 30s
111
+ - **`gcTime`: 30 minutes** — unused cache entries garbage collected after 30min
112
+
113
+ ```tsx
114
+ export const Route = createFileRoute("/posts")({
115
+ loader: () => fetchPosts(),
116
+ staleTime: 10_000, // 10s: data considered fresh for 10 seconds
117
+ gcTime: 5 * 60 * 1000, // 5min: garbage collect after 5 minutes
118
+ });
119
+ ```
120
+
121
+ Disable SWR caching entirely:
122
+
123
+ ```tsx
124
+ export const Route = createFileRoute("/posts")({
125
+ loader: () => fetchPosts(),
126
+ staleTime: Infinity,
127
+ });
128
+ ```
129
+
130
+ Globally:
131
+
132
+ ```tsx
133
+ const router = createRouter({
134
+ routeTree,
135
+ defaultStaleTime: Infinity,
136
+ });
137
+ ```
138
+
139
+ ### Pending States (pendingComponent / pendingMs / pendingMinMs)
140
+
141
+ By default, a pending component shows after 1 second (`pendingMs: 1000`) and stays for at least 500ms (`pendingMinMs: 500`) to avoid flash.
142
+
143
+ ```tsx
144
+ export const Route = createFileRoute("/posts")({
145
+ loader: () => fetchPosts(),
146
+ pendingMs: 500,
147
+ pendingMinMs: 300,
148
+ pendingComponent: () => <div>Loading posts...</div>,
149
+ component: PostsComponent,
150
+ });
151
+ ```
152
+
153
+ ### Router Context with createRootRouteWithContext (Factory Pattern)
154
+
155
+ `createRootRouteWithContext` is a factory that returns a function. You must call it twice — the first call passes the generic type, the second passes route options:
156
+
157
+ ```tsx
158
+ // src/routes/__root.tsx
159
+ import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
160
+
161
+ interface MyRouterContext {
162
+ auth: { userId: string };
163
+ fetchPosts: () => Promise<Post[]>;
164
+ }
165
+
166
+ // NOTE: double call — createRootRouteWithContext<Type>()({...})
167
+ export const Route = createRootRouteWithContext<MyRouterContext>()({
168
+ component: () => <Outlet />,
169
+ });
170
+ ```
171
+
172
+ Supply the context when creating the router:
173
+
174
+ ```tsx
175
+ // src/router.tsx
176
+ import { createRouter } from "@tanstack/react-router";
177
+ import { routeTree } from "./routeTree.gen";
178
+
179
+ const router = createRouter({
180
+ routeTree,
181
+ context: {
182
+ auth: { userId: "123" },
183
+ fetchPosts,
184
+ },
185
+ });
186
+ ```
187
+
188
+ Consume in loaders and beforeLoad:
189
+
190
+ ```tsx
191
+ // src/routes/posts.tsx
192
+ export const Route = createFileRoute("/posts")({
193
+ loader: ({ context: { fetchPosts } }) => fetchPosts(),
194
+ });
195
+ ```
196
+
197
+ To pass React hook values into the router context, call the hook above `RouterProvider` and inject via the `context` prop:
198
+
199
+ ```tsx
200
+ import { RouterProvider } from "@tanstack/react-router";
201
+
202
+ function InnerApp() {
203
+ const auth = useAuth();
204
+ return <RouterProvider router={router} context={{ auth }} />;
205
+ }
206
+
207
+ function App() {
208
+ return (
209
+ <AuthProvider>
210
+ <InnerApp />
211
+ </AuthProvider>
212
+ );
213
+ }
214
+ ```
215
+
216
+ Route-level context via `beforeLoad`:
217
+
218
+ ```tsx
219
+ export const Route = createFileRoute("/posts")({
220
+ beforeLoad: ({ context }) => ({
221
+ fetchPosts: context.fetchPosts,
222
+ }),
223
+ loader: ({ context: { fetchPosts } }) => fetchPosts(),
224
+ });
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
+
243
+ ### Deferred Data Loading
244
+
245
+ Return unawaited promises from the loader for non-critical data. Use the `Await` component to render them:
246
+
247
+ ```tsx
248
+ import { createFileRoute, Await } from "@tanstack/react-router";
249
+
250
+ export const Route = createFileRoute("/posts/$postId")({
251
+ loader: async ({ params: { postId } }) => {
252
+ // Slow data — do NOT await
253
+ const slowDataPromise = fetchComments(postId);
254
+ // Fast data — await
255
+ const post = await fetchPost(postId);
256
+
257
+ return { post, deferredComments: slowDataPromise };
258
+ },
259
+ component: PostComponent,
260
+ });
261
+
262
+ function PostComponent() {
263
+ const { post, deferredComments } = Route.useLoaderData();
264
+
265
+ return (
266
+ <div>
267
+ <h1>{post.title}</h1>
268
+ <Await promise={deferredComments} fallback={<div>Loading comments...</div>}>
269
+ {(comments) => (
270
+ <ul>
271
+ {comments.map((c) => (
272
+ <li key={c.id}>{c.body}</li>
273
+ ))}
274
+ </ul>
275
+ )}
276
+ </Await>
277
+ </div>
278
+ );
279
+ }
280
+ ```
281
+
282
+ ### Invalidation After Mutations
283
+
284
+ `router.invalidate()` forces all active route loaders to re-run and marks all cached data as stale:
285
+
286
+ ```tsx
287
+ import { useRouter } from "@tanstack/react-router";
288
+
289
+ function AddPostButton() {
290
+ const router = useRouter();
291
+
292
+ const handleAdd = async () => {
293
+ await createPost({ title: "New post" });
294
+ await router.invalidate({ sync: true });
295
+ };
296
+
297
+ return <button onClick={handleAdd}>Add Post</button>;
298
+ }
299
+ ```
300
+
301
+ Use `await router.invalidate({ sync: true })` when the next step requires refreshed loader data.
302
+
303
+ 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.
304
+
305
+ ### Error Handling
306
+
307
+ ```tsx
308
+ import { createFileRoute, ErrorComponent, useRouter } from "@tanstack/react-router";
309
+
310
+ export const Route = createFileRoute("/posts")({
311
+ loader: () => fetchPosts(),
312
+ errorComponent: ({ error, reset }) => {
313
+ const router = useRouter();
314
+
315
+ if (error instanceof CustomError) {
316
+ return <div>{error.message}</div>;
317
+ }
318
+
319
+ return (
320
+ <div>
321
+ <ErrorComponent error={error} />
322
+ <button
323
+ onClick={() => {
324
+ // For loader errors, invalidate to re-run loader + reset boundary
325
+ router.invalidate();
326
+ }}
327
+ >
328
+ Retry
329
+ </button>
330
+ </div>
331
+ );
332
+ },
333
+ });
334
+ ```
335
+
336
+ ### Loader Parameters
337
+
338
+ The `loader` function receives:
339
+
340
+ - `params` — parsed path params
341
+ - `deps` — object from `loaderDeps`
342
+ - `context` — merged parent + beforeLoad context
343
+ - `abortController` — cancelled when route unloads or becomes stale
344
+ - `cause` — `'enter'`, `'stay'`, or `'preload'`
345
+ - `preload` — `true` during preloading
346
+ - `location` — current location object
347
+ - `parentMatchPromise` — promise of parent route match
348
+ - `route` — the route object itself
349
+
350
+ ```tsx
351
+ export const Route = createFileRoute("/posts/$postId")({
352
+ loader: ({ params: { postId }, abortController }) =>
353
+ fetchPost(postId, { signal: abortController.signal }),
354
+ });
355
+ ```
356
+
357
+ ## Common Mistakes
358
+
359
+ ### CRITICAL: Assuming loaders only run on the server
360
+
361
+ TanStack Router is **client-first**. Loaders run on the **client** by default. They also run on the server when using TanStack Start for SSR, but the default mental model is client-side execution.
362
+
363
+ ```tsx
364
+ // WRONG — this will crash in the browser
365
+ export const Route = createFileRoute("/posts")({
366
+ loader: async () => {
367
+ const fs = await import("fs"); // Node.js only!
368
+ return JSON.parse(fs.readFileSync("...")); // fails in browser
369
+ },
370
+ });
371
+
372
+ // CORRECT for an SPA — use a client-safe API helper
373
+ export const Route = createFileRoute("/posts")({
374
+ loader: () => fetchPosts(),
375
+ });
376
+ ```
377
+
378
+ 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.
379
+
380
+ ### MEDIUM: Not understanding staleTime default is 0
381
+
382
+ Default `staleTime` is `0`. This means data reloads in the background on every route re-match. This is intentional — it ensures fresh data. But if your data is expensive or static, set `staleTime`:
383
+
384
+ ```tsx
385
+ export const Route = createFileRoute("/posts")({
386
+ loader: () => fetchPosts(),
387
+ staleTime: 60_000, // Consider fresh for 1 minute
388
+ });
389
+ ```
390
+
391
+ ### HIGH: Using reset() instead of router.invalidate() in error components
392
+
393
+ `reset()` only resets the error boundary UI. It does NOT re-run the loader. For loader errors, use `router.invalidate()` which re-runs loaders and resets the boundary:
394
+
395
+ ```tsx
396
+ // WRONG — resets boundary but loader still has stale error
397
+ function PostErrorComponent({ error, reset }) {
398
+ return <button onClick={reset}>Retry</button>;
399
+ }
400
+
401
+ // CORRECT — re-runs loader and resets the error boundary
402
+ function PostErrorComponent({ error }) {
403
+ const router = useRouter();
404
+ return <button onClick={() => router.invalidate()}>Retry</button>;
405
+ }
406
+ ```
407
+
408
+ ### HIGH: Missing double parentheses on createRootRouteWithContext
409
+
410
+ `createRootRouteWithContext<Type>()` is a factory — it returns a function. Must call twice:
411
+
412
+ ```tsx
413
+ // WRONG — missing second call, passes options to the factory
414
+ const rootRoute = createRootRouteWithContext<{ auth: AuthState }>({
415
+ component: RootComponent,
416
+ });
417
+
418
+ // CORRECT — factory()({options})
419
+ const rootRoute = createRootRouteWithContext<{ auth: AuthState }>()({
420
+ component: RootComponent,
421
+ });
422
+ ```
423
+
424
+ ### HIGH: Using React hooks in beforeLoad or loader
425
+
426
+ `beforeLoad` and `loader` are NOT React components. You cannot call hooks inside them. Use router context to inject values from hooks:
427
+
428
+ ```tsx
429
+ // WRONG — hooks cannot be called outside React components
430
+ export const Route = createFileRoute("/posts")({
431
+ loader: () => {
432
+ const auth = useAuth(); // This will crash!
433
+ return fetchPosts(auth.userId);
434
+ },
435
+ });
436
+
437
+ // CORRECT — inject hook values via router context
438
+ // In your App component:
439
+ function InnerApp() {
440
+ const auth = useAuth();
441
+ return <RouterProvider router={router} context={{ auth }} />;
442
+ }
443
+
444
+ // In your route:
445
+ export const Route = createFileRoute("/posts")({
446
+ loader: ({ context: { auth } }) => fetchPosts(auth.userId),
447
+ });
448
+ ```
449
+
450
+ ### HIGH: Property order affects TypeScript inference
451
+
452
+ Router infers types from earlier properties into later ones. Declaring `beforeLoad` after `loader` means context from `beforeLoad` is unknown in the loader:
453
+
454
+ ```tsx
455
+ // WRONG — context.user is unknown because beforeLoad declared after loader
456
+ export const Route = createFileRoute("/admin")({
457
+ loader: ({ context }) => fetchData(context.user),
458
+ beforeLoad: () => ({ user: getUser() }),
459
+ });
460
+
461
+ // CORRECT — validateSearch → loaderDeps → beforeLoad → loader
462
+ export const Route = createFileRoute("/admin")({
463
+ beforeLoad: () => ({ user: getUser() }),
464
+ loader: ({ context }) => fetchData(context.user),
465
+ });
466
+ ```
467
+
468
+ ### HIGH: Returning entire search object from loaderDeps
469
+
470
+ ```tsx
471
+ // WRONG — loader re-runs on ANY search param change
472
+ loaderDeps: ({ search }) => search;
473
+
474
+ // CORRECT — only re-run when page changes
475
+ loaderDeps: ({ search }) => ({ page: search.page });
476
+ ```
477
+
478
+ Returning the whole `search` object means unrelated param changes (e.g., `sortDirection`, `viewMode`) trigger unnecessary reloads because deep equality fails on the entire object.
479
+
480
+ ## Tensions
481
+
482
+ - **Client-first loaders vs SSR expectations**: Loaders run on the client by default. When using SSR (TanStack Start), they run on both client and server. Browser-only APIs work by default but break under SSR. Server-only APIs (fs, db) break by default but work under Start server functions. See **router-core/ssr/SKILL.md**.
483
+ - **Built-in SWR cache vs external cache coordination**: Router has built-in caching. When using TanStack Query, set `defaultPreloadStaleTime: 0` to avoid double-caching. See **compositions/router-query/SKILL.md**.
484
+
485
+ ---
486
+
487
+ ## Cross-References
488
+
489
+ - See also: **router-core/search-params/SKILL.md** — `loaderDeps` consumes validated search params as cache keys
490
+ - See also: **compositions/router-query/SKILL.md** — for external cache coordination with TanStack Query
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: router-core
3
+ description: >-
4
+ Framework-agnostic core concepts for TanStack Router: route trees,
5
+ createRouter, createRoute, createRootRoute, createRootRouteWithContext,
6
+ addChildren, Register type declaration, route matching, route sorting,
7
+ file naming conventions. Entry point for all router skills.
8
+ metadata:
9
+ type: core
10
+ library: tanstack-router
11
+ library_version: "1.171.15"
12
+ ---
13
+
14
+ # TanStack Router Core
15
+
16
+ TanStack Router is a type-safe router for React and Solid with built-in SWR caching, JSON-first search params, file-based route generation, and end-to-end type inference. The core is framework-agnostic; React and Solid bindings layer on top.
17
+
18
+ > **CRITICAL**: TanStack Router types are FULLY INFERRED. Never cast, never annotate inferred values. This is the #1 AI agent mistake.
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.
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
+
24
+ ## Sub-Skills
25
+
26
+ | Task | Sub-Skill |
27
+ | -------------------------------------------------- | -------------------------------------------------------------------- |
28
+ | Validate, read, write, transform search params | [router-core/search-params/SKILL.md](search-params.md) |
29
+ | Dynamic segments, splats, optional params | [router-core/path-params/SKILL.md](path-params.md) |
30
+ | Link, useNavigate, preloading, blocking | [router-core/navigation/SKILL.md](navigation.md) |
31
+ | Route loaders, SWR caching, context, deferred data | [router-core/data-loading/SKILL.md](data-loading.md) |
32
+ | Auth guards, RBAC, beforeLoad redirects | [router-core/auth-and-guards/SKILL.md](auth-and-guards.md) |
33
+ | Automatic and manual code splitting | [router-core/code-splitting/SKILL.md](code-splitting.md) |
34
+ | 404 handling, error boundaries, notFound() | [router-core/not-found-and-errors/SKILL.md](not-found-and-errors.md) |
35
+ | Inference, Register, from narrowing, TS perf | [router-core/type-safety/SKILL.md](type-safety.md) |
36
+ | Streaming/non-streaming SSR, hydration, head mgmt | [router-core/ssr/SKILL.md](ssr.md) |
37
+
38
+ ## Quick Decision Tree
39
+
40
+ ```
41
+ Need to add/read/write URL query parameters?
42
+ → router-core/search-params
43
+
44
+ Need dynamic URL segments like /posts/$postId?
45
+ → router-core/path-params
46
+
47
+ Need to create links or navigate programmatically?
48
+ → router-core/navigation
49
+
50
+ Need to fetch data for a route?
51
+ Is it client-side only or client+server?
52
+ → router-core/data-loading
53
+ Using TanStack Query as external cache?
54
+ → compositions/router-query (separate skill)
55
+
56
+ Need to protect routes behind auth?
57
+ → router-core/auth-and-guards
58
+
59
+ Need to reduce bundle size per route?
60
+ → router-core/code-splitting
61
+
62
+ Need custom 404 or error handling?
63
+ → router-core/not-found-and-errors
64
+
65
+ Having TypeScript issues or performance problems?
66
+ → router-core/type-safety
67
+
68
+ Need server-side rendering?
69
+ → router-core/ssr
70
+ ```
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
+
88
+ ## Minimal Working Example
89
+
90
+ ```tsx
91
+ // src/routes/__root.tsx
92
+ import { createRootRoute, Outlet } from "@tanstack/react-router";
93
+
94
+ export const Route = createRootRoute({
95
+ component: () => <Outlet />,
96
+ });
97
+ ```
98
+
99
+ ```tsx
100
+ // src/routes/index.tsx
101
+ import { createFileRoute } from "@tanstack/react-router";
102
+
103
+ export const Route = createFileRoute("/")({
104
+ component: () => <h1>Home</h1>,
105
+ });
106
+ ```
107
+
108
+ ```tsx
109
+ // src/router.tsx
110
+ import { createRouter } from "@tanstack/react-router";
111
+ import { routeTree } from "./routeTree.gen";
112
+
113
+ const router = createRouter({ routeTree });
114
+
115
+ // REQUIRED for type safety — without this, Link/useNavigate have no autocomplete
116
+ declare module "@tanstack/react-router" {
117
+ interface Register {
118
+ router: typeof router;
119
+ }
120
+ }
121
+
122
+ export default router;
123
+ ```
124
+
125
+ ```tsx
126
+ // src/main.tsx
127
+ import { RouterProvider } from "@tanstack/react-router";
128
+ import router from "./router";
129
+
130
+ function App() {
131
+ return <RouterProvider router={router} />;
132
+ }
133
+ ```
134
+
135
+ ## Common Mistakes
136
+
137
+ ### HIGH: createFileRoute path string must match the file path
138
+
139
+ The Vite plugin manages the path string in `createFileRoute`. Do not change it manually — it must match the file's location under `src/routes/`:
140
+
141
+ ```tsx
142
+ // File: src/routes/posts/$postId.tsx
143
+ export const Route = createFileRoute("/posts/$postId")({
144
+ // ✅ matches file path
145
+ component: PostPage,
146
+ });
147
+
148
+ export const Route = createFileRoute("/post/$postId")({
149
+ // ❌ silent mismatch
150
+ component: PostPage,
151
+ });
152
+ ```
153
+
154
+ The plugin auto-generates this string. If you rename a route file, the plugin updates it. Never edit the path string by hand.
155
+
156
+ ## Version Note
157
+
158
+ This skill targets `@tanstack/router-core` v1.171.15. Splat routes use `$` (not `*`); the `*` compat alias will be removed in v2.