@tanstack/react-router 1.160.0 → 1.161.1

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.
@@ -144,32 +144,22 @@ Enough overview, there's so much more to do with TanStack Router. Hit that next
144
144
 
145
145
  The fastest way to get started with TanStack Router is to scaffold a new project. Just run:
146
146
 
147
- [//]: # 'createAppCommand'
147
+ <!-- ::start:tabs variant="package-managers" mode="local-install" -->
148
148
 
149
- \`\`\`sh
150
- npx create-tsrouter-app@latest
151
- \`\`\`
149
+ react: create-tsrouter-app@latest
150
+ solid: create-tsrouter-app@latest --framework solid
152
151
 
153
- [//]: # 'createAppCommand'
152
+ <!-- ::end:tabs -->
154
153
 
155
154
  The CLI will guide you through a short series of prompts to customize your setup, including options for:
156
155
 
157
- [//]: # 'CLIPrompts'
158
-
159
156
  - File-based or code-based route configuration
160
157
  - TypeScript support
161
158
  - Tailwind CSS integration
162
159
  - Toolchain setup
163
160
  - Git initialization
164
161
 
165
- [//]: # 'CLIPrompts'
166
-
167
- Once complete, a new React project will be generated with TanStack Router installed and ready to use:
168
-
169
- \`\`\`sh
170
- cd your-project-name
171
- npm run dev
172
- \`\`\`
162
+ Once complete, a new project will be generated with TanStack Router installed and ready to use.
173
163
 
174
164
  > [!TIP]
175
165
  > For full details on available options and templates, visit the [\`create-tsrouter-app\` documentation](https://github.com/TanStack/create-tsrouter-app/tree/main/cli/create-tsrouter-app).
@@ -182,32 +172,25 @@ TanStack Router supports both file-based and code-based route configurations. Yo
182
172
 
183
173
  The file-based approach is the recommended option for most projects. It automatically creates routes based on your file structure, giving you the best mix of performance, simplicity, and developer experience.
184
174
 
185
- [//]: # 'createAppCommandFileBased'
175
+ <!-- ::start:tabs variant="package-manager" mode="local-install" -->
186
176
 
187
- \`\`\`sh
188
- npx create-tsrouter-app@latest my-app --template file-router
189
- \`\`\`
177
+ react: create-tsrouter-app@latest my-app --template file-router
178
+ solid: create-tsrouter-app@latest my-app --framework solid --template file-router
190
179
 
191
- [//]: # 'createAppCommandFileBased'
180
+ <!-- ::end:tabs -->
192
181
 
193
182
  ### Code-Based Route Configuration
194
183
 
195
184
  If you prefer to define routes programmatically, you can use the code-based route configuration. This approach gives you full control over routing logic.
196
185
 
197
- [//]: # 'createAppCommandCodeBased'
186
+ <!-- ::start:tabs variant="package-manager" mode="local-install" -->
198
187
 
199
- \`\`\`sh
200
- npx create-tsrouter-app@latest my-app
201
- \`\`\`
202
-
203
- [//]: # 'createAppCommandCodeBased'
188
+ react: create-tsrouter-app@latest my-app
189
+ solid: create-tsrouter-app@latest my-app --framework solid
204
190
 
205
- With either approach, navigate to your project directory and start the development server:
191
+ <!-- ::end:tabs -->
206
192
 
207
- \`\`\`sh
208
- cd my-app
209
- npm run dev
210
- \`\`\`
193
+ With either approach, navigate to your project directory and start the development server.
211
194
 
212
195
  ## Existing Project
213
196
 
@@ -217,12 +200,18 @@ If you have an existing React project and want to add TanStack Router to it, you
217
200
 
218
201
  Before installing TanStack Router, please ensure your project meets the following requirements:
219
202
 
220
- [//]: # 'Requirements'
203
+ <!-- ::start:framework -->
204
+
205
+ # React
221
206
 
222
207
  - \`react\` v18 or later with \`createRoot\` support.
223
208
  - \`react-dom\` v18 or later.
224
209
 
225
- [//]: # 'Requirements'
210
+ # Solid
211
+
212
+ - \`solid-js\` v1.x.x
213
+
214
+ <!-- ::end:framework -->
226
215
 
227
216
  > [!NOTE]
228
217
  > Using TypeScript (\`v5.3.x or higher\`) is recommended for the best development experience, though not strictly required. We aim to support the last 5 minor versions of TypeScript, but using the latest version will help avoid potential issues.
@@ -233,25 +222,18 @@ TanStack Router is currently only compatible with React (with ReactDOM) and Soli
233
222
 
234
223
  To install TanStack Router in your project, run the following command using your preferred package manager:
235
224
 
236
- [//]: # 'installCommand'
237
-
238
- \`\`\`sh
239
- npm install @tanstack/react-router
240
- # or
241
- pnpm add @tanstack/react-router
242
- #or
243
- yarn add @tanstack/react-router
244
- # or
245
- bun add @tanstack/react-router
246
- # or
247
- deno add npm:@tanstack/react-router
248
- \`\`\`
225
+ <!-- ::start:tabs variant="package-managers" -->
226
+
227
+ react: @tanstack/react-router
228
+ solid: @tanstack/solid-router
249
229
 
250
- [//]: # 'installCommand'
230
+ <!-- ::end:tabs -->
251
231
 
252
232
  Once installed, you can verify the installation by checking your \`package.json\` file for the dependency.
253
233
 
254
- [//]: # 'packageJson'
234
+ <!-- ::start:framework -->
235
+
236
+ # React
255
237
 
256
238
  \`\`\`json
257
239
  {
@@ -261,7 +243,17 @@ Once installed, you can verify the installation by checking your \`package.json\
261
243
  }
262
244
  \`\`\`
263
245
 
264
- [//]: # 'packageJson'
246
+ # Solid
247
+
248
+ \`\`\`json
249
+ {
250
+ "dependencies": {
251
+ "@tanstack/solid-router": "^x.x.x"
252
+ }
253
+ }
254
+ \`\`\`
255
+
256
+ <!-- ::end:framework -->
265
257
 
266
258
  # Decisions on Developer Experience
267
259
 
@@ -277,9 +269,12 @@ And they are all valid questions. For the most part, people are used to using ro
277
269
 
278
270
  But TanStack Router is different. It's not your average routing library. It's not your average state management library. It's not your average anything.
279
271
 
272
+ > [!TIP]
273
+ > The examples in this guide use React for components and code snippets, but the same principles apply to Solid. The only difference is in the syntax and API of the framework, but the underlying concepts and design decisions are the same.
274
+
280
275
  ## TanStack Router's origin story
281
276
 
282
- It's important to remember that TanStack Router's origins stem from [Nozzle.io](https://nozzle.io)'s need for a client-side routing solution that offered a first-in-class _URL Search Parameters_ experience without compromising on the **_type-safety_** that was required to power its complex dashboards.
277
+ It's important to remember that TanStack Router's origins stem from [Nozzle.io](https://nozzle.io?utm_source=tanstack)'s need for a client-side routing solution that offered a first-in-class _URL Search Parameters_ experience without compromising on the **_type-safety_** that was required to power its complex dashboards.
283
278
 
284
279
  And so, from TanStack Router's very inception, every facet of its design was meticulously thought out to ensure that its type-safety and developer experience were second to none.
285
280
 
@@ -511,48 +506,66 @@ When you begin your TanStack Router journey, you'll want these devtools by your
511
506
 
512
507
  The devtools are a separate package that you need to install:
513
508
 
514
- \`\`\`sh
515
- npm install @tanstack/react-router-devtools
516
- \`\`\`
509
+ <!-- ::start:tabs variant="package-manager" -->
517
510
 
518
- or
511
+ react: @tanstack/react-router-devtools
512
+ solid: @tanstack/solid-router-devtools
519
513
 
520
- \`\`\`sh
521
- pnpm add @tanstack/react-router-devtools
522
- \`\`\`
514
+ <!-- ::end:tabs -->
523
515
 
524
- or
516
+ ## Import the Devtools
525
517
 
526
- \`\`\`sh
527
- yarn add @tanstack/react-router-devtools
528
- \`\`\`
518
+ <!-- ::start:framework -->
529
519
 
530
- or
520
+ # React
531
521
 
532
- \`\`\`sh
533
- bun add @tanstack/react-router-devtools
522
+ \`\`\`tsx
523
+ import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
534
524
  \`\`\`
535
525
 
536
- ## Import the Devtools
526
+ # Solid
537
527
 
538
- \`\`\`js
539
- import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
528
+ \`\`\`tsx
529
+ import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
540
530
  \`\`\`
541
531
 
532
+ <!-- ::end:framework -->
533
+
542
534
  ## Using Devtools in production
543
535
 
544
536
  The Devtools, if imported as \`TanStackRouterDevtools\` will not be shown in production. If you want to have devtools in an environment with \`process.env.NODE_ENV === 'production'\`, use instead \`TanStackRouterDevtoolsInProd\`, which has all the same options:
545
537
 
538
+ <!-- ::start:framework -->
539
+
540
+ # React
541
+
546
542
  \`\`\`tsx
547
543
  import { TanStackRouterDevtoolsInProd } from '@tanstack/react-router-devtools'
548
544
  \`\`\`
549
545
 
550
- ## Using inside of the \`RouterProvider\`
546
+ # Solid
547
+
548
+ \`\`\`tsx
549
+ import { TanStackRouterDevtoolsInProd } from '@tanstack/solid-router-devtools'
550
+ \`\`\`
551
+
552
+ <!-- ::end:framework -->
553
+
554
+ ## Using the Devtools in the root route
551
555
 
552
556
  The easiest way for the devtools to work is to render them inside of your root route (or any other route). This will automatically connect the devtools to the router instance.
553
557
 
554
- \`\`\`tsx
555
- const rootRoute = createRootRoute({
558
+ <!-- ::start:framework -->
559
+
560
+ # React
561
+
562
+ <!-- ::start:tabs variant="files" -->
563
+
564
+ \`\`\`tsx title="src/routes/__root.tsx"
565
+ import { createRootRoute, Outlet } from '@tanstack/react-router'
566
+ import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
567
+
568
+ export const Route = createRootRoute({
556
569
  component: () => (
557
570
  <>
558
571
  <Outlet />
@@ -560,24 +573,53 @@ const rootRoute = createRootRoute({
560
573
  </>
561
574
  ),
562
575
  })
576
+ \`\`\`
563
577
 
564
- const routeTree = rootRoute.addChildren([
565
- // ... other routes
566
- ])
578
+ <!-- ::end:tabs -->
567
579
 
568
- const router = createRouter({
569
- routeTree,
570
- })
580
+ # Solid
571
581
 
572
- function App() {
573
- return <RouterProvider router={router} />
574
- }
582
+ <!-- ::start:tabs variant="files" -->
583
+
584
+ \`\`\`tsx title="src/routes/__root.tsx"
585
+ import { createRootRoute, Outlet } from '@tanstack/solid-router'
586
+ import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
587
+
588
+ export const Route = createRootRoute({
589
+ component: () => (
590
+ <>
591
+ <Outlet />
592
+ <TanStackRouterDevtools />
593
+ </>
594
+ ),
595
+ })
575
596
  \`\`\`
576
597
 
598
+ <!-- ::end:tabs -->
599
+
600
+ <!-- ::end:framework -->
601
+
577
602
  ## Manually passing the Router Instance
578
603
 
579
604
  If rendering the devtools inside of the \`RouterProvider\` isn't your cup of tea, a \`router\` prop for the devtools accepts the same \`router\` instance you pass to the \`Router\` component. This makes it possible to place the devtools anywhere on the page, not just inside the provider:
580
605
 
606
+ <!-- ::start:framework -->
607
+
608
+ # React
609
+
610
+ \`\`\`tsx
611
+ function App() {
612
+ return (
613
+ <>
614
+ <RouterProvider router={router} />
615
+ <TanStackRouterDevtools router={router} />
616
+ </>
617
+ )
618
+ }
619
+ \`\`\`
620
+
621
+ # Solid
622
+
581
623
  \`\`\`tsx
582
624
  function App() {
583
625
  return (
@@ -589,25 +631,44 @@ function App() {
589
631
  }
590
632
  \`\`\`
591
633
 
634
+ <!-- ::end:framework -->
635
+
592
636
  ## Floating Mode
593
637
 
594
638
  Floating Mode will mount the devtools as a fixed, floating element in your app and provide a toggle in the corner of the screen to show and hide the devtools. This toggle state will be stored and remembered in localStorage across reloads.
595
639
 
596
- Place the following code as high in your React app as you can. The closer it is to the root of the page, the better it will work!
640
+ Place the following code as high in your app as you can. The closer it is to the root of the page, the better it will work!
597
641
 
598
- \`\`\`js
599
- import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
642
+ <!-- ::start:framework -->
643
+
644
+ # React
645
+
646
+ \`\`\`tsx
647
+ function App() {
648
+ return (
649
+ <>
650
+ <RouterProvider router={router} />
651
+ <TanStackRouterDevtools initialIsOpen={false} />
652
+ </>
653
+ )
654
+ }
655
+ \`\`\`
656
+
657
+ # Solid
600
658
 
659
+ \`\`\`tsx
601
660
  function App() {
602
661
  return (
603
662
  <>
604
- <Router />
663
+ <RouterProvider router={router} />
605
664
  <TanStackRouterDevtools initialIsOpen={false} />
606
665
  </>
607
666
  )
608
667
  }
609
668
  \`\`\`
610
669
 
670
+ <!-- ::end:framework -->
671
+
611
672
  ### Devtools Options
612
673
 
613
674
  - \`router: Router\`
@@ -635,32 +696,63 @@ function App() {
635
696
 
636
697
  To control the position of the devtools, import the \`TanStackRouterDevtoolsPanel\`:
637
698
 
638
- \`\`\`js
699
+ <!-- ::start:framework -->
700
+
701
+ # React
702
+
703
+ \`\`\`tsx
639
704
  import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
640
705
  \`\`\`
641
706
 
707
+ # Solid
708
+
709
+ \`\`\`tsx
710
+ import { TanStackRouterDevtoolsPanel } from '@tanstack/solid-router-devtools'
711
+ \`\`\`
712
+
713
+ <!-- ::end:framework -->
714
+
642
715
  It can then be attached to provided shadow DOM target:
643
716
 
644
- \`\`\`js
717
+ <!-- ::start:framework -->
718
+
719
+ # React
720
+
721
+ \`\`\`tsx
645
722
  <TanStackRouterDevtoolsPanel
646
723
  shadowDOMTarget={shadowContainer}
647
724
  router={router}
648
725
  />
649
726
  \`\`\`
650
727
 
728
+ # Solid
729
+
730
+ \`\`\`tsx
731
+ <TanStackRouterDevtoolsPanel
732
+ shadowDOMTarget={shadowContainer}
733
+ router={router}
734
+ />
735
+ \`\`\`
736
+
737
+ <!-- ::end:framework -->
738
+
651
739
  Click [here](https://tanstack.com/router/latest/docs/framework/react/examples/basic-devtools-panel) to see a live example of this in StackBlitz.
652
740
 
653
741
  ## Embedded Mode
654
742
 
655
743
  Embedded Mode will embed the devtools as a regular component in your application. You can style it however you'd like after that!
656
744
 
657
- \`\`\`js
745
+ <!-- ::start:framework -->
746
+
747
+ # React
748
+
749
+ \`\`\`tsx
658
750
  import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
659
751
 
660
752
  function App() {
661
753
  return (
662
754
  <>
663
- <Router router={router} />
755
+ <RouterProvider router={router} />
664
756
  <TanStackRouterDevtoolsPanel
665
757
  router={router}
666
758
  style={styles}
@@ -671,14 +763,50 @@ function App() {
671
763
  }
672
764
  \`\`\`
673
765
 
766
+ # Solid
767
+
768
+ \`\`\`tsx
769
+ import { TanStackRouterDevtoolsPanel } from '@tanstack/solid-router-devtools'
770
+
771
+ function App() {
772
+ return (
773
+ <>
774
+ <RouterProvider router={router} />
775
+ <TanStackRouterDevtoolsPanel
776
+ router={router}
777
+ style={styles}
778
+ class={className}
779
+ />
780
+ </>
781
+ )
782
+ }
783
+ \`\`\`
784
+
785
+ <!-- ::end:framework -->
786
+
674
787
  ### DevtoolsPanel Options
675
788
 
676
789
  - \`router: Router\`
677
790
  - The router instance to connect to.
678
- - \`style: StyleObject\`
791
+
792
+ <!-- ::start:framework -->
793
+
794
+ # React
795
+
796
+ - \`style?: StyleObject\`
679
797
  - The standard React style object used to style a component with inline styles.
680
- - \`className: string\`
798
+ - \`className?: string\`
681
799
  - The standard React className property used to style a component with classes.
800
+
801
+ # Solid
802
+
803
+ - \`style?: StyleObject\`
804
+ - The standard Solid style object used to style a component with inline styles.
805
+ - \`class?: string\`
806
+ - The standard Solid class property used to style a component with classes.
807
+
808
+ <!-- ::end:framework -->
809
+
682
810
  - \`isOpen?: boolean\`
683
811
  - A boolean variable indicating whether the panel is open or closed.
684
812
  - \`setIsOpen?: (isOpen: boolean) => void\`
@@ -716,7 +844,6 @@ You should commit this file into git so that other developers can use it to buil
716
844
  ## Can I conditionally render the Root Route component?
717
845
 
718
846
  No, the root route is always rendered as it is the entry point of your application.
719
-
720
847
  If you need to conditionally render a route's component, this usually means that the page content needs to be different based on some condition (e.g. user authentication). For this use case, you should use a [Layout Route](./routing/routing-concepts.md#layout-routes) or a [Pathless Layout Route](./routing/routing-concepts.md#pathless-layout-routes) to conditionally render the content.
721
848
 
722
849
  You can restrict access to these routes using a conditional check in the \`beforeLoad\` function of the route.
@@ -724,6 +851,10 @@ You can restrict access to these routes using a conditional check in the \`befor
724
851
  <details>
725
852
  <summary>What does this look like?</summary>
726
853
 
854
+ <!-- ::start:framework -->
855
+
856
+ # React
857
+
727
858
  \`\`\`tsx
728
859
  // src/routes/_pathless-layout.tsx
729
860
  import { createFileRoute, Outlet } from '@tanstack/react-router'
@@ -752,6 +883,38 @@ function PathlessLayoutRouteComponent() {
752
883
  }
753
884
  \`\`\`
754
885
 
886
+ # Solid
887
+
888
+ \`\`\`tsx
889
+ // src/routes/_pathless-layout.tsx
890
+ import { createFileRoute, Outlet } from '@tanstack/solid-router'
891
+ import { isAuthenticated } from '../utils/auth'
892
+
893
+ export const Route = createFileRoute('/_pathless-layout', {
894
+ beforeLoad: async () => {
895
+ // Check if the user is authenticated
896
+ const authed = await isAuthenticated()
897
+ if (!authed) {
898
+ // Redirect the user to the login page
899
+ return '/login'
900
+ }
901
+ },
902
+ component: PathlessLayoutRouteComponent,
903
+ // ...
904
+ })
905
+
906
+ function PathlessLayoutRouteComponent() {
907
+ return (
908
+ <div>
909
+ <h1>You are authed</h1>
910
+ <Outlet />
911
+ </div>
912
+ )
913
+ }
914
+ \`\`\`
915
+
916
+ <!-- ::end:framework -->
917
+
755
918
  </details>
756
919
 
757
920
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.160.0",
3
+ "version": "1.161.1",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -82,7 +82,7 @@
82
82
  "tiny-invariant": "^1.3.3",
83
83
  "tiny-warning": "^1.0.3",
84
84
  "@tanstack/history": "1.154.14",
85
- "@tanstack/router-core": "1.160.0"
85
+ "@tanstack/router-core": "1.161.1"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@testing-library/jest-dom": "^6.6.3",
package/src/Asset.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react'
2
2
  import { isServer } from '@tanstack/router-core/isServer'
3
3
  import { useRouter } from './useRouter'
4
+ import { useHydrated } from './ClientOnly'
4
5
  import type { RouterManagedTag } from '@tanstack/router-core'
5
6
 
6
7
  interface ScriptAttrs {
@@ -49,12 +50,24 @@ function Script({
49
50
  children?: string
50
51
  }) {
51
52
  const router = useRouter()
53
+ const hydrated = useHydrated()
52
54
  const dataScript =
53
55
  typeof attrs?.type === 'string' &&
54
56
  attrs.type !== '' &&
55
57
  attrs.type !== 'text/javascript' &&
56
58
  attrs.type !== 'module'
57
59
 
60
+ if (
61
+ process.env.NODE_ENV !== 'production' &&
62
+ attrs?.src &&
63
+ typeof children === 'string' &&
64
+ children.trim().length
65
+ ) {
66
+ console.warn(
67
+ '[TanStack Router] <Script> received both `src` and `children`. The `children` content will be ignored. Remove `children` or remove `src`.',
68
+ )
69
+ }
70
+
58
71
  React.useEffect(() => {
59
72
  if (dataScript) return
60
73
 
@@ -151,41 +164,56 @@ function Script({
151
164
  return undefined
152
165
  }, [attrs, children, dataScript])
153
166
 
154
- if (!(isServer ?? router.isServer)) {
155
- if (dataScript && typeof children === 'string') {
167
+ // --- Server rendering ---
168
+ if (isServer ?? router.isServer) {
169
+ if (attrs?.src) {
170
+ return <script {...attrs} suppressHydrationWarning />
171
+ }
172
+
173
+ if (typeof children === 'string') {
156
174
  return (
157
175
  <script
158
176
  {...attrs}
159
- suppressHydrationWarning
160
177
  dangerouslySetInnerHTML={{ __html: children }}
178
+ suppressHydrationWarning
161
179
  />
162
180
  )
163
181
  }
164
182
 
165
- const { src: _src, async: _async, defer: _defer, ...rest } = attrs || {}
166
- // render an empty script on the client just to avoid hydration errors
167
- return (
168
- <script
169
- suppressHydrationWarning
170
- dangerouslySetInnerHTML={{ __html: '' }}
171
- {...rest}
172
- ></script>
173
- )
183
+ return null
174
184
  }
175
185
 
176
- if (attrs?.src && typeof attrs.src === 'string') {
177
- return <script {...attrs} suppressHydrationWarning />
178
- }
186
+ // --- Client rendering ---
179
187
 
180
- if (typeof children === 'string') {
188
+ // Data scripts (e.g. application/ld+json) are rendered in the tree;
189
+ // the useEffect intentionally skips them.
190
+ if (dataScript && typeof children === 'string') {
181
191
  return (
182
192
  <script
183
193
  {...attrs}
184
- dangerouslySetInnerHTML={{ __html: children }}
185
194
  suppressHydrationWarning
195
+ dangerouslySetInnerHTML={{ __html: children }}
186
196
  />
187
197
  )
188
198
  }
189
199
 
200
+ // During hydration (before useEffect has fired), render the script element
201
+ // to match the server-rendered HTML and avoid structural hydration mismatches.
202
+ // After hydration, return null — the useEffect handles imperative injection.
203
+ if (!hydrated) {
204
+ if (attrs?.src) {
205
+ return <script {...attrs} suppressHydrationWarning />
206
+ }
207
+ if (typeof children === 'string') {
208
+ return (
209
+ <script
210
+ {...attrs}
211
+ dangerouslySetInnerHTML={{ __html: children }}
212
+ suppressHydrationWarning
213
+ />
214
+ )
215
+ }
216
+ }
217
+
190
218
  return null
191
219
  }