@tanstack/react-router 1.168.1 → 1.168.2

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 (40) hide show
  1. package/dist/cjs/Match.cjs +37 -14
  2. package/dist/cjs/Match.cjs.map +1 -1
  3. package/dist/cjs/Matches.cjs +2 -4
  4. package/dist/cjs/Matches.cjs.map +1 -1
  5. package/dist/cjs/fileRoute.cjs +4 -6
  6. package/dist/cjs/fileRoute.cjs.map +1 -1
  7. package/dist/cjs/renderRouteNotFound.cjs +3 -3
  8. package/dist/cjs/renderRouteNotFound.cjs.map +1 -1
  9. package/dist/cjs/route.cjs +0 -2
  10. package/dist/cjs/route.cjs.map +1 -1
  11. package/dist/cjs/useMatch.cjs +8 -4
  12. package/dist/cjs/useMatch.cjs.map +1 -1
  13. package/dist/cjs/useRouter.cjs +3 -3
  14. package/dist/cjs/useRouter.cjs.map +1 -1
  15. package/dist/esm/Match.js +38 -13
  16. package/dist/esm/Match.js.map +1 -1
  17. package/dist/esm/Matches.js +2 -3
  18. package/dist/esm/Matches.js.map +1 -1
  19. package/dist/esm/fileRoute.js +4 -4
  20. package/dist/esm/fileRoute.js.map +1 -1
  21. package/dist/esm/renderRouteNotFound.js +3 -2
  22. package/dist/esm/renderRouteNotFound.js.map +1 -1
  23. package/dist/esm/route.js +0 -2
  24. package/dist/esm/route.js.map +1 -1
  25. package/dist/esm/useMatch.js +9 -4
  26. package/dist/esm/useMatch.js.map +1 -1
  27. package/dist/esm/useRouter.js +3 -2
  28. package/dist/esm/useRouter.js.map +1 -1
  29. package/dist/llms/rules/api.d.ts +1 -1
  30. package/dist/llms/rules/api.js +10 -10
  31. package/dist/llms/rules/guide.d.ts +1 -1
  32. package/dist/llms/rules/guide.js +27 -6
  33. package/package.json +2 -4
  34. package/src/Match.tsx +77 -24
  35. package/src/Matches.tsx +3 -5
  36. package/src/fileRoute.ts +7 -9
  37. package/src/renderRouteNotFound.tsx +6 -6
  38. package/src/route.tsx +0 -2
  39. package/src/useMatch.tsx +19 -10
  40. package/src/useRouter.tsx +7 -5
@@ -8384,7 +8384,9 @@ For validation libraries we recommend using adapters which infer the correct \`i
8384
8384
 
8385
8385
  ### Zod
8386
8386
 
8387
- An adapter is provided for [Zod](https://zod.dev/) which will pipe through the correct \`input\` type and \`output\` type
8387
+ An adapter is provided for [Zod](https://zod.dev/) which will pipe through the correct \`input\` type and \`output\` type.
8388
+
8389
+ For Zod v3:
8388
8390
 
8389
8391
  \`\`\`tsx
8390
8392
  import { zodValidator } from '@tanstack/zod-adapter'
@@ -8401,13 +8403,30 @@ export const Route = createFileRoute('/shop/products/')({
8401
8403
  })
8402
8404
  \`\`\`
8403
8405
 
8404
- The important part here is the following use of \`Link\` no longer requires \`search\` params
8406
+ With Zod v4, you should directly use the schema in \`validateSearch\`:
8407
+
8408
+ \`\`\`tsx
8409
+ import { z } from 'zod'
8410
+
8411
+ const productSearchSchema = z.object({
8412
+ page: z.number().default(1),
8413
+ filter: z.string().default(''),
8414
+ sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
8415
+ })
8416
+
8417
+ export const Route = createFileRoute('/shop/products/')({
8418
+ // With Zod v4, we can use the schema without the adapter
8419
+ validateSearch: productSearchSchema,
8420
+ })
8421
+ \`\`\`
8422
+
8423
+ The important part here is the following use of \`Link\` no longer requires \`search\` params:
8405
8424
 
8406
8425
  \`\`\`tsx
8407
8426
  <Link to="/shop/products" />
8408
8427
  \`\`\`
8409
8428
 
8410
- However the use of \`catch\` here overrides the types and makes \`page\`, \`filter\` and \`sort\` \`unknown\` causing type loss. We have handled this case by providing a \`fallback\` generic function which retains the types but provides a \`fallback\` value when validation fails
8429
+ In Zod v3, the use of \`catch\` here overrides the types and makes \`page\`, \`filter\` and \`sort\` \`unknown\` causing type loss. We have handled this case by providing a \`fallback\` generic function which retains the types but provides a \`fallback\` value when validation fails:
8411
8430
 
8412
8431
  \`\`\`tsx
8413
8432
  import { fallback, zodValidator } from '@tanstack/zod-adapter'
@@ -8428,7 +8447,9 @@ export const Route = createFileRoute('/shop/products/')({
8428
8447
 
8429
8448
  Therefore when navigating to this route, \`search\` is optional and retains the correct types.
8430
8449
 
8431
- While not recommended, it is also possible to configure \`input\` and \`output\` type in case the \`output\` type is more accurate than the \`input\` type
8450
+ In Zod v4, schemas may use \`catch\` instead of the fallback and will retain type inference throughout.
8451
+
8452
+ While not recommended, it is also possible to configure \`input\` and \`output\` type in case the \`output\` type is more accurate than the \`input\` type:
8432
8453
 
8433
8454
  \`\`\`tsx
8434
8455
  const productSearchSchema = z.object({
@@ -8645,7 +8666,7 @@ Now that you've learned how to read your route's search params, you'll be happy
8645
8666
 
8646
8667
  The best way to update search params is to use the \`search\` prop on the \`<Link />\` component.
8647
8668
 
8648
- If the search for the current page shall be updated and the \`from\` prop is specified, the \`to\` prop can be omitted.
8669
+ If the search for the current page shall be updated and the \`from\` prop is specified, the \`to\` prop can be omitted.
8649
8670
  Here's an example:
8650
8671
 
8651
8672
  \`\`\`tsx title="src/routes/shop/products.tsx"
@@ -8666,7 +8687,7 @@ const ProductList = () => {
8666
8687
 
8667
8688
  If you want to update the search params in a generic component that is rendered on multiple routes, specifying \`from\` can be challenging.
8668
8689
 
8669
- In this scenario you can set \`to="."\` which will give you access to loosely typed search params.
8690
+ In this scenario you can set \`to="."\` which will give you access to loosely typed search params.
8670
8691
  Here is an example that illustrates this:
8671
8692
 
8672
8693
  \`\`\`tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.168.1",
3
+ "version": "1.168.2",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -79,10 +79,8 @@
79
79
  "dependencies": {
80
80
  "@tanstack/react-store": "^0.9.2",
81
81
  "isbot": "^5.1.22",
82
- "tiny-invariant": "^1.3.3",
83
- "tiny-warning": "^1.0.3",
84
82
  "@tanstack/history": "1.161.6",
85
- "@tanstack/router-core": "1.168.1"
83
+ "@tanstack/router-core": "1.168.2"
86
84
  },
87
85
  "devDependencies": {
88
86
  "@testing-library/jest-dom": "^6.6.3",
package/src/Match.tsx CHANGED
@@ -1,10 +1,9 @@
1
1
  import * as React from 'react'
2
2
  import { useStore } from '@tanstack/react-store'
3
- import invariant from 'tiny-invariant'
4
- import warning from 'tiny-warning'
5
3
  import {
6
4
  createControlledPromise,
7
5
  getLocationChangeInfo,
6
+ invariant,
8
7
  isNotFound,
9
8
  isRedirect,
10
9
  rootRouteId,
@@ -33,10 +32,15 @@ export const Match = React.memo(function MatchImpl({
33
32
 
34
33
  if (isServer ?? router.isServer) {
35
34
  const match = router.stores.activeMatchStoresById.get(matchId)?.state
36
- invariant(
37
- match,
38
- `Could not find match for matchId "${matchId}". Please file an issue!`,
39
- )
35
+ if (!match) {
36
+ if (process.env.NODE_ENV !== 'production') {
37
+ throw new Error(
38
+ `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`,
39
+ )
40
+ }
41
+
42
+ invariant()
43
+ }
40
44
 
41
45
  const routeId = match.routeId as string
42
46
  const parentRouteId = (router.routesById[routeId] as AnyRoute).parentRoute
@@ -62,10 +66,15 @@ export const Match = React.memo(function MatchImpl({
62
66
  // and reconcileMatchPool reuses stores for the same matchId.
63
67
  // eslint-disable-next-line react-hooks/rules-of-hooks
64
68
  const matchStore = router.stores.activeMatchStoresById.get(matchId)
65
- invariant(
66
- matchStore,
67
- `Could not find match for matchId "${matchId}". Please file an issue!`,
68
- )
69
+ if (!matchStore) {
70
+ if (process.env.NODE_ENV !== 'production') {
71
+ throw new Error(
72
+ `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`,
73
+ )
74
+ }
75
+
76
+ invariant()
77
+ }
69
78
  // eslint-disable-next-line react-hooks/rules-of-hooks
70
79
  const resetKey = useStore(router.stores.loadedAt, (loadedAt) => loadedAt)
71
80
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -162,7 +171,9 @@ function MatchView({
162
171
  onCatch={(error, errorInfo) => {
163
172
  // Forward not found errors (we don't want to show the error component for these)
164
173
  if (isNotFound(error)) throw error
165
- warning(false, `Error in route match: ${matchId}`)
174
+ if (process.env.NODE_ENV !== 'production') {
175
+ console.warn(`Warning: Error in route match: ${matchId}`)
176
+ }
166
177
  routeOnCatch?.(error, errorInfo)
167
178
  }}
168
179
  >
@@ -249,10 +260,15 @@ export const MatchInner = React.memo(function MatchInnerImpl({
249
260
 
250
261
  if (isServer ?? router.isServer) {
251
262
  const match = router.stores.activeMatchStoresById.get(matchId)?.state
252
- invariant(
253
- match,
254
- `Could not find match for matchId "${matchId}". Please file an issue!`,
255
- )
263
+ if (!match) {
264
+ if (process.env.NODE_ENV !== 'production') {
265
+ throw new Error(
266
+ `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`,
267
+ )
268
+ }
269
+
270
+ invariant()
271
+ }
256
272
 
257
273
  const routeId = match.routeId as string
258
274
  const route = router.routesById[routeId] as AnyRoute
@@ -282,12 +298,24 @@ export const MatchInner = React.memo(function MatchInnerImpl({
282
298
  }
283
299
 
284
300
  if (match.status === 'notFound') {
285
- invariant(isNotFound(match.error), 'Expected a notFound error')
301
+ if (!isNotFound(match.error)) {
302
+ if (process.env.NODE_ENV !== 'production') {
303
+ throw new Error('Invariant failed: Expected a notFound error')
304
+ }
305
+
306
+ invariant()
307
+ }
286
308
  return renderRouteNotFound(router, route, match.error)
287
309
  }
288
310
 
289
311
  if (match.status === 'redirected') {
290
- invariant(isRedirect(match.error), 'Expected a redirect error')
312
+ if (!isRedirect(match.error)) {
313
+ if (process.env.NODE_ENV !== 'production') {
314
+ throw new Error('Invariant failed: Expected a redirect error')
315
+ }
316
+
317
+ invariant()
318
+ }
291
319
  throw router.getMatch(match.id)?._nonReactive.loadPromise
292
320
  }
293
321
 
@@ -312,10 +340,15 @@ export const MatchInner = React.memo(function MatchInnerImpl({
312
340
 
313
341
  // eslint-disable-next-line react-hooks/rules-of-hooks
314
342
  const matchStore = router.stores.activeMatchStoresById.get(matchId)
315
- invariant(
316
- matchStore,
317
- `Could not find match for matchId "${matchId}". Please file an issue!`,
318
- )
343
+ if (!matchStore) {
344
+ if (process.env.NODE_ENV !== 'production') {
345
+ throw new Error(
346
+ `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`,
347
+ )
348
+ }
349
+
350
+ invariant()
351
+ }
319
352
  // eslint-disable-next-line react-hooks/rules-of-hooks
320
353
  const match = useStore(matchStore, (value) => value)
321
354
  const routeId = match.routeId as string
@@ -384,14 +417,26 @@ export const MatchInner = React.memo(function MatchInnerImpl({
384
417
  }
385
418
 
386
419
  if (match.status === 'notFound') {
387
- invariant(isNotFound(match.error), 'Expected a notFound error')
420
+ if (!isNotFound(match.error)) {
421
+ if (process.env.NODE_ENV !== 'production') {
422
+ throw new Error('Invariant failed: Expected a notFound error')
423
+ }
424
+
425
+ invariant()
426
+ }
388
427
  return renderRouteNotFound(router, route, match.error)
389
428
  }
390
429
 
391
430
  if (match.status === 'redirected') {
392
431
  // Redirects should be handled by the router transition. If we happen to
393
432
  // encounter a redirect here, it's a bug. Let's warn, but render nothing.
394
- invariant(isRedirect(match.error), 'Expected a redirect error')
433
+ if (!isRedirect(match.error)) {
434
+ if (process.env.NODE_ENV !== 'production') {
435
+ throw new Error('Invariant failed: Expected a redirect error')
436
+ }
437
+
438
+ invariant()
439
+ }
395
440
 
396
441
  // warning(
397
442
  // false,
@@ -479,7 +524,15 @@ export const Outlet = React.memo(function OutletImpl() {
479
524
  ) : null
480
525
 
481
526
  if (parentGlobalNotFound) {
482
- invariant(route, 'Could not resolve route for Outlet render')
527
+ if (!route) {
528
+ if (process.env.NODE_ENV !== 'production') {
529
+ throw new Error(
530
+ 'Invariant failed: Could not resolve route for Outlet render',
531
+ )
532
+ }
533
+
534
+ invariant()
535
+ }
483
536
  return renderRouteNotFound(router, route, undefined)
484
537
  }
485
538
 
package/src/Matches.tsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as React from 'react'
2
- import warning from 'tiny-warning'
3
2
  import { useStore } from '@tanstack/react-store'
4
3
  import { replaceEqualDeep, rootRouteId } from '@tanstack/router-core'
5
4
  import { isServer } from '@tanstack/router-core/isServer'
@@ -100,11 +99,10 @@ function MatchesInner() {
100
99
  onCatch={
101
100
  process.env.NODE_ENV !== 'production'
102
101
  ? (error) => {
103
- warning(
104
- false,
105
- `The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
102
+ console.warn(
103
+ `Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
106
104
  )
107
- warning(false, error.message || error.toString())
105
+ console.warn(`Warning: ${error.message || error.toString()}`)
108
106
  }
109
107
  : undefined
110
108
  }
package/src/fileRoute.ts CHANGED
@@ -1,4 +1,3 @@
1
- import warning from 'tiny-warning'
2
1
  import { createRoute } from './route'
3
2
 
4
3
  import { useMatch } from './useMatch'
@@ -151,10 +150,11 @@ export class FileRoute<
151
150
  THandlers
152
151
  > => {
153
152
  if (process.env.NODE_ENV !== 'production') {
154
- warning(
155
- this.silent,
156
- 'FileRoute is deprecated and will be removed in the next major version. Use the createFileRoute(path)(options) function instead.',
157
- )
153
+ if (!this.silent) {
154
+ console.warn(
155
+ 'Warning: FileRoute is deprecated and will be removed in the next major version. Use the createFileRoute(path)(options) function instead.',
156
+ )
157
+ }
158
158
  }
159
159
  const route = createRoute(options as any)
160
160
  ;(route as any).isRoot = false
@@ -187,9 +187,8 @@ export function FileRouteLoader<
187
187
  >,
188
188
  ) => TLoaderFn {
189
189
  if (process.env.NODE_ENV !== 'production') {
190
- warning(
191
- false,
192
- `FileRouteLoader is deprecated and will be removed in the next major version. Please place the loader function in the the main route file, inside the \`createFileRoute('/path/to/file')(options)\` options`,
190
+ console.warn(
191
+ `Warning: FileRouteLoader is deprecated and will be removed in the next major version. Please place the loader function in the the main route file, inside the \`createFileRoute('/path/to/file')(options)\` options`,
193
192
  )
194
193
  }
195
194
  return (loaderFn) => loaderFn as any
@@ -218,7 +217,6 @@ export class LazyRoute<TRoute extends AnyRoute> {
218
217
  } & LazyRouteOptions,
219
218
  ) {
220
219
  this.options = opts
221
- ;(this as any).$$typeof = Symbol.for('react.memo')
222
220
  }
223
221
 
224
222
  useMatch: UseMatchRoute<TRoute['id']> = (opts) => {
@@ -1,5 +1,4 @@
1
1
  import * as React from 'react'
2
- import warning from 'tiny-warning'
3
2
  import { DefaultGlobalNotFound } from './not-found'
4
3
  import type { AnyRoute, AnyRouter } from '@tanstack/router-core'
5
4
 
@@ -21,11 +20,12 @@ export function renderRouteNotFound(
21
20
  return <router.options.defaultNotFoundComponent {...data} />
22
21
  }
23
22
 
24
- if (process.env.NODE_ENV === 'development') {
25
- warning(
26
- route.options.notFoundComponent,
27
- `A notFoundError was encountered on the route with ID "${route.id}", but a notFoundComponent option was not configured, nor was a router level defaultNotFoundComponent configured. Consider configuring at least one of these to avoid TanStack Router's overly generic defaultNotFoundComponent (<p>Not Found</p>)`,
28
- )
23
+ if (process.env.NODE_ENV !== 'production') {
24
+ if (!route.options.notFoundComponent) {
25
+ console.warn(
26
+ `Warning: A notFoundError was encountered on the route with ID "${route.id}", but a notFoundComponent option was not configured, nor was a router level defaultNotFoundComponent configured. Consider configuring at least one of these to avoid TanStack Router's overly generic defaultNotFoundComponent (<p>Not Found</p>)`,
27
+ )
28
+ }
29
29
  }
30
30
 
31
31
  return <DefaultGlobalNotFound />
package/src/route.tsx CHANGED
@@ -258,7 +258,6 @@ export class Route<
258
258
  >,
259
259
  ) {
260
260
  super(options)
261
- ;(this as any).$$typeof = Symbol.for('react.memo')
262
261
  }
263
262
 
264
263
  useMatch: UseMatchRoute<TId> = (opts) => {
@@ -530,7 +529,6 @@ export class RootRoute<
530
529
  >,
531
530
  ) {
532
531
  super(options)
533
- ;(this as any).$$typeof = Symbol.for('react.memo')
534
532
  }
535
533
 
536
534
  useMatch: UseMatchRoute<RootRouteId> = (opts) => {
package/src/useMatch.tsx CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as React from 'react'
2
2
  import { useStore } from '@tanstack/react-store'
3
- import { replaceEqualDeep } from '@tanstack/router-core'
3
+ import { invariant, replaceEqualDeep } from '@tanstack/router-core'
4
4
  import { isServer } from '@tanstack/router-core/isServer'
5
- import invariant from 'tiny-invariant'
6
5
  import { dummyMatchContext, matchContext } from './matchContext'
7
6
  import { useRouter } from './useRouter'
8
7
  import type {
@@ -119,10 +118,15 @@ export function useMatch<
119
118
 
120
119
  if (isServer ?? router.isServer) {
121
120
  const match = matchStore?.state
122
- invariant(
123
- !((opts.shouldThrow ?? true) && !match),
124
- `Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
125
- )
121
+ if ((opts.shouldThrow ?? true) && !match) {
122
+ if (process.env.NODE_ENV !== 'production') {
123
+ throw new Error(
124
+ `Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
125
+ )
126
+ }
127
+
128
+ invariant()
129
+ }
126
130
 
127
131
  if (match === undefined) {
128
132
  return undefined as any
@@ -139,10 +143,15 @@ export function useMatch<
139
143
 
140
144
  // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
141
145
  return useStore(matchStore ?? dummyStore, (match) => {
142
- invariant(
143
- !((opts.shouldThrow ?? true) && !match),
144
- `Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
145
- )
146
+ if ((opts.shouldThrow ?? true) && !match) {
147
+ if (process.env.NODE_ENV !== 'production') {
148
+ throw new Error(
149
+ `Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
150
+ )
151
+ }
152
+
153
+ invariant()
154
+ }
146
155
 
147
156
  if (match === undefined) {
148
157
  return undefined
package/src/useRouter.tsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as React from 'react'
2
- import warning from 'tiny-warning'
3
2
  import { routerContext } from './routerContext'
4
3
  import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core'
5
4
 
@@ -17,9 +16,12 @@ export function useRouter<TRouter extends AnyRouter = RegisteredRouter>(opts?: {
17
16
  warn?: boolean
18
17
  }): TRouter {
19
18
  const value = React.useContext(routerContext)
20
- warning(
21
- !((opts?.warn ?? true) && !value),
22
- 'useRouter must be used inside a <RouterProvider> component!',
23
- )
19
+ if (process.env.NODE_ENV !== 'production') {
20
+ if ((opts?.warn ?? true) && !value) {
21
+ console.warn(
22
+ 'Warning: useRouter must be used inside a <RouterProvider> component!',
23
+ )
24
+ }
25
+ }
24
26
  return value as any
25
27
  }