@tanstack/react-router 1.168.1 → 1.168.3

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 (61) hide show
  1. package/dist/cjs/HeadContent.cjs +2 -2
  2. package/dist/cjs/HeadContent.cjs.map +1 -1
  3. package/dist/cjs/HeadContent.d.cts +5 -1
  4. package/dist/cjs/HeadContent.dev.cjs +2 -2
  5. package/dist/cjs/HeadContent.dev.cjs.map +1 -1
  6. package/dist/cjs/HeadContent.dev.d.cts +2 -1
  7. package/dist/cjs/Match.cjs +37 -14
  8. package/dist/cjs/Match.cjs.map +1 -1
  9. package/dist/cjs/Matches.cjs +2 -4
  10. package/dist/cjs/Matches.cjs.map +1 -1
  11. package/dist/cjs/fileRoute.cjs +4 -6
  12. package/dist/cjs/fileRoute.cjs.map +1 -1
  13. package/dist/cjs/headContentUtils.cjs +11 -5
  14. package/dist/cjs/headContentUtils.cjs.map +1 -1
  15. package/dist/cjs/headContentUtils.d.cts +2 -2
  16. package/dist/cjs/renderRouteNotFound.cjs +3 -3
  17. package/dist/cjs/renderRouteNotFound.cjs.map +1 -1
  18. package/dist/cjs/route.cjs +0 -2
  19. package/dist/cjs/route.cjs.map +1 -1
  20. package/dist/cjs/useMatch.cjs +8 -4
  21. package/dist/cjs/useMatch.cjs.map +1 -1
  22. package/dist/cjs/useRouter.cjs +3 -3
  23. package/dist/cjs/useRouter.cjs.map +1 -1
  24. package/dist/esm/HeadContent.d.ts +5 -1
  25. package/dist/esm/HeadContent.dev.d.ts +2 -1
  26. package/dist/esm/HeadContent.dev.js +2 -2
  27. package/dist/esm/HeadContent.dev.js.map +1 -1
  28. package/dist/esm/HeadContent.js +2 -2
  29. package/dist/esm/HeadContent.js.map +1 -1
  30. package/dist/esm/Match.js +38 -13
  31. package/dist/esm/Match.js.map +1 -1
  32. package/dist/esm/Matches.js +2 -3
  33. package/dist/esm/Matches.js.map +1 -1
  34. package/dist/esm/fileRoute.js +4 -4
  35. package/dist/esm/fileRoute.js.map +1 -1
  36. package/dist/esm/headContentUtils.d.ts +2 -2
  37. package/dist/esm/headContentUtils.js +12 -6
  38. package/dist/esm/headContentUtils.js.map +1 -1
  39. package/dist/esm/renderRouteNotFound.js +3 -2
  40. package/dist/esm/renderRouteNotFound.js.map +1 -1
  41. package/dist/esm/route.js +0 -2
  42. package/dist/esm/route.js.map +1 -1
  43. package/dist/esm/useMatch.js +9 -4
  44. package/dist/esm/useMatch.js.map +1 -1
  45. package/dist/esm/useRouter.js +3 -2
  46. package/dist/esm/useRouter.js.map +1 -1
  47. package/dist/llms/rules/api.d.ts +1 -1
  48. package/dist/llms/rules/api.js +10 -10
  49. package/dist/llms/rules/guide.d.ts +1 -1
  50. package/dist/llms/rules/guide.js +45 -6
  51. package/package.json +2 -4
  52. package/src/HeadContent.dev.tsx +3 -2
  53. package/src/HeadContent.tsx +7 -2
  54. package/src/Match.tsx +78 -26
  55. package/src/Matches.tsx +3 -5
  56. package/src/fileRoute.ts +7 -9
  57. package/src/headContentUtils.tsx +29 -5
  58. package/src/renderRouteNotFound.tsx +6 -6
  59. package/src/route.tsx +0 -2
  60. package/src/useMatch.tsx +19 -10
  61. package/src/useRouter.tsx +7 -5
@@ -2923,6 +2923,24 @@ The \`<HeadContent />\` component is **required** to render the head, title, met
2923
2923
 
2924
2924
  It should be **rendered either in the \`<head>\` tag of your root layout or as high up in the component tree as possible** if your application doesn't or can't manage the \`<head>\` tag.
2925
2925
 
2926
+ For manifest-managed assets, you can also set \`crossorigin\` values on emitted
2927
+ \`modulepreload\` and stylesheet links:
2928
+
2929
+ \`\`\`tsx
2930
+ <HeadContent assetCrossOrigin="anonymous" />
2931
+
2932
+ <HeadContent
2933
+ assetCrossOrigin={{
2934
+ modulepreload: 'anonymous',
2935
+ stylesheet: 'use-credentials',
2936
+ }}
2937
+ />
2938
+ \`\`\`
2939
+
2940
+ \`assetCrossOrigin\` only applies to manifest-managed asset links emitted by Start.
2941
+ If you also set \`crossOrigin\` via \`transformAssets\` (either the object shorthand
2942
+ or a callback return value), \`assetCrossOrigin\` wins.
2943
+
2926
2944
  ### Start/Full-Stack Applications
2927
2945
 
2928
2946
  <!-- ::start:framework -->
@@ -8384,7 +8402,9 @@ For validation libraries we recommend using adapters which infer the correct \`i
8384
8402
 
8385
8403
  ### Zod
8386
8404
 
8387
- An adapter is provided for [Zod](https://zod.dev/) which will pipe through the correct \`input\` type and \`output\` type
8405
+ An adapter is provided for [Zod](https://zod.dev/) which will pipe through the correct \`input\` type and \`output\` type.
8406
+
8407
+ For Zod v3:
8388
8408
 
8389
8409
  \`\`\`tsx
8390
8410
  import { zodValidator } from '@tanstack/zod-adapter'
@@ -8401,13 +8421,30 @@ export const Route = createFileRoute('/shop/products/')({
8401
8421
  })
8402
8422
  \`\`\`
8403
8423
 
8404
- The important part here is the following use of \`Link\` no longer requires \`search\` params
8424
+ With Zod v4, you should directly use the schema in \`validateSearch\`:
8425
+
8426
+ \`\`\`tsx
8427
+ import { z } from 'zod'
8428
+
8429
+ const productSearchSchema = z.object({
8430
+ page: z.number().default(1),
8431
+ filter: z.string().default(''),
8432
+ sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
8433
+ })
8434
+
8435
+ export const Route = createFileRoute('/shop/products/')({
8436
+ // With Zod v4, we can use the schema without the adapter
8437
+ validateSearch: productSearchSchema,
8438
+ })
8439
+ \`\`\`
8440
+
8441
+ The important part here is the following use of \`Link\` no longer requires \`search\` params:
8405
8442
 
8406
8443
  \`\`\`tsx
8407
8444
  <Link to="/shop/products" />
8408
8445
  \`\`\`
8409
8446
 
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
8447
+ 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
8448
 
8412
8449
  \`\`\`tsx
8413
8450
  import { fallback, zodValidator } from '@tanstack/zod-adapter'
@@ -8428,7 +8465,9 @@ export const Route = createFileRoute('/shop/products/')({
8428
8465
 
8429
8466
  Therefore when navigating to this route, \`search\` is optional and retains the correct types.
8430
8467
 
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
8468
+ In Zod v4, schemas may use \`catch\` instead of the fallback and will retain type inference throughout.
8469
+
8470
+ 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
8471
 
8433
8472
  \`\`\`tsx
8434
8473
  const productSearchSchema = z.object({
@@ -8645,7 +8684,7 @@ Now that you've learned how to read your route's search params, you'll be happy
8645
8684
 
8646
8685
  The best way to update search params is to use the \`search\` prop on the \`<Link />\` component.
8647
8686
 
8648
- If the search for the current page shall be updated and the \`from\` prop is specified, the \`to\` prop can be omitted.
8687
+ If the search for the current page shall be updated and the \`from\` prop is specified, the \`to\` prop can be omitted.
8649
8688
  Here's an example:
8650
8689
 
8651
8690
  \`\`\`tsx title="src/routes/shop/products.tsx"
@@ -8666,7 +8705,7 @@ const ProductList = () => {
8666
8705
 
8667
8706
  If you want to update the search params in a generic component that is rendered on multiple routes, specifying \`from\` can be challenging.
8668
8707
 
8669
- In this scenario you can set \`to="."\` which will give you access to loosely typed search params.
8708
+ In this scenario you can set \`to="."\` which will give you access to loosely typed search params.
8670
8709
  Here is an example that illustrates this:
8671
8710
 
8672
8711
  \`\`\`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.3",
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.3"
86
84
  },
87
85
  "devDependencies": {
88
86
  "@testing-library/jest-dom": "^6.6.3",
@@ -3,6 +3,7 @@ import { Asset } from './Asset'
3
3
  import { useRouter } from './useRouter'
4
4
  import { useHydrated } from './ClientOnly'
5
5
  import { useTags } from './headContentUtils'
6
+ import type { HeadContentProps } from './HeadContent'
6
7
 
7
8
  const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'
8
9
 
@@ -15,8 +16,8 @@ const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'
15
16
  *
16
17
  * @link https://tanstack.com/router/latest/docs/framework/react/guide/document-head-management
17
18
  */
18
- export function HeadContent() {
19
- const tags = useTags()
19
+ export function HeadContent(props: HeadContentProps) {
20
+ const tags = useTags(props.assetCrossOrigin)
20
21
  const router = useRouter()
21
22
  const nonce = router.options.ssr?.nonce
22
23
  const hydrated = useHydrated()
@@ -2,14 +2,19 @@ import * as React from 'react'
2
2
  import { Asset } from './Asset'
3
3
  import { useRouter } from './useRouter'
4
4
  import { useTags } from './headContentUtils'
5
+ import type { AssetCrossOriginConfig } from '@tanstack/router-core'
6
+
7
+ export interface HeadContentProps {
8
+ assetCrossOrigin?: AssetCrossOriginConfig
9
+ }
5
10
 
6
11
  /**
7
12
  * Render route-managed head tags (title, meta, links, styles, head scripts).
8
13
  * Place inside the document head of your app shell.
9
14
  * @link https://tanstack.com/router/latest/docs/framework/react/guide/document-head-management
10
15
  */
11
- export function HeadContent() {
12
- const tags = useTags()
16
+ export function HeadContent(props: HeadContentProps) {
17
+ const tags = useTags(props.assetCrossOrigin)
13
18
  const router = useRouter()
14
19
  const nonce = router.options.ssr?.nonce
15
20
  return (
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
@@ -60,12 +64,17 @@ export const Match = React.memo(function MatchImpl({
60
64
  // Subscribe directly to the match store from the pool.
61
65
  // The matchId prop is stable for this component's lifetime (set by Outlet),
62
66
  // and reconcileMatchPool reuses stores for the same matchId.
63
- // eslint-disable-next-line react-hooks/rules-of-hooks
67
+
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
 
@@ -310,12 +338,16 @@ export const MatchInner = React.memo(function MatchInnerImpl({
310
338
  return out
311
339
  }
312
340
 
313
- // eslint-disable-next-line react-hooks/rules-of-hooks
314
341
  const matchStore = router.stores.activeMatchStoresById.get(matchId)
315
- invariant(
316
- matchStore,
317
- `Could not find match for matchId "${matchId}". Please file an issue!`,
318
- )
342
+ if (!matchStore) {
343
+ if (process.env.NODE_ENV !== 'production') {
344
+ throw new Error(
345
+ `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`,
346
+ )
347
+ }
348
+
349
+ invariant()
350
+ }
319
351
  // eslint-disable-next-line react-hooks/rules-of-hooks
320
352
  const match = useStore(matchStore, (value) => value)
321
353
  const routeId = match.routeId as string
@@ -384,14 +416,26 @@ export const MatchInner = React.memo(function MatchInnerImpl({
384
416
  }
385
417
 
386
418
  if (match.status === 'notFound') {
387
- invariant(isNotFound(match.error), 'Expected a notFound error')
419
+ if (!isNotFound(match.error)) {
420
+ if (process.env.NODE_ENV !== 'production') {
421
+ throw new Error('Invariant failed: Expected a notFound error')
422
+ }
423
+
424
+ invariant()
425
+ }
388
426
  return renderRouteNotFound(router, route, match.error)
389
427
  }
390
428
 
391
429
  if (match.status === 'redirected') {
392
430
  // Redirects should be handled by the router transition. If we happen to
393
431
  // encounter a redirect here, it's a bug. Let's warn, but render nothing.
394
- invariant(isRedirect(match.error), 'Expected a redirect error')
432
+ if (!isRedirect(match.error)) {
433
+ if (process.env.NODE_ENV !== 'production') {
434
+ throw new Error('Invariant failed: Expected a redirect error')
435
+ }
436
+
437
+ invariant()
438
+ }
395
439
 
396
440
  // warning(
397
441
  // false,
@@ -479,7 +523,15 @@ export const Outlet = React.memo(function OutletImpl() {
479
523
  ) : null
480
524
 
481
525
  if (parentGlobalNotFound) {
482
- invariant(route, 'Could not resolve route for Outlet render')
526
+ if (!route) {
527
+ if (process.env.NODE_ENV !== 'production') {
528
+ throw new Error(
529
+ 'Invariant failed: Could not resolve route for Outlet render',
530
+ )
531
+ }
532
+
533
+ invariant()
534
+ }
483
535
  return renderRouteNotFound(router, route, undefined)
484
536
  }
485
537
 
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,14 +1,23 @@
1
1
  import * as React from 'react'
2
2
  import { useStore } from '@tanstack/react-store'
3
- import { deepEqual, escapeHtml } from '@tanstack/router-core'
3
+ import {
4
+ deepEqual,
5
+ escapeHtml,
6
+ getAssetCrossOrigin,
7
+ resolveManifestAssetLink,
8
+ } from '@tanstack/router-core'
4
9
  import { isServer } from '@tanstack/router-core/isServer'
5
10
  import { useRouter } from './useRouter'
6
- import type { RouterManagedTag } from '@tanstack/router-core'
11
+ import type {
12
+ AssetCrossOriginConfig,
13
+ RouterManagedTag,
14
+ } from '@tanstack/router-core'
7
15
 
8
16
  function buildTagsFromMatches(
9
17
  router: ReturnType<typeof useRouter>,
10
18
  nonce: string | undefined,
11
19
  matches: Array<any>,
20
+ assetCrossOrigin?: AssetCrossOriginConfig,
12
21
  ): Array<RouterManagedTag> {
13
22
  const routeMeta = matches.map((match) => match.meta!).filter(Boolean)
14
23
 
@@ -101,6 +110,9 @@ function buildTagsFromMatches(
101
110
  tag: 'link',
102
111
  attrs: {
103
112
  ...asset.attrs,
113
+ crossOrigin:
114
+ getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
115
+ asset.attrs?.crossOrigin,
104
116
  suppressHydrationWarning: true,
105
117
  nonce,
106
118
  },
@@ -114,11 +126,15 @@ function buildTagsFromMatches(
114
126
  router.ssr?.manifest?.routes[route.id]?.preloads
115
127
  ?.filter(Boolean)
116
128
  .forEach((preload) => {
129
+ const preloadLink = resolveManifestAssetLink(preload)
117
130
  preloadLinks.push({
118
131
  tag: 'link',
119
132
  attrs: {
120
133
  rel: 'modulepreload',
121
- href: preload,
134
+ href: preloadLink.href,
135
+ crossOrigin:
136
+ getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??
137
+ preloadLink.crossOrigin,
122
138
  nonce,
123
139
  },
124
140
  })
@@ -170,7 +186,7 @@ function buildTagsFromMatches(
170
186
  * Build the list of head/link/meta/script tags to render for active matches.
171
187
  * Used internally by `HeadContent`.
172
188
  */
173
- export const useTags = () => {
189
+ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
174
190
  const router = useRouter()
175
191
  const nonce = router.options.ssr?.nonce
176
192
 
@@ -179,6 +195,7 @@ export const useTags = () => {
179
195
  router,
180
196
  nonce,
181
197
  router.stores.activeMatchesSnapshot.state,
198
+ assetCrossOrigin,
182
199
  )
183
200
  }
184
201
 
@@ -294,6 +311,9 @@ export const useTags = () => {
294
311
  tag: 'link',
295
312
  attrs: {
296
313
  ...asset.attrs,
314
+ crossOrigin:
315
+ getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
316
+ asset.attrs?.crossOrigin,
297
317
  suppressHydrationWarning: true,
298
318
  nonce,
299
319
  },
@@ -317,11 +337,15 @@ export const useTags = () => {
317
337
  router.ssr?.manifest?.routes[route.id]?.preloads
318
338
  ?.filter(Boolean)
319
339
  .forEach((preload) => {
340
+ const preloadLink = resolveManifestAssetLink(preload)
320
341
  preloadLinks.push({
321
342
  tag: 'link',
322
343
  attrs: {
323
344
  rel: 'modulepreload',
324
- href: preload,
345
+ href: preloadLink.href,
346
+ crossOrigin:
347
+ getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??
348
+ preloadLink.crossOrigin,
325
349
  nonce,
326
350
  },
327
351
  })
@@ -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
  }