@hitch42/applet 0.1.1-beta.1 → 0.1.1-beta.11

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 (178) 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 +14 -1
  5. package/dist/_internal/server.js +122 -118
  6. package/dist/applet.js +53 -29
  7. package/dist/build-Bh_S5BUv.js +2 -0
  8. package/dist/{build-Rk3xDZV7.js → build-CEfFoIZZ.js} +30 -14
  9. package/dist/ci-BKw14X2h.js +69 -0
  10. package/dist/client.d.ts +3 -3
  11. package/dist/{contract-DMrpbaLJ.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  12. package/dist/{deploy-D3BGDqgC.js → deploy-wGYjwXrx.js} +249 -35
  13. package/dist/email-DBTVhNB_.js +62 -0
  14. package/dist/{generate-CH0VQmUi.js → generate-BEIYs17x.js} +79 -25
  15. package/dist/generate-Dox-OYt6.js +2 -0
  16. package/dist/headers-DkNr9MLJ.js +17 -0
  17. package/dist/index.d.ts +123 -11
  18. package/dist/index.js +142 -26
  19. package/dist/{init-BS1Y-eNL.js → init-6oGPM46R.js} +17 -18
  20. package/dist/load-config-xEdTXs0y.js +27 -0
  21. package/dist/log-context-zu8U9Sjm.js +73 -0
  22. package/dist/login-CUx3TmFQ.js +12 -0
  23. package/dist/{logs-m00pzr_O.js → logs-DSjuEN0Q.js} +20 -22
  24. package/dist/{manifest-BQCAxATS.d.ts → manifest-C6_F4bL8.d.ts} +9 -0
  25. package/dist/{manifest-DyoJ1MIL.js → manifest-CJSL9GuC.js} +25 -3
  26. package/dist/me-BW7Paary.js +32 -0
  27. package/dist/{project-CxN-Dtc7.js → project-BRZGd6xq.js} +227 -34
  28. package/dist/prompt-ChS-grYO.js +29 -0
  29. package/dist/query-cache-CXmAyLv3.js +82 -0
  30. package/dist/react.d.ts +3 -2
  31. package/dist/react.js +98 -80
  32. package/dist/schedules-C8_Svr4h.js +42 -0
  33. package/dist/server.d.ts +9 -4
  34. package/dist/server.js +29 -8
  35. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  36. package/dist/telephony.d.ts +97 -0
  37. package/dist/telephony.js +726 -0
  38. package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
  39. package/dist/{variables-CP7Ce3Np.js → variables-DT5RyHdH.js} +14 -39
  40. package/dist/vite.d.ts +3 -60
  41. package/dist/vite.js +503 -34
  42. package/dist/workflow-engine-BOTzURKM.js +111 -0
  43. package/dist/{auth-DaVK0k5R.js → workspace-D0dnnlxL.js} +75 -40
  44. package/package.json +19 -6
  45. package/scaffold/.agents/skills/hitch-applets/SKILL.md +60 -15
  46. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  47. package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
  48. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +29 -2
  49. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  50. package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
  51. package/scaffold/.agents/skills/hitch-applets/references/records.md +32 -8
  52. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
  53. package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
  54. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
  55. package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
  56. package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
  57. package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
  58. package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
  59. package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
  60. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  106. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  107. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  108. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  109. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  110. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  111. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  112. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  113. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  114. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  115. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  116. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  136. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  137. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  138. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  139. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  140. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  141. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  142. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  143. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  144. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  145. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  146. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  148. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  149. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  150. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  151. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  152. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  153. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  154. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  155. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  156. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  157. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  158. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  159. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  160. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  161. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  162. package/scaffold/.github/workflows/hitch.yml +15 -0
  163. package/scaffold/AGENTS.md +2 -2
  164. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  165. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  166. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  167. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  168. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  169. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  170. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  171. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  172. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  173. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  174. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  175. package/scaffold/applet/src/client/styles/globals.css +2 -0
  176. package/dist/build-CEku1lyb.js +0 -2
  177. package/dist/generate-6Nbt7uii.js +0 -2
  178. package/dist/load-config-BnxocLAQ.js +0 -56
@@ -0,0 +1,383 @@
1
+ ---
2
+ name: path-params
3
+ description: >-
4
+ Dynamic path segments ($paramName), splat routes ($ / _splat),
5
+ optional params ({-$paramName}), prefix/suffix patterns ({$param}.ext),
6
+ useParams, params.parse/stringify, pathParamsAllowedCharacters,
7
+ i18n locale patterns.
8
+ metadata:
9
+ type: sub-skill
10
+ library: tanstack-router
11
+ library_version: "1.171.15"
12
+ requires:
13
+ - router-core
14
+ sources:
15
+ - TanStack/router:docs/router/guide/path-params.md
16
+ - TanStack/router:docs/router/routing/routing-concepts.md
17
+ - TanStack/router:docs/router/guide/internationalization-i18n.md
18
+ ---
19
+
20
+ # Path Params
21
+
22
+ Path params capture dynamic URL segments into named variables. They are defined with a `$` prefix in the route path.
23
+
24
+ > **CRITICAL**: Never interpolate params into the `to` string. Always use the `params` prop. This is the most common agent mistake for path params.
25
+
26
+ > **CRITICAL**: Types are fully inferred. Never annotate the return of `useParams()`.
27
+
28
+ ## Dynamic Segments
29
+
30
+ A segment prefixed with `$` captures text until the next `/`.
31
+
32
+ ```tsx
33
+ // src/routes/posts.$postId.tsx
34
+ import { createFileRoute } from "@tanstack/react-router";
35
+
36
+ export const Route = createFileRoute("/posts/$postId")({
37
+ loader: async ({ params }) => {
38
+ // params.postId is string — fully inferred, do not annotate
39
+ return fetchPost(params.postId);
40
+ },
41
+ component: PostComponent,
42
+ });
43
+
44
+ function PostComponent() {
45
+ const { postId } = Route.useParams();
46
+ const data = Route.useLoaderData();
47
+ return (
48
+ <h1>
49
+ Post {postId}: {data.title}
50
+ </h1>
51
+ );
52
+ }
53
+ ```
54
+
55
+ Multiple dynamic segments work across path levels:
56
+
57
+ ```tsx
58
+ // src/routes/teams.$teamId.members.$memberId.tsx
59
+ export const Route = createFileRoute("/teams/$teamId/members/$memberId")({
60
+ component: MemberComponent,
61
+ });
62
+
63
+ function MemberComponent() {
64
+ const { teamId, memberId } = Route.useParams();
65
+ return (
66
+ <div>
67
+ Team {teamId}, Member {memberId}
68
+ </div>
69
+ );
70
+ }
71
+ ```
72
+
73
+ ## Splat / Catch-All Routes
74
+
75
+ A route with a path ending in `$` (bare dollar sign) captures everything after it. The value is available under the `_splat` key.
76
+
77
+ ```tsx
78
+ // src/routes/files.$.tsx
79
+ import { createFileRoute } from "@tanstack/react-router";
80
+
81
+ export const Route = createFileRoute("/files/$")({
82
+ component: FileViewer,
83
+ });
84
+
85
+ function FileViewer() {
86
+ const { _splat } = Route.useParams();
87
+ // URL: /files/documents/report.pdf → _splat = "documents/report.pdf"
88
+ return <div>File path: {_splat}</div>;
89
+ }
90
+ ```
91
+
92
+ ## Optional Params
93
+
94
+ Optional params use `{-$paramName}` syntax. The segment may or may not be present. When absent, the value is `undefined`.
95
+
96
+ ```tsx
97
+ // src/routes/posts.{-$category}.tsx
98
+ import { createFileRoute } from "@tanstack/react-router";
99
+
100
+ export const Route = createFileRoute("/posts/{-$category}")({
101
+ component: PostsComponent,
102
+ });
103
+
104
+ function PostsComponent() {
105
+ const { category } = Route.useParams();
106
+ // URL: /posts → category is undefined
107
+ // URL: /posts/tech → category is "tech"
108
+ return <div>{category ? `Posts in ${category}` : "All Posts"}</div>;
109
+ }
110
+ ```
111
+
112
+ Multiple optional params:
113
+
114
+ ```tsx
115
+ // Matches: /posts, /posts/tech, /posts/tech/hello-world
116
+ export const Route = createFileRoute("/posts/{-$category}/{-$slug}")({
117
+ component: PostComponent,
118
+ });
119
+ ```
120
+
121
+ ### i18n with Optional Locale
122
+
123
+ ```tsx
124
+ // src/routes/{-$locale}/about.tsx
125
+ import { createFileRoute } from "@tanstack/react-router";
126
+
127
+ export const Route = createFileRoute("/{-$locale}/about")({
128
+ component: AboutComponent,
129
+ });
130
+
131
+ function AboutComponent() {
132
+ const { locale } = Route.useParams();
133
+ const currentLocale = locale || "en";
134
+ return <h1>{currentLocale === "fr" ? "À Propos" : "About Us"}</h1>;
135
+ }
136
+ // Matches: /about, /en/about, /fr/about
137
+ ```
138
+
139
+ ## Prefix and Suffix Patterns
140
+
141
+ Curly braces `{}` around `$paramName` allow text before or after the dynamic part within a single segment.
142
+
143
+ ### Prefix
144
+
145
+ ```tsx
146
+ // src/routes/posts/post-{$postId}.tsx
147
+ import { createFileRoute } from "@tanstack/react-router";
148
+
149
+ export const Route = createFileRoute("/posts/post-{$postId}")({
150
+ component: PostComponent,
151
+ });
152
+
153
+ function PostComponent() {
154
+ const { postId } = Route.useParams();
155
+ // URL: /posts/post-123 → postId = "123"
156
+ return <div>Post ID: {postId}</div>;
157
+ }
158
+ ```
159
+
160
+ ### Suffix
161
+
162
+ ```tsx
163
+ // src/routes/files/{$fileName}[.]txt.tsx
164
+ import { createFileRoute } from "@tanstack/react-router";
165
+
166
+ export const Route = createFileRoute("/files/{$fileName}.txt")({
167
+ component: FileComponent,
168
+ });
169
+
170
+ function FileComponent() {
171
+ const { fileName } = Route.useParams();
172
+ // URL: /files/readme.txt → fileName = "readme"
173
+ return <div>File: {fileName}.txt</div>;
174
+ }
175
+ ```
176
+
177
+ ### Combined Prefix + Suffix
178
+
179
+ ```tsx
180
+ // URL: /users/user-456.json → userId = "456"
181
+ export const Route = createFileRoute("/users/user-{$userId}.json")({
182
+ component: UserComponent,
183
+ });
184
+
185
+ function UserComponent() {
186
+ const { userId } = Route.useParams();
187
+ return <div>User: {userId}</div>;
188
+ }
189
+ ```
190
+
191
+ ## Navigating with Path Params
192
+
193
+ ### Object Form
194
+
195
+ ```tsx
196
+ import { Link } from "@tanstack/react-router";
197
+
198
+ function PostLink({ postId }: { postId: string }) {
199
+ return (
200
+ <Link to="/posts/$postId" params={{ postId }}>
201
+ View Post
202
+ </Link>
203
+ );
204
+ }
205
+ ```
206
+
207
+ ### Function Form (Preserves Other Params)
208
+
209
+ ```tsx
210
+ function PostLink({ postId }: { postId: string }) {
211
+ return (
212
+ <Link to="/posts/$postId" params={(prev) => ({ ...prev, postId })}>
213
+ View Post
214
+ </Link>
215
+ );
216
+ }
217
+ ```
218
+
219
+ ### Programmatic Navigation
220
+
221
+ ```tsx
222
+ import { useNavigate } from "@tanstack/react-router";
223
+
224
+ function GoToPost({ postId }: { postId: string }) {
225
+ const navigate = useNavigate();
226
+
227
+ return (
228
+ <button
229
+ onClick={() => {
230
+ navigate({ to: "/posts/$postId", params: { postId } });
231
+ }}
232
+ >
233
+ Go to Post
234
+ </button>
235
+ );
236
+ }
237
+ ```
238
+
239
+ ### Navigating with Optional Params
240
+
241
+ ```tsx
242
+ // Include the optional param
243
+ <Link to="/posts/{-$category}" params={{ category: 'tech' }}>
244
+ Tech Posts
245
+ </Link>
246
+
247
+ // Omit the optional param (renders /posts)
248
+ <Link to="/posts/{-$category}" params={{ category: undefined }}>
249
+ All Posts
250
+ </Link>
251
+ ```
252
+
253
+ ## Reading Params Outside Route Components
254
+
255
+ ### `useParams` with `from`
256
+
257
+ ```tsx
258
+ import { useParams } from "@tanstack/react-router";
259
+
260
+ function PostHeader() {
261
+ const { postId } = useParams({ from: "/posts/$postId" });
262
+ return <h2>Post {postId}</h2>;
263
+ }
264
+ ```
265
+
266
+ ### `useParams` with `strict: false`
267
+
268
+ ```tsx
269
+ function GenericBreadcrumb() {
270
+ const params = useParams({ strict: false });
271
+ // params is a union of all possible route params
272
+ return <span>{params.postId ?? "Home"}</span>;
273
+ }
274
+ ```
275
+
276
+ ## Params in Loaders and `beforeLoad`
277
+
278
+ ```tsx
279
+ export const Route = createFileRoute("/posts/$postId")({
280
+ beforeLoad: async ({ params }) => {
281
+ // params.postId available here
282
+ const canView = await checkPermission(params.postId);
283
+ if (!canView) throw redirect({ to: "/unauthorized" });
284
+ },
285
+ loader: async ({ params }) => {
286
+ return fetchPost(params.postId);
287
+ },
288
+ });
289
+ ```
290
+
291
+ ## Allowed Characters
292
+
293
+ By default, params are encoded with `encodeURIComponent`. Allow extra characters via router config:
294
+
295
+ ```tsx
296
+ import { createRouter } from "@tanstack/react-router";
297
+
298
+ const router = createRouter({
299
+ routeTree,
300
+ pathParamsAllowedCharacters: ["@", "+"],
301
+ });
302
+ ```
303
+
304
+ Allowed characters: `;`, `:`, `@`, `&`, `=`, `+`, `$`, `,`.
305
+
306
+ ## Common Mistakes
307
+
308
+ ### 1. CRITICAL (cross-skill): Interpolating path params into `to` string
309
+
310
+ ```tsx
311
+ // WRONG — breaks type safety and param encoding
312
+ <Link to={`/posts/${postId}`}>Post</Link>
313
+
314
+ // CORRECT — use params prop
315
+ <Link to="/posts/$postId" params={{ postId }}>Post</Link>
316
+ ```
317
+
318
+ ### 2. MEDIUM: Using `*` for splat routes instead of `$`
319
+
320
+ TanStack Router uses `$` for splat routes. The captured value is under `_splat`, not `*`.
321
+
322
+ ```tsx
323
+ // WRONG (React Router / other frameworks)
324
+ // <Route path="/files/*" />
325
+
326
+ // CORRECT (TanStack Router)
327
+ // File: src/routes/files.$.tsx
328
+ export const Route = createFileRoute("/files/$")({
329
+ component: () => {
330
+ const { _splat } = Route.useParams();
331
+ return <div>{_splat}</div>;
332
+ },
333
+ });
334
+ ```
335
+
336
+ > Note: `*` works in v1 for backwards compatibility but will be removed in v2. Always use `_splat`.
337
+
338
+ ### 3. MEDIUM: Using curly braces for basic dynamic segments
339
+
340
+ Curly braces are ONLY for prefix/suffix patterns and optional params. Basic dynamic segments use bare `$`.
341
+
342
+ ```tsx
343
+ // WRONG — braces not needed for basic params
344
+ createFileRoute("/posts/{$postId}");
345
+
346
+ // CORRECT — bare $ for basic dynamic segments
347
+ createFileRoute("/posts/$postId");
348
+
349
+ // CORRECT — braces for prefix pattern
350
+ createFileRoute("/posts/post-{$postId}");
351
+
352
+ // CORRECT — braces for optional param
353
+ createFileRoute("/posts/{-$category}");
354
+ ```
355
+
356
+ ### 4. Params are always strings
357
+
358
+ Path params are always parsed as strings. If you need a number, parse in the loader or component:
359
+
360
+ ```tsx
361
+ export const Route = createFileRoute("/posts/$postId")({
362
+ loader: async ({ params }) => {
363
+ const id = parseInt(params.postId, 10);
364
+ if (isNaN(id)) throw notFound();
365
+ return fetchPost(id);
366
+ },
367
+ });
368
+ ```
369
+
370
+ You can also use `params.parse` and `params.stringify` on the route for bidirectional transformation:
371
+
372
+ ```tsx
373
+ export const Route = createFileRoute("/posts/$postId")({
374
+ params: {
375
+ parse: (raw) => ({ postId: parseInt(raw.postId, 10) }),
376
+ stringify: (parsed) => ({ postId: String(parsed.postId) }),
377
+ },
378
+ loader: async ({ params }) => {
379
+ // params.postId is now number
380
+ return fetchPost(params.postId);
381
+ },
382
+ });
383
+ ```
@@ -0,0 +1,343 @@
1
+ ---
2
+ name: search-params
3
+ description: >-
4
+ validateSearch, search param validation with Zod/Valibot/ArkType adapters,
5
+ fallback(), search middlewares (retainSearchParams, stripSearchParams),
6
+ custom serialization (parseSearch, stringifySearch), search param
7
+ inheritance, loaderDeps for cache keys, reading and writing search params.
8
+ metadata:
9
+ type: sub-skill
10
+ library: tanstack-router
11
+ library_version: "1.171.15"
12
+ requires:
13
+ - router-core
14
+ sources:
15
+ - TanStack/router:docs/router/guide/search-params.md
16
+ - TanStack/router:docs/router/how-to/setup-basic-search-params.md
17
+ - TanStack/router:docs/router/how-to/validate-search-params.md
18
+ - TanStack/router:docs/router/how-to/navigate-with-search-params.md
19
+ - TanStack/router:docs/router/how-to/share-search-params-across-routes.md
20
+ - TanStack/router:docs/router/guide/custom-search-param-serialization.md
21
+ ---
22
+
23
+ # Search Params
24
+
25
+ TanStack Router treats search params as JSON-first application state. They are automatically parsed from the URL into structured objects (numbers, booleans, arrays, nested objects) and validated via `validateSearch` on each route.
26
+
27
+ > **CRITICAL**: When using `zodValidator()` and Zod v3, use `fallback()` from `@tanstack/zod-adapter`, NOT zod's `.catch()`. Using `.catch()` with the zod adapter makes the output type `unknown`, destroying type safety. This does not apply to Valibot or ArkType (which use their own fallback mechanisms). It also does not apply to Zod v4, which should use `.catch()` and not use the `zodValidator()`.
28
+ > **CRITICAL**: Types are fully inferred. Never annotate the return of `useSearch()`.
29
+
30
+ ## Setup: Zod Adapter (Recommended)
31
+
32
+ ```bash
33
+ npm install zod @tanstack/zod-adapter
34
+ ```
35
+
36
+ ```tsx
37
+ // src/routes/products.tsx
38
+ import { createFileRoute } from "@tanstack/react-router";
39
+ import { z } from "zod";
40
+
41
+ const productSearchSchema = z.object({
42
+ page: z.number().default(1).catch(1),
43
+ filter: z.string().default(""),
44
+ sort: z.enum(["newest", "oldest", "price"]).default("newest").catch("newest"),
45
+ });
46
+
47
+ export const Route = createFileRoute("/products")({
48
+ validateSearch: productSearchSchema,
49
+ component: ProductsPage,
50
+ });
51
+
52
+ function ProductsPage() {
53
+ // page: number, filter: string, sort: 'newest' | 'oldest' | 'price'
54
+ // ALL INFERRED — do not annotate
55
+ const { page, filter, sort } = Route.useSearch();
56
+
57
+ return (
58
+ <div>
59
+ <p>
60
+ Page {page}, filter: {filter}, sort: {sort}
61
+ </p>
62
+ </div>
63
+ );
64
+ }
65
+ ```
66
+
67
+ ## Reading Search Params
68
+
69
+ ### In Route Components: `Route.useSearch()`
70
+
71
+ ```tsx
72
+ function ProductsPage() {
73
+ const { page, sort } = Route.useSearch();
74
+ return <div>Page {page}</div>;
75
+ }
76
+ ```
77
+
78
+ ### In Code-Split Components: `getRouteApi()`
79
+
80
+ ```tsx
81
+ import { getRouteApi } from "@tanstack/react-router";
82
+
83
+ const routeApi = getRouteApi("/products");
84
+
85
+ function ProductFilters() {
86
+ const { sort } = routeApi.useSearch();
87
+ return <select value={sort}>{/* options */}</select>;
88
+ }
89
+ ```
90
+
91
+ ### From Any Component: `useSearch({ from })`
92
+
93
+ ```tsx
94
+ import { useSearch } from "@tanstack/react-router";
95
+
96
+ function SortIndicator() {
97
+ const { sort } = useSearch({ from: "/products" });
98
+ return <span>Sorted by: {sort}</span>;
99
+ }
100
+ ```
101
+
102
+ ### Loose Access: `useSearch({ strict: false })`
103
+
104
+ ```tsx
105
+ function GenericPaginator() {
106
+ const search = useSearch({ strict: false });
107
+ // search.page is number | undefined (union of all routes)
108
+ return <span>Page: {search.page ?? 1}</span>;
109
+ }
110
+ ```
111
+
112
+ ## Writing Search Params
113
+
114
+ ### Link with Function Form (Preserves Existing Params)
115
+
116
+ ```tsx
117
+ import { Link } from "@tanstack/react-router";
118
+
119
+ function Pagination() {
120
+ return (
121
+ <Link from="/products" search={(prev) => ({ ...prev, page: prev.page + 1 })}>
122
+ Next Page
123
+ </Link>
124
+ );
125
+ }
126
+ ```
127
+
128
+ ### Link with Object Form (Replaces All Params)
129
+
130
+ ```tsx
131
+ <Link to="/products" search={{ page: 1, filter: "", sort: "newest" }}>
132
+ Reset
133
+ </Link>
134
+ ```
135
+
136
+ ### Programmatic: `useNavigate()`
137
+
138
+ ```tsx
139
+ import { useNavigate } from "@tanstack/react-router";
140
+
141
+ function SortDropdown() {
142
+ const navigate = useNavigate({ from: "/products" });
143
+
144
+ return (
145
+ <select
146
+ onChange={(e) => {
147
+ navigate({
148
+ search: (prev) => ({ ...prev, sort: e.target.value, page: 1 }),
149
+ });
150
+ }}
151
+ >
152
+ <option value="newest">Newest</option>
153
+ <option value="price">Price</option>
154
+ </select>
155
+ );
156
+ }
157
+ ```
158
+
159
+ ## Search Param Inheritance
160
+
161
+ Parent route search params are automatically merged into child routes:
162
+
163
+ ```tsx
164
+ // src/routes/shop.tsx — parent defines shared params
165
+ import { createFileRoute } from "@tanstack/react-router";
166
+ import { z } from "zod";
167
+
168
+ const shopSearchSchema = z.object({
169
+ currency: z.enum(["USD", "EUR"]).default("USD").catch("USD"),
170
+ });
171
+
172
+ export const Route = createFileRoute("/shop")({
173
+ validateSearch: shopSearchSchema,
174
+ });
175
+ ```
176
+
177
+ ```tsx
178
+ // src/routes/shop/products.tsx — child inherits currency
179
+ import { createFileRoute } from "@tanstack/react-router";
180
+
181
+ export const Route = createFileRoute("/shop/products")({
182
+ component: ShopProducts,
183
+ });
184
+
185
+ function ShopProducts() {
186
+ // currency is available here from parent — fully typed
187
+ const { currency } = Route.useSearch();
188
+ return <div>Currency: {currency}</div>;
189
+ }
190
+ ```
191
+
192
+ ## Search Middlewares
193
+
194
+ ### `retainSearchParams` — Keep Params Across Navigation
195
+
196
+ ```tsx
197
+ import { createRootRoute, retainSearchParams } from "@tanstack/react-router";
198
+ import { z } from "zod";
199
+
200
+ const rootSearchSchema = z.object({
201
+ debug: z.boolean().optional(),
202
+ });
203
+
204
+ export const Route = createRootRoute({
205
+ validateSearch: rootSearchSchema,
206
+ search: {
207
+ middlewares: [retainSearchParams(["debug"])],
208
+ },
209
+ });
210
+ ```
211
+
212
+ ### `stripSearchParams` — Remove Default Values from URL
213
+
214
+ ```tsx
215
+ import { createFileRoute, stripSearchParams } from "@tanstack/react-router";
216
+ import { z } from "zod";
217
+
218
+ const defaults = { sort: "newest", page: 1 };
219
+
220
+ const searchSchema = z.object({
221
+ sort: z.string().default(defaults.sort),
222
+ page: z.number().default(defaults.page),
223
+ });
224
+
225
+ export const Route = createFileRoute("/items")({
226
+ validateSearch: searchSchema,
227
+ search: {
228
+ middlewares: [stripSearchParams(defaults)],
229
+ },
230
+ });
231
+ ```
232
+
233
+ ### Chaining Middlewares
234
+
235
+ ```tsx
236
+ export const Route = createFileRoute("/search")({
237
+ validateSearch: z.object({
238
+ retainMe: z.string().optional(),
239
+ arrayWithDefaults: z.string().array().default(["foo", "bar"]),
240
+ required: z.string(),
241
+ }),
242
+ search: {
243
+ middlewares: [
244
+ retainSearchParams(["retainMe"]),
245
+ stripSearchParams({ arrayWithDefaults: ["foo", "bar"] }),
246
+ ],
247
+ },
248
+ });
249
+ ```
250
+
251
+ ## Custom Serialization
252
+
253
+ Override the default JSON serialization at the router level:
254
+
255
+ ```tsx
256
+ import { createRouter, parseSearchWith, stringifySearchWith } from "@tanstack/react-router";
257
+
258
+ const router = createRouter({
259
+ routeTree,
260
+ // Example: use JSURL2 for compact, human-readable URLs
261
+ parseSearch: parseSearchWith(parse),
262
+ stringifySearch: stringifySearchWith(stringify),
263
+ });
264
+ ```
265
+
266
+ ## Using Search Params in Loaders via `loaderDeps`
267
+
268
+ ```tsx
269
+ export const Route = createFileRoute("/products")({
270
+ validateSearch: productSearchSchema,
271
+ // Pick ONLY the params the loader needs — not the entire search object
272
+ loaderDeps: ({ search }) => ({ page: search.page }),
273
+ loader: async ({ deps }) => {
274
+ return fetchProducts({ page: deps.page });
275
+ },
276
+ });
277
+ ```
278
+
279
+ ## Common Mistakes
280
+
281
+ ### 1. HIGH: Using zod v3's `.catch()` with `zodValidator()` instead of adapter `fallback()`
282
+
283
+ ```tsx
284
+ // WRONG — .catch() with zodValidator makes the type unknown
285
+ const schema = z.object({ page: z.number().catch(1) });
286
+ validateSearch: zodValidator(schema); // page is typed as unknown!
287
+
288
+ // CORRECT — fallback() preserves the inferred type
289
+ import { fallback } from "@tanstack/zod-adapter";
290
+ const schema = z.object({ page: fallback(z.number(), 1) });
291
+ ```
292
+
293
+ **Important:** This only applies when using Zod v3, not when using Zod v4. For v4, using `.catch()` is correct.
294
+
295
+ ### 2. HIGH: Returning entire search object from `loaderDeps`
296
+
297
+ ```tsx
298
+ // WRONG — loader re-runs on ANY search param change
299
+ loaderDeps: ({ search }) => search;
300
+
301
+ // CORRECT — loader only re-runs when page changes
302
+ loaderDeps: ({ search }) => ({ page: search.page });
303
+ ```
304
+
305
+ ### 3. HIGH: Passing Date objects in search params
306
+
307
+ ```tsx
308
+ // WRONG — Date does not serialize correctly to JSON in URLs
309
+ <Link search={{ startDate: new Date() }}>
310
+
311
+ // CORRECT — convert to ISO string
312
+ <Link search={{ startDate: new Date().toISOString() }}>
313
+ ```
314
+
315
+ ### 4. MEDIUM: Parent route missing `validateSearch` blocks inheritance
316
+
317
+ ```tsx
318
+ // WRONG — child cannot access shared params
319
+ export const Route = createRootRoute({
320
+ component: RootComponent,
321
+ // no validateSearch!
322
+ });
323
+
324
+ // CORRECT — parent must define validateSearch for children to inherit
325
+ export const Route = createRootRoute({
326
+ validateSearch: globalSearchSchema,
327
+ component: RootComponent,
328
+ });
329
+ ```
330
+
331
+ ### 5. HIGH (cross-skill): Using search as object instead of function loses params
332
+
333
+ ```tsx
334
+ // WRONG — replaces ALL search params, losing any existing ones
335
+ <Link to="." search={{ page: 2 }}>Page 2</Link>
336
+
337
+ // CORRECT — preserves existing params, updates only page
338
+ <Link to="." search={(prev) => ({ ...prev, page: 2 })}>Page 2</Link>
339
+ ```
340
+
341
+ ## References
342
+
343
+ - [Validation Patterns Reference](https://tanstack.com/router/latest/docs/framework/react/guide/search-params) — comprehensive patterns for all validation libraries