@tanstack/react-router 1.160.0 → 1.160.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.
@@ -52,6 +52,10 @@ export const Route = createFileRoute('/_authenticated')({
52
52
 
53
53
  If your authentication check can throw errors (network failures, token validation, etc.), wrap it in try/catch:
54
54
 
55
+ <!-- ::start:framework -->
56
+
57
+ # React
58
+
55
59
  \`\`\`tsx
56
60
  import { createFileRoute, redirect, isRedirect } from '@tanstack/react-router'
57
61
 
@@ -81,6 +85,39 @@ export const Route = createFileRoute('/_authenticated')({
81
85
  })
82
86
  \`\`\`
83
87
 
88
+ # Solid
89
+
90
+ \`\`\`tsx
91
+ import { createFileRoute, redirect, isRedirect } from '@tanstack/solid-router'
92
+
93
+ // src/routes/_authenticated.tsx
94
+ export const Route = createFileRoute('/_authenticated')({
95
+ beforeLoad: async ({ location }) => {
96
+ try {
97
+ const user = await verifySession() // might throw on network error
98
+ if (!user) {
99
+ throw redirect({
100
+ to: '/login',
101
+ search: { redirect: location.href },
102
+ })
103
+ }
104
+ return { user }
105
+ } catch (error) {
106
+ // Re-throw redirects (they're intentional, not errors)
107
+ if (isRedirect(error)) throw error
108
+
109
+ // Auth check failed (network error, etc.) - redirect to login
110
+ throw redirect({
111
+ to: '/login',
112
+ search: { redirect: location.href },
113
+ })
114
+ }
115
+ },
116
+ })
117
+ \`\`\`
118
+
119
+ <!-- ::end:framework -->
120
+
84
121
  The [\`isRedirect()\`](../api/router/isRedirectFunction.md) helper distinguishes between actual errors and intentional redirects.
85
122
 
86
123
  Once you have authenticated a user, it's also common practice to redirect them back to the page they were trying to access. To do this, you can utilize the \`redirect\` search param that we added in our original redirect. Since we'll be replacing the entire URL with what it was, \`router.history.push\` is better suited for this than \`router.navigate\`:
@@ -119,9 +156,13 @@ We'll cover the \`router.context\` options in-detail in the [Router Context](./r
119
156
 
120
157
  Here's an example that uses React context and hooks for protecting authenticated routes in TanStack Router. See the entire working setup in the [Authenticated Routes example](https://github.com/TanStack/router/tree/main/examples/react/authenticated-routes).
121
158
 
122
- - \`src/routes/__root.tsx\`
159
+ <!-- ::start:framework -->
123
160
 
124
- \`\`\`tsx
161
+ # React
162
+
163
+ <!-- ::start:tabs variant="files" -->
164
+
165
+ \`\`\`tsx title="src/routes/__root.tsx"
125
166
  import { createRootRouteWithContext } from '@tanstack/react-router'
126
167
 
127
168
  interface MyRouterContext {
@@ -134,9 +175,7 @@ export const Route = createRootRouteWithContext<MyRouterContext>()({
134
175
  })
135
176
  \`\`\`
136
177
 
137
- - \`src/router.tsx\`
138
-
139
- \`\`\`tsx
178
+ \`\`\`tsx title="src/router.tsx"
140
179
  import { createRouter } from '@tanstack/react-router'
141
180
 
142
181
  import { routeTree } from './routeTree.gen'
@@ -151,9 +190,7 @@ export const router = createRouter({
151
190
  })
152
191
  \`\`\`
153
192
 
154
- - \`src/App.tsx\`
155
-
156
- \`\`\`tsx
193
+ \`\`\`tsx title="src/App.tsx"
157
194
  import { RouterProvider } from '@tanstack/react-router'
158
195
 
159
196
  import { AuthProvider, useAuth } from './auth'
@@ -174,11 +211,72 @@ function App() {
174
211
  }
175
212
  \`\`\`
176
213
 
214
+ <!-- ::end:tabs -->
215
+
216
+ # Solid
217
+
218
+ <!-- ::start:tabs variant="files" -->
219
+
220
+ \`\`\`tsx title="src/routes/__root.tsx"
221
+ import { createRootRouteWithContext } from '@tanstack/solid-router'
222
+
223
+ interface MyRouterContext {
224
+ // The ReturnType of your useAuth hook or the value of your AuthContext
225
+ auth: AuthState
226
+ }
227
+
228
+ export const Route = createRootRouteWithContext<MyRouterContext>()({
229
+ component: () => <Outlet />,
230
+ })
231
+ \`\`\`
232
+
233
+ \`\`\`tsx title="src/router.tsx"
234
+ import { createRouter } from '@tanstack/solid-router'
235
+
236
+ import { routeTree } from './routeTree.gen'
237
+
238
+ export const router = createRouter({
239
+ routeTree,
240
+ context: {
241
+ // auth will initially be undefined
242
+ // We'll be passing down the auth state from within a React component
243
+ auth: undefined!,
244
+ },
245
+ })
246
+ \`\`\`
247
+
248
+ \`\`\`tsx title="src/App.tsx"
249
+ import { RouterProvider } from '@tanstack/solid-router'
250
+
251
+ import { AuthProvider, useAuth } from './auth'
252
+
253
+ import { router } from './router'
254
+
255
+ function InnerApp() {
256
+ const auth = useAuth()
257
+ return <RouterProvider router={router} context={{ auth }} />
258
+ }
259
+
260
+ function App() {
261
+ return (
262
+ <AuthProvider>
263
+ <InnerApp />
264
+ </AuthProvider>
265
+ )
266
+ }
267
+ \`\`\`
268
+
269
+ <!-- ::end:tabs -->
270
+
271
+ <!-- ::end:framework -->
272
+
177
273
  Then in the authenticated route, you can check the auth state using the \`beforeLoad\` function, and **throw a \`redirect()\`** to your **Login route** if the user is not signed-in.
178
274
 
179
- - \`src/routes/dashboard.route.tsx\`
275
+ <!-- ::start:framework -->
180
276
 
181
- \`\`\`tsx
277
+ # React
278
+
279
+ \`\`\`tsx title="src/routes/dashboard.route.tsx"
182
280
  import { createFileRoute, redirect } from '@tanstack/react-router'
183
281
 
184
282
  export const Route = createFileRoute('/dashboard')({
@@ -195,6 +293,27 @@ export const Route = createFileRoute('/dashboard')({
195
293
  })
196
294
  \`\`\`
197
295
 
296
+ # Solid
297
+
298
+ \`\`\`tsx title="src/routes/dashboard.route.tsx"
299
+ import { createFileRoute, redirect } from '@tanstack/solid-router'
300
+
301
+ export const Route = createFileRoute('/dashboard')({
302
+ beforeLoad: ({ context, location }) => {
303
+ if (!context.auth.isAuthenticated()) {
304
+ throw redirect({
305
+ to: '/login',
306
+ search: {
307
+ redirect: location.href,
308
+ },
309
+ })
310
+ }
311
+ },
312
+ })
313
+ \`\`\`
314
+
315
+ <!-- ::end:framework -->
316
+
198
317
  You can _optionally_, also use the [Non-Redirected Authentication](#non-redirected-authentication) approach to show a login form instead of calling a **redirect**.
199
318
 
200
319
  This approach can also be used in conjunction with Pathless or Layout Route to protect all routes under their parent route.
@@ -298,8 +417,6 @@ For automatic code splitting to work, there are some rules in-place to make sure
298
417
  Route properties like \`component\`, \`loader\`, etc., should not be exported from the route file. Exporting these properties results in them being bundled into the main application bundle, which means that they will not be code-split.
299
418
 
300
419
  \`\`\`tsx
301
- import { createRoute } from '@tanstack/react-router'
302
-
303
420
  export const Route = createRoute('/posts')({
304
421
  // ...
305
422
  notFoundComponent: PostsNotFoundComponent,
@@ -331,8 +448,7 @@ You can change how TanStack Router splits your routes by changing the \`defaultB
331
448
 
332
449
  For example, to bundle all UI-related components into a single chunk, you could configure it like this:
333
450
 
334
- \`\`\`ts
335
- // vite.config.ts
451
+ \`\`\`ts title="vite.config.ts"
336
452
  import { defineConfig } from 'vite'
337
453
  import { tanstackRouter } from '@tanstack/router-plugin/vite'
338
454
 
@@ -359,8 +475,7 @@ export default defineConfig({
359
475
 
360
476
  For complex rulesets, you can use the \`splitBehavior\` function in your vite config to programmatically define how routes should be split into chunks based on their \`routeId\`. This function allows you to implement custom logic for grouping properties together, giving you fine-grained control over the code splitting behavior.
361
477
 
362
- \`\`\`ts
363
- // vite.config.ts
478
+ \`\`\`ts title="vite.config.ts"
364
479
  import { defineConfig } from 'vite'
365
480
  import { tanstackRouter } from '@tanstack/router-plugin/vite'
366
481
 
@@ -386,9 +501,7 @@ export default defineConfig({
386
501
 
387
502
  For ultimate control, you can override the global configuration directly inside a route file by adding a \`codeSplitGroupings\` property. This is useful for routes that have unique optimization needs.
388
503
 
389
- \`\`\`tsx
390
- // src/routes/posts.route.tsx
391
- import { createFileRoute } from '@tanstack/react-router'
504
+ \`\`\`tsx title="src/routes/posts.route.tsx"
392
505
  import { loadPostsData } from './-heavy-posts-utils'
393
506
 
394
507
  export const Route = createFileRoute('/posts')({
@@ -565,8 +678,11 @@ When you are using \`.lazy.tsx\` you can split your route into two files to enab
565
678
 
566
679
  **Before (Single File)**
567
680
 
568
- \`\`\`tsx
569
- // src/routes/posts.tsx
681
+ <!-- ::start:framework -->
682
+
683
+ # React
684
+
685
+ \`\`\`tsx title="src/routes/posts.tsx"
570
686
  import { createFileRoute } from '@tanstack/react-router'
571
687
  import { fetchPosts } from './api'
572
688
 
@@ -580,13 +696,33 @@ function Posts() {
580
696
  }
581
697
  \`\`\`
582
698
 
699
+ # Solid
700
+
701
+ \`\`\`tsx title="src/routes/posts.tsx"
702
+ import { createFileRoute } from '@tanstack/solid-router'
703
+ import { fetchPosts } from './api'
704
+
705
+ export const Route = createFileRoute('/posts')({
706
+ loader: fetchPosts,
707
+ component: Posts,
708
+ })
709
+
710
+ function Posts() {
711
+ // ...
712
+ }
713
+ \`\`\`
714
+
715
+ <!-- ::end:framework -->
716
+
583
717
  **After (Split into two files)**
584
718
 
585
719
  This file would contain the critical route configuration:
586
720
 
587
- \`\`\`tsx
588
- // src/routes/posts.tsx
721
+ <!-- ::start:framework -->
722
+
723
+ # React
589
724
 
725
+ \`\`\`tsx title="src/routes/posts.tsx"
590
726
  import { createFileRoute } from '@tanstack/react-router'
591
727
  import { fetchPosts } from './api'
592
728
 
@@ -595,10 +731,26 @@ export const Route = createFileRoute('/posts')({
595
731
  })
596
732
  \`\`\`
597
733
 
734
+ # Solid
735
+
736
+ \`\`\`tsx title="src/routes/posts.tsx"
737
+ import { createFileRoute } from '@tanstack/solid-router'
738
+ import { fetchPosts } from './api'
739
+
740
+ export const Route = createFileRoute('/posts')({
741
+ loader: fetchPosts,
742
+ })
743
+ \`\`\`
744
+
745
+ <!-- ::end:framework -->
746
+
598
747
  With the non-critical route configuration going into the file with the \`.lazy.tsx\` suffix:
599
748
 
600
- \`\`\`tsx
601
- // src/routes/posts.lazy.tsx
749
+ <!-- ::start:framework -->
750
+
751
+ # React
752
+
753
+ \`\`\`tsx title="src/routes/posts.lazy.tsx"
602
754
  import { createLazyFileRoute } from '@tanstack/react-router'
603
755
 
604
756
  export const Route = createLazyFileRoute('/posts')({
@@ -610,14 +762,35 @@ function Posts() {
610
762
  }
611
763
  \`\`\`
612
764
 
765
+ # Solid
766
+
767
+ \`\`\`tsx title="src/routes/posts.lazy.tsx"
768
+ import { createLazyFileRoute } from '@tanstack/solid-router'
769
+
770
+ export const Route = createLazyFileRoute('/posts')({
771
+ component: Posts,
772
+ })
773
+
774
+ function Posts() {
775
+ // ...
776
+ }
777
+ \`\`\`
778
+
779
+ <!-- ::end:framework -->
780
+
613
781
  ## Using Virtual Routes
614
782
 
615
783
  You might run into a situation where you end up splitting out everything from a route file, leaving it empty! In this case, simply **delete the route file entirely**! A virtual route will automatically be generated for you to serve as an anchor for your code split files. This virtual route will live directly in the generated route tree file.
616
784
 
617
785
  **Before (Virtual Routes)**
618
786
 
619
- \`\`\`tsx
620
- // src/routes/posts.tsx
787
+ <!-- ::start:framework -->
788
+
789
+ # React
790
+
791
+ <!-- ::start:tabs variant="files" -->
792
+
793
+ \`\`\`tsx title="src/routes/posts.tsx"
621
794
  import { createFileRoute } from '@tanstack/react-router'
622
795
 
623
796
  export const Route = createFileRoute('/posts')({
@@ -625,8 +798,7 @@ export const Route = createFileRoute('/posts')({
625
798
  })
626
799
  \`\`\`
627
800
 
628
- \`\`\`tsx
629
- // src/routes/posts.lazy.tsx
801
+ \`\`\`tsx title="src/routes/posts.lazy.tsx"
630
802
  import { createLazyFileRoute } from '@tanstack/react-router'
631
803
 
632
804
  export const Route = createLazyFileRoute('/posts')({
@@ -638,10 +810,43 @@ function Posts() {
638
810
  }
639
811
  \`\`\`
640
812
 
813
+ <!-- ::end:tabs -->
814
+
815
+ # Solid
816
+
817
+ <!-- ::start:tabs variant="files" -->
818
+
819
+ \`\`\`tsx title="src/routes/posts.tsx"
820
+ import { createFileRoute } from '@tanstack/solid-router'
821
+
822
+ export const Route = createFileRoute('/posts')({
823
+ // Hello?
824
+ })
825
+ \`\`\`
826
+
827
+ \`\`\`tsx title="src/routes/posts.lazy.tsx"
828
+ import { createLazyFileRoute } from '@tanstack/solid-router'
829
+
830
+ export const Route = createLazyFileRoute('/posts')({
831
+ component: Posts,
832
+ })
833
+
834
+ function Posts() {
835
+ // ...
836
+ }
837
+ \`\`\`
838
+
839
+ <!-- ::end:tabs -->
840
+
841
+ <!-- ::end:framework -->
842
+
641
843
  **After (Virtual Routes)**
642
844
 
643
- \`\`\`tsx
644
- // src/routes/posts.lazy.tsx
845
+ <!-- ::start:framework -->
846
+
847
+ # React
848
+
849
+ \`\`\`tsx title="src/routes/posts.lazy.tsx"
645
850
  import { createLazyFileRoute } from '@tanstack/react-router'
646
851
 
647
852
  export const Route = createLazyFileRoute('/posts')({
@@ -653,6 +858,22 @@ function Posts() {
653
858
  }
654
859
  \`\`\`
655
860
 
861
+ # Solid
862
+
863
+ \`\`\`tsx title="src/routes/posts.lazy.tsx"
864
+ import { createLazyFileRoute } from '@tanstack/solid-router'
865
+
866
+ export const Route = createLazyFileRoute('/posts')({
867
+ component: Posts,
868
+ })
869
+
870
+ function Posts() {
871
+ // ...
872
+ }
873
+ \`\`\`
874
+
875
+ <!-- ::end:framework -->
876
+
656
877
  Tada! 🎉
657
878
 
658
879
  ## Code-Based Splitting
@@ -661,8 +882,7 @@ If you are using code-based routing, you can still code-split your routes using
661
882
 
662
883
  Create a lazy route using the \`createLazyRoute\` function.
663
884
 
664
- \`\`\`tsx
665
- // src/posts.lazy.tsx
885
+ \`\`\`tsx title="src/posts.lazy.tsx"
666
886
  export const Route = createLazyRoute('/posts')({
667
887
  component: MyComponent,
668
888
  })
@@ -674,8 +894,7 @@ function MyComponent() {
674
894
 
675
895
  Then, call the \`.lazy\` method on the route definition in your \`app.tsx\` to import the lazy/code-split route with the non-critical route configuration.
676
896
 
677
- \`\`\`tsx
678
- // src/app.tsx
897
+ \`\`\`tsx title="src/app.tsx"
679
898
  const postsRoute = createRoute({
680
899
  getParentRoute: () => rootRoute,
681
900
  path: '/posts',
@@ -690,6 +909,10 @@ It can be a powerful tool to reduce bundle size, but it comes with a cost as men
690
909
 
691
910
  You can code split your data loading logic using the Route's \`loader\` option. While this process makes it difficult to maintain type-safety with the parameters passed to your loader, you can always use the generic \`LoaderContext\` type to get you most of the way there:
692
911
 
912
+ <!-- ::start:framework -->
913
+
914
+ # React
915
+
693
916
  \`\`\`tsx
694
917
  import { lazyFn } from '@tanstack/react-router'
695
918
 
@@ -705,15 +928,38 @@ export const loader = async (context: LoaderContext) => {
705
928
  }
706
929
  \`\`\`
707
930
 
931
+ # Solid
932
+
933
+ \`\`\`tsx
934
+ import { lazyFn } from '@tanstack/solid-router'
935
+
936
+ const route = createRoute({
937
+ path: '/my-route',
938
+ component: MyComponent,
939
+ loader: lazyFn(() => import('./loader'), 'loader'),
940
+ })
941
+
942
+ // In another file...a
943
+ export const loader = async (context: LoaderContext) => {
944
+ /// ...
945
+ }
946
+ \`\`\`
947
+
948
+ <!-- ::end:framework -->
949
+
708
950
  If you are using file-based routing, you'll only be able to split your \`loader\` if you are using [Automatic Code Splitting](#using-automatic-code-splitting) with customized bundling options.
709
951
 
710
952
  ## Manually accessing Route APIs in other files with the \`getRouteApi\` helper
711
953
 
712
954
  As you might have guessed, placing your component code in a separate file than your route can make it difficult to consume the route itself. To help with this, TanStack Router exports a handy \`getRouteApi\` function that you can use to access a route's type-safe APIs in a file without importing the route itself.
713
955
 
714
- - \`my-route.tsx\`
956
+ <!-- ::start:framework -->
715
957
 
716
- \`\`\`tsx
958
+ # React
959
+
960
+ <!-- ::start:tabs variant="files" -->
961
+
962
+ \`\`\`tsx title="src/my-route.tsx"
717
963
  import { createRoute } from '@tanstack/react-router'
718
964
  import { MyComponent } from './MyComponent'
719
965
 
@@ -726,9 +972,7 @@ const route = createRoute({
726
972
  })
727
973
  \`\`\`
728
974
 
729
- - \`MyComponent.tsx\`
730
-
731
- \`\`\`tsx
975
+ \`\`\`tsx title="src/MyComponent.tsx"
732
976
  import { getRouteApi } from '@tanstack/react-router'
733
977
 
734
978
  const route = getRouteApi('/my-route')
@@ -741,6 +985,42 @@ export function MyComponent() {
741
985
  }
742
986
  \`\`\`
743
987
 
988
+ <!-- ::end:tabs -->
989
+
990
+ # Solid
991
+
992
+ <!-- ::start:tabs variant="files" -->
993
+
994
+ \`\`\`tsx title="src/my-route.tsx"
995
+ import { createRoute } from '@tanstack/solid-router'
996
+ import { MyComponent } from './MyComponent'
997
+
998
+ const route = createRoute({
999
+ path: '/my-route',
1000
+ loader: () => ({
1001
+ foo: 'bar',
1002
+ }),
1003
+ component: MyComponent,
1004
+ })
1005
+ \`\`\`
1006
+
1007
+ \`\`\`tsx title="src/MyComponent.tsx"
1008
+ import { getRouteApi } from '@tanstack/solid-router'
1009
+
1010
+ const route = getRouteApi('/my-route')
1011
+
1012
+ export function MyComponent() {
1013
+ const loaderData = route.useLoaderData()
1014
+ // ^? { foo: string }
1015
+
1016
+ return <div>...</div>
1017
+ }
1018
+ \`\`\`
1019
+
1020
+ <!-- ::end:tabs -->
1021
+
1022
+ <!-- ::end:framework -->
1023
+
744
1024
  The \`getRouteApi\` function is useful for accessing other type-safe APIs:
745
1025
 
746
1026
  - \`useLoaderData\`
@@ -752,11 +1032,15 @@ The \`getRouteApi\` function is useful for accessing other type-safe APIs:
752
1032
 
753
1033
  # Creating a Router
754
1034
 
755
- ## The \`Router\` Class
1035
+ ## The \`createRouter\` function
756
1036
 
757
1037
  When you're ready to start using your router, you'll need to create a new \`Router\` instance. The router instance is the core brains of TanStack Router and is responsible for managing the route tree, matching routes, and coordinating navigations and route transitions. It also serves as a place to configure router-wide settings.
758
1038
 
759
- \`\`\`tsx
1039
+ <!-- ::start:framework -->
1040
+
1041
+ # React
1042
+
1043
+ \`\`\`tsx title="src/router.tsx"
760
1044
  import { createRouter } from '@tanstack/react-router'
761
1045
 
762
1046
  const router = createRouter({
@@ -764,7 +1048,19 @@ const router = createRouter({
764
1048
  })
765
1049
  \`\`\`
766
1050
 
767
- ## Route Tree
1051
+ # Solid
1052
+
1053
+ \`\`\`tsx title="src/router.tsx"
1054
+ import { createRouter } from '@tanstack/solid-router'
1055
+
1056
+ const router = createRouter({
1057
+ // ...
1058
+ })
1059
+ \`\`\`
1060
+
1061
+ <!-- ::end:framework -->
1062
+
1063
+ ## Route Tree
768
1064
 
769
1065
  You'll probably notice quickly that the \`Router\` constructor requires a \`routeTree\` option. This is the route tree that the router will use to match routes and render components.
770
1066
 
@@ -795,7 +1091,11 @@ const routeTree = rootRoute.addChildren([
795
1091
 
796
1092
  TanStack Router provides amazing support for TypeScript, even for things you wouldn't expect like bare imports straight from the library! To make this possible, you must register your router's types using TypeScripts' [Declaration Merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) feature. This is done by extending the \`Register\` interface on \`@tanstack/react-router\` with a \`router\` property that has the type of your \`router\` instance:
797
1093
 
798
- \`\`\`tsx
1094
+ <!-- ::start:framework -->
1095
+
1096
+ # React
1097
+
1098
+ \`\`\`tsx title="src/router.tsx"
799
1099
  declare module '@tanstack/react-router' {
800
1100
  interface Register {
801
1101
  // This infers the type of our router and registers it across your entire project
@@ -804,6 +1104,19 @@ declare module '@tanstack/react-router' {
804
1104
  }
805
1105
  \`\`\`
806
1106
 
1107
+ # Solid
1108
+
1109
+ \`\`\`tsx title="src/router.tsx"
1110
+ declare module '@tanstack/solid-router' {
1111
+ interface Register {
1112
+ // This infers the type of our router and registers it across your entire project
1113
+ router: typeof router
1114
+ }
1115
+ }
1116
+ \`\`\`
1117
+
1118
+ <!-- ::end:framework -->
1119
+
807
1120
  With your router registered, you'll now get type-safety across your entire project for anything related to routing.
808
1121
 
809
1122
  ## 404 Not Found Route
@@ -837,7 +1150,9 @@ While repeating yourself can be acceptable in many situations, you might find th
837
1150
 
838
1151
  If you want to create a basic custom link component, you can do so with the following:
839
1152
 
840
- [//]: # 'BasicExampleImplementation'
1153
+ <!-- ::start:framework -->
1154
+
1155
+ # React
841
1156
 
842
1157
  \`\`\`tsx
843
1158
  import * as React from 'react'
@@ -862,7 +1177,34 @@ export const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
862
1177
  }
863
1178
  \`\`\`
864
1179
 
865
- [//]: # 'BasicExampleImplementation'
1180
+ # Solid
1181
+
1182
+ \`\`\`tsx
1183
+ import * as Solid from 'solid-js'
1184
+ import { createLink, LinkComponent } from '@tanstack/solid-router'
1185
+
1186
+ export const Route = createRootRoute({
1187
+ component: RootComponent,
1188
+ })
1189
+
1190
+ type BasicLinkProps = Solid.JSX.IntrinsicElements['a'] & {
1191
+ // Add any additional props you want to pass to the anchor element
1192
+ }
1193
+
1194
+ const BasicLinkComponent: Solid.Component<BasicLinkProps> = (props) => (
1195
+ <a {...props} class="block px-3 py-2 text-red-700">
1196
+ {props.children}
1197
+ </a>
1198
+ )
1199
+
1200
+ const CreatedLinkComponent = createLink(BasicLinkComponent)
1201
+
1202
+ export const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
1203
+ return <CreatedLinkComponent preload={'intent'} {...props} />
1204
+ }
1205
+ \`\`\`
1206
+
1207
+ <!-- ::end:framework -->
866
1208
 
867
1209
  You can then use your newly created \`Link\` component as any other \`Link\`
868
1210
 
@@ -870,17 +1212,21 @@ You can then use your newly created \`Link\` component as any other \`Link\`
870
1212
  <CustomLink to={'/dashboard/invoices/$invoiceId'} params={{ invoiceId: 0 }} />
871
1213
  \`\`\`
872
1214
 
873
- [//]: # 'ExamplesUsingThirdPartyLibs'
874
-
875
1215
  ## \`createLink\` with third party libraries
876
1216
 
877
1217
  Here are some examples of how you can use \`createLink\` with third-party libraries.
878
1218
 
1219
+ <!-- ::start:framework -->
1220
+
1221
+ # React
1222
+
879
1223
  ### React Aria Components example
880
1224
 
881
1225
  React Aria Components v1.11.0 and later works with TanStack Router's \`preload (intent)\` prop. Use \`createLink\` to wrap each React Aria component that you use as a link.
882
1226
 
883
- \`\`\`tsx
1227
+ <!-- ::start:tabs variant="files" -->
1228
+
1229
+ \`\`\`tsx title="RACLink.tsx"
884
1230
  import { createLink } from '@tanstack/react-router'
885
1231
  import { Link as RACLink, MenuItem } from 'react-aria-components'
886
1232
 
@@ -888,9 +1234,7 @@ export const Link = createLink(RACLink)
888
1234
  export const MenuItemLink = createLink(MenuItem)
889
1235
  \`\`\`
890
1236
 
891
- To use React Aria's render props, including the \`className\`, \`style\`, and \`children\` functions, create a wrapper component and pass that to \`createLink\`.
892
-
893
- \`\`\`tsx
1237
+ \`\`\`tsx title="CustomRACLink.tsx"
894
1238
  import { createLink } from '@tanstack/react-router'
895
1239
  import { Link as RACLink, type LinkProps } from 'react-aria-components'
896
1240
 
@@ -912,9 +1256,19 @@ function MyLink(props: MyLinkProps) {
912
1256
  export const Link = createLink(MyLink)
913
1257
  \`\`\`
914
1258
 
1259
+ <!-- ::end:tabs -->
1260
+
1261
+ To use React Aria's render props, including the \`className\`, \`style\`, and \`children\` functions, create a wrapper component and pass that to \`createLink\`.
1262
+
1263
+ <!-- ::end:framework -->
1264
+
1265
+ <!-- ::start:framework -->
1266
+
1267
+ # React
1268
+
915
1269
  ### Chakra UI example
916
1270
 
917
- \`\`\`tsx
1271
+ \`\`\`tsx title="ChakraLinkComponent.tsx"
918
1272
  import * as React from 'react'
919
1273
  import { createLink, LinkComponent } from '@tanstack/react-router'
920
1274
  import { Link } from '@chakra-ui/react'
@@ -950,6 +1304,12 @@ export const CustomLink: LinkComponent<typeof ChakraLinkComponent> = (
950
1304
  }
951
1305
  \`\`\`
952
1306
 
1307
+ <!-- ::end:framework -->
1308
+
1309
+ <!-- ::start:framework -->
1310
+
1311
+ # React
1312
+
953
1313
  ### MUI example
954
1314
 
955
1315
  There is an [example](https://github.com/TanStack/router/tree/main/examples/react/start-material-ui) available which uses these patterns.
@@ -958,16 +1318,22 @@ There is an [example](https://github.com/TanStack/router/tree/main/examples/reac
958
1318
 
959
1319
  If the MUI \`Link\` should simply behave like the router \`Link\`, it can be just wrapped with \`createLink\`:
960
1320
 
961
- \`\`\`tsx
1321
+ <!-- ::start:tabs variant="files" -->
1322
+
1323
+ \`\`\`tsx title="CustomLink.tsx"
962
1324
  import { createLink } from '@tanstack/react-router'
963
1325
  import { Link } from '@mui/material'
964
1326
 
965
1327
  export const CustomLink = createLink(Link)
966
1328
  \`\`\`
967
1329
 
1330
+ <!-- ::end:tabs -->
1331
+
968
1332
  If the \`Link\` should be customized this approach can be used:
969
1333
 
970
- \`\`\`tsx
1334
+ <!-- ::start:tabs variant="files" -->
1335
+
1336
+ \`\`\`tsx title="CustomLink.tsx"
971
1337
  import React from 'react'
972
1338
  import { createLink } from '@tanstack/react-router'
973
1339
  import { Link } from '@mui/material'
@@ -991,11 +1357,15 @@ export const CustomLink: LinkComponent<typeof MUILinkComponent> = (props) => {
991
1357
  // Can also be styled
992
1358
  \`\`\`
993
1359
 
1360
+ <!-- ::end:tabs -->
1361
+
994
1362
  #### \`Button\`
995
1363
 
996
1364
  If a \`Button\` should be used as a router \`Link\`, the \`component\` should be set as \`a\`:
997
1365
 
998
- \`\`\`tsx
1366
+ <!-- ::start:tabs variant="files" -->
1367
+
1368
+ \`\`\`tsx title="CustomButtonLink.tsx"
999
1369
  import React from 'react'
1000
1370
  import { createLink } from '@tanstack/react-router'
1001
1371
  import { Button } from '@mui/material'
@@ -1020,11 +1390,15 @@ export const CustomButtonLink: LinkComponent<typeof MUIButtonLinkComponent> = (
1020
1390
  }
1021
1391
  \`\`\`
1022
1392
 
1393
+ <!-- ::end:tabs -->
1394
+
1023
1395
  #### Usage with \`styled\`
1024
1396
 
1025
1397
  Any of these MUI approaches can then be used with \`styled\`:
1026
1398
 
1027
- \`\`\`tsx
1399
+ <!-- ::start:tabs variant="files" -->
1400
+
1401
+ \`\`\`tsx title="StyledCustomLink.tsx"
1028
1402
  import { css, styled } from '@mui/material'
1029
1403
  import { CustomLink } from './CustomLink'
1030
1404
 
@@ -1035,9 +1409,19 @@ const StyledCustomLink = styled(CustomLink)(
1035
1409
  )
1036
1410
  \`\`\`
1037
1411
 
1412
+ <!-- ::end:tabs -->
1413
+
1414
+ <!-- ::end:framework -->
1415
+
1416
+ <!-- ::start:framework -->
1417
+
1418
+ # React
1419
+
1038
1420
  ### Mantine example
1039
1421
 
1040
- \`\`\`tsx
1422
+ <!-- ::start:tabs variant="files" -->
1423
+
1424
+ \`\`\`tsx title="CustomLink.tsx"
1041
1425
  import * as React from 'react'
1042
1426
  import { createLink, LinkComponent } from '@tanstack/react-router'
1043
1427
  import { Anchor, AnchorProps } from '@mantine/core'
@@ -1062,7 +1446,25 @@ export const CustomLink: LinkComponent<typeof MantineLinkComponent> = (
1062
1446
  }
1063
1447
  \`\`\`
1064
1448
 
1065
- [//]: # 'ExamplesUsingThirdPartyLibs'
1449
+ <!-- ::end:tabs -->
1450
+
1451
+ <!-- ::end:framework -->
1452
+
1453
+ <!-- ::start:framework -->
1454
+
1455
+ # Solid
1456
+
1457
+ ### Some Library example
1458
+
1459
+ <!-- ::start:tabs variant="files" -->
1460
+
1461
+ \`\`\`tsx title="UntitledLink.tsx"
1462
+ // TODO: Add this example.
1463
+ \`\`\`
1464
+
1465
+ <!-- ::end:tabs -->
1466
+
1467
+ <!-- ::end:framework -->
1066
1468
 
1067
1469
  # Custom Search Param Serialization
1068
1470
 
@@ -1086,6 +1488,10 @@ It would be serialized and escaped into the following search string:
1086
1488
 
1087
1489
  We can implement the default behavior with the following code:
1088
1490
 
1491
+ <!-- ::start:framework -->
1492
+
1493
+ # React
1494
+
1089
1495
  \`\`\`tsx
1090
1496
  import {
1091
1497
  createRouter,
@@ -1100,6 +1506,24 @@ const router = createRouter({
1100
1506
  })
1101
1507
  \`\`\`
1102
1508
 
1509
+ # Solid
1510
+
1511
+ \`\`\`tsx
1512
+ import {
1513
+ createRouter,
1514
+ parseSearchWith,
1515
+ stringifySearchWith,
1516
+ } from '@tanstack/solid-router'
1517
+
1518
+ const router = createRouter({
1519
+ // ...
1520
+ parseSearch: parseSearchWith(JSON.parse),
1521
+ stringifySearch: stringifySearchWith(JSON.stringify),
1522
+ })
1523
+ \`\`\`
1524
+
1525
+ <!-- ::end:framework -->
1526
+
1103
1527
  However, this default behavior may not be suitable for all use cases. For example, you may want to use a different serialization format, such as base64 encoding, or you may want to use a purpose-built serialization/deserialization library, like [query-string](https://github.com/sindresorhus/query-string), [JSURL2](https://github.com/wmertens/jsurl2), or [Zipson](https://jgranstrom.github.io/zipson/).
1104
1528
 
1105
1529
  This can be achieved by providing your own serialization and deserialization functions to the \`parseSearch\` and \`stringifySearch\` options in the [\`Router\`](../api/router/RouterOptionsType.md#stringifysearch-method) configuration. When doing this, you can utilize TanStack Router's built-in helper functions, \`parseSearchWith\` and \`stringifySearchWith\`, to simplify the process.
@@ -1115,6 +1539,10 @@ Here are some examples of how you can customize the search param serialization i
1115
1539
 
1116
1540
  It's common to base64 encode your search params to achieve maximum compatibility across browsers and URL unfurlers, etc. This can be done with the following code:
1117
1541
 
1542
+ <!-- ::start:framework -->
1543
+
1544
+ # React
1545
+
1118
1546
  \`\`\`tsx
1119
1547
  import {
1120
1548
  Router,
@@ -1148,6 +1576,43 @@ function encodeToBinary(str: string): string {
1148
1576
  }
1149
1577
  \`\`\`
1150
1578
 
1579
+ # Solid
1580
+
1581
+ \`\`\`tsx
1582
+ import {
1583
+ Router,
1584
+ parseSearchWith,
1585
+ stringifySearchWith,
1586
+ } from '@tanstack/solid-router'
1587
+
1588
+ const router = createRouter({
1589
+ parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
1590
+ stringifySearch: stringifySearchWith((value) =>
1591
+ encodeToBinary(JSON.stringify(value)),
1592
+ ),
1593
+ })
1594
+
1595
+ function decodeFromBinary(str: string): string {
1596
+ return decodeURIComponent(
1597
+ Array.prototype.map
1598
+ .call(atob(str), function (c) {
1599
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
1600
+ })
1601
+ .join(''),
1602
+ )
1603
+ }
1604
+
1605
+ function encodeToBinary(str: string): string {
1606
+ return btoa(
1607
+ encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
1608
+ return String.fromCharCode(parseInt(p1, 16))
1609
+ }),
1610
+ )
1611
+ }
1612
+ \`\`\`
1613
+
1614
+ <!-- ::end:framework -->
1615
+
1151
1616
  > [⚠️ Why does this snippet not use atob/btoa?](#safe-binary-encodingdecoding)
1152
1617
 
1153
1618
  So, if we were to turn the previous object into a search string using this configuration, it would look like this:
@@ -1163,6 +1628,10 @@ So, if we were to turn the previous object into a search string using this confi
1163
1628
 
1164
1629
  The [query-string](https://github.com/sindresorhus/query-string) library is a popular for being able to reliably parse and stringify query strings. You can use it to customize the serialization format of your search params. This can be done with the following code:
1165
1630
 
1631
+ <!-- ::start:framework -->
1632
+
1633
+ # React
1634
+
1166
1635
  \`\`\`tsx
1167
1636
  import { createRouter } from '@tanstack/react-router'
1168
1637
  import qs from 'query-string'
@@ -1182,6 +1651,29 @@ const router = createRouter({
1182
1651
  })
1183
1652
  \`\`\`
1184
1653
 
1654
+ # Solid
1655
+
1656
+ \`\`\`tsx
1657
+ import { createRouter } from '@tanstack/solid-router'
1658
+ import qs from 'query-string'
1659
+
1660
+ const router = createRouter({
1661
+ // ...
1662
+ stringifySearch: stringifySearchWith((value) =>
1663
+ qs.stringify(value, {
1664
+ // ...options
1665
+ }),
1666
+ ),
1667
+ parseSearch: parseSearchWith((value) =>
1668
+ qs.parse(value, {
1669
+ // ...options
1670
+ }),
1671
+ ),
1672
+ })
1673
+ \`\`\`
1674
+
1675
+ <!-- ::end:framework -->
1676
+
1185
1677
  So, if we were to turn the previous object into a search string using this configuration, it would look like this:
1186
1678
 
1187
1679
  \`\`\`txt
@@ -1192,6 +1684,10 @@ So, if we were to turn the previous object into a search string using this confi
1192
1684
 
1193
1685
  [JSURL2](https://github.com/wmertens/jsurl2) is a non-standard library that can compress URLs while still maintaining readability. This can be done with the following code:
1194
1686
 
1687
+ <!-- ::start:framework -->
1688
+
1689
+ # React
1690
+
1195
1691
  \`\`\`tsx
1196
1692
  import {
1197
1693
  Router,
@@ -1207,6 +1703,25 @@ const router = createRouter({
1207
1703
  })
1208
1704
  \`\`\`
1209
1705
 
1706
+ # Solid
1707
+
1708
+ \`\`\`tsx
1709
+ import {
1710
+ Router,
1711
+ parseSearchWith,
1712
+ stringifySearchWith,
1713
+ } from '@tanstack/solid-router'
1714
+ import { parse, stringify } from 'jsurl2'
1715
+
1716
+ const router = createRouter({
1717
+ // ...
1718
+ parseSearch: parseSearchWith(parse),
1719
+ stringifySearch: stringifySearchWith(stringify),
1720
+ })
1721
+ \`\`\`
1722
+
1723
+ <!-- ::end:framework -->
1724
+
1210
1725
  So, if we were to turn the previous object into a search string using this configuration, it would look like this:
1211
1726
 
1212
1727
  \`\`\`txt
@@ -1217,6 +1732,10 @@ So, if we were to turn the previous object into a search string using this confi
1217
1732
 
1218
1733
  [Zipson](https://jgranstrom.github.io/zipson/) is a very user-friendly and performant JSON compression library (both in runtime performance and the resulting compression performance). To compress your search params with it (which requires escaping/unescaping and base64 encoding/decoding them as well), you can use the following code:
1219
1734
 
1735
+ <!-- ::start:framework -->
1736
+
1737
+ # React
1738
+
1220
1739
  \`\`\`tsx
1221
1740
  import {
1222
1741
  Router,
@@ -1251,6 +1770,44 @@ function encodeToBinary(str: string): string {
1251
1770
  }
1252
1771
  \`\`\`
1253
1772
 
1773
+ # Solid
1774
+
1775
+ \`\`\`tsx
1776
+ import {
1777
+ Router,
1778
+ parseSearchWith,
1779
+ stringifySearchWith,
1780
+ } from '@tanstack/solid-router'
1781
+ import { stringify, parse } from 'zipson'
1782
+
1783
+ const router = createRouter({
1784
+ parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
1785
+ stringifySearch: stringifySearchWith((value) =>
1786
+ encodeToBinary(stringify(value)),
1787
+ ),
1788
+ })
1789
+
1790
+ function decodeFromBinary(str: string): string {
1791
+ return decodeURIComponent(
1792
+ Array.prototype.map
1793
+ .call(atob(str), function (c) {
1794
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
1795
+ })
1796
+ .join(''),
1797
+ )
1798
+ }
1799
+
1800
+ function encodeToBinary(str: string): string {
1801
+ return btoa(
1802
+ encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
1803
+ return String.fromCharCode(parseInt(p1, 16))
1804
+ }),
1805
+ )
1806
+ }
1807
+ \`\`\`
1808
+
1809
+ <!-- ::end:framework -->
1810
+
1254
1811
  > [⚠️ Why does this snippet not use atob/btoa?](#safe-binary-encodingdecoding)
1255
1812
 
1256
1813
  So, if we were to turn the previous object into a search string using this configuration, it would look like this:
@@ -1261,13 +1818,13 @@ So, if we were to turn the previous object into a search string using this confi
1261
1818
 
1262
1819
  <hr>
1263
1820
 
1264
- ### Safe Binary Encoding/Decoding
1821
+ ## Safe Binary Encoding/Decoding
1265
1822
 
1266
1823
  In the browser, the \`atob\` and \`btoa\` functions are not guaranteed to work properly with non-UTF8 characters. We recommend using these encoding/decoding utilities instead:
1267
1824
 
1268
1825
  To encode from a string to a binary string:
1269
1826
 
1270
- \`\`\`typescript
1827
+ \`\`\`ts
1271
1828
  export function encodeToBinary(str: string): string {
1272
1829
  return btoa(
1273
1830
  encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
@@ -1279,7 +1836,7 @@ export function encodeToBinary(str: string): string {
1279
1836
 
1280
1837
  To decode from a binary string to a string:
1281
1838
 
1282
- \`\`\`typescript
1839
+ \`\`\`ts
1283
1840
  export function decodeFromBinary(str: string): string {
1284
1841
  return decodeURIComponent(
1285
1842
  Array.prototype.map
@@ -1350,7 +1907,7 @@ The router cache is built-in and is as easy as returning data from any route's \
1350
1907
  Route \`loader\` functions are called when a route match is loaded. They are called with a single parameter which is an object containing many helpful properties. We'll go over those in a bit, but first, let's look at an example of a route \`loader\` function:
1351
1908
 
1352
1909
  \`\`\`tsx
1353
- // routes/posts.tsx
1910
+ // src/routes/posts.tsx
1354
1911
  export const Route = createFileRoute('/posts')({
1355
1912
  loader: () => fetchPosts(),
1356
1913
  })
@@ -1387,6 +1944,10 @@ const posts = Route.useLoaderData()
1387
1944
 
1388
1945
  If you don't have ready access to your route object (i.e. you're deep in the component tree for the current route), you can use \`getRouteApi\` to access the same hook (as well as the other hooks on the Route object). This should be preferred over importing the Route object, which is likely to create circular dependencies.
1389
1946
 
1947
+ <!-- ::start:framework -->
1948
+
1949
+ # React
1950
+
1390
1951
  \`\`\`tsx
1391
1952
  import { getRouteApi } from '@tanstack/react-router'
1392
1953
 
@@ -1396,6 +1957,19 @@ const routeApi = getRouteApi('/posts')
1396
1957
  const data = routeApi.useLoaderData()
1397
1958
  \`\`\`
1398
1959
 
1960
+ # Solid
1961
+
1962
+ \`\`\`tsx
1963
+ import { getRouteApi } from '@tanstack/solid-router'
1964
+
1965
+ // in your component
1966
+
1967
+ const routeApi = getRouteApi('/posts')
1968
+ const data = routeApi.useLoaderData()
1969
+ \`\`\`
1970
+
1971
+ <!-- ::end:framework -->
1972
+
1399
1973
  ## Dependency-based Stale-While-Revalidate Caching
1400
1974
 
1401
1975
  TanStack Router provides a built-in Stale-While-Revalidate caching layer for route loaders that is keyed on the dependencies of a route:
@@ -1570,6 +2144,10 @@ export const fetchPosts = async () => {
1570
2144
 
1571
2145
  - \`/routes/__root.tsx\`
1572
2146
 
2147
+ <!-- ::start:framework -->
2148
+
2149
+ # React
2150
+
1573
2151
  \`\`\`tsx
1574
2152
  import { createRootRouteWithContext } from '@tanstack/react-router'
1575
2153
 
@@ -1579,11 +2157,22 @@ export const Route = createRootRouteWithContext<{
1579
2157
  }>()() // NOTE: the double call is on purpose, since createRootRouteWithContext is a factory ;)
1580
2158
  \`\`\`
1581
2159
 
1582
- - \`/routes/posts.tsx\`
2160
+ # Solid
1583
2161
 
1584
2162
  \`\`\`tsx
1585
- import { createFileRoute } from '@tanstack/react-router'
2163
+ import { createRootRouteWithContext } from '@tanstack/solid-router'
2164
+
2165
+ // Create a root route using the createRootRouteWithContext<{...}>() function and pass it whatever types you would like to be available in your router context.
2166
+ export const Route = createRootRouteWithContext<{
2167
+ fetchPosts: typeof fetchPosts
2168
+ }>()() // NOTE: the double call is on purpose, since createRootRouteWithContext is a factory ;)
2169
+ \`\`\`
1586
2170
 
2171
+ <!-- ::end:framework -->
2172
+
2173
+ - \`/routes/posts.tsx\`
2174
+
2175
+ \`\`\`tsx
1587
2176
  // Notice how our postsRoute references context to get our fetchPosts function
1588
2177
  // This can be a powerful tool for dependency injection across your router
1589
2178
  // and routes.
@@ -1613,7 +2202,7 @@ const router = createRouter({
1613
2202
  To use path params in your \`loader\` function, access them via the \`params\` property on the function's parameters. Here's an example:
1614
2203
 
1615
2204
  \`\`\`tsx
1616
- // routes/posts.$postId.tsx
2205
+ // src/routes/posts.$postId.tsx
1617
2206
  export const Route = createFileRoute('/posts/$postId')({
1618
2207
  loader: ({ params: { postId } }) => fetchPostById(postId),
1619
2208
  })
@@ -1624,9 +2213,7 @@ export const Route = createFileRoute('/posts/$postId')({
1624
2213
  Passing down global context to your router is great, but what if you want to provide context that is specific to a route? This is where the \`beforeLoad\` option comes in. The \`beforeLoad\` option is a function that runs right before attempting to load a route and receives the same parameters as \`loader\`. Beyond its ability to redirect potential matches, block loader requests, etc, it can also return an object that will be merged into the route's context. Let's take a look at an example where we inject some data into our route context via the \`beforeLoad\` option:
1625
2214
 
1626
2215
  \`\`\`tsx
1627
- // /routes/posts.tsx
1628
- import { createFileRoute } from '@tanstack/react-router'
1629
-
2216
+ // src/routes/posts.tsx
1630
2217
  export const Route = createFileRoute('/posts')({
1631
2218
  // Pass the fetchPosts function to the route context
1632
2219
  beforeLoad: () => ({
@@ -1688,7 +2275,7 @@ export const Route = createFileRoute('/posts')({
1688
2275
  The \`abortController\` property of the \`loader\` function is an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). Its signal is cancelled when the route is unloaded or when the \`loader\` call becomes outdated. This is useful for cancelling network requests when the route is unloaded or when the route's params change. Here is an example using it with a fetch call:
1689
2276
 
1690
2277
  \`\`\`tsx
1691
- // routes/posts.tsx
2278
+ // src/routes/posts.tsx
1692
2279
  export const Route = createFileRoute('/posts')({
1693
2280
  loader: ({ abortController }) =>
1694
2281
  fetchPosts({
@@ -1703,7 +2290,7 @@ export const Route = createFileRoute('/posts')({
1703
2290
  The \`preload\` property of the \`loader\` function is a boolean which is \`true\` when the route is being preloaded instead of loaded. Some data loading libraries may handle preloading differently than a standard fetch, so you may want to pass \`preload\` to your data loading library, or use it to execute the appropriate data loading logic:
1704
2291
 
1705
2292
  \`\`\`tsx
1706
- // routes/posts.tsx
2293
+ // src/routes/posts.tsx
1707
2294
  export const Route = createFileRoute('/posts')({
1708
2295
  loader: async ({ preload }) =>
1709
2296
  fetchPosts({
@@ -1744,7 +2331,7 @@ TanStack Router provides a few ways to handle errors that occur during the route
1744
2331
  The \`routeOptions.onError\` option is a function that is called when an error occurs during the route loading.
1745
2332
 
1746
2333
  \`\`\`tsx
1747
- // routes/posts.tsx
2334
+ // src/routes/posts.tsx
1748
2335
  export const Route = createFileRoute('/posts')({
1749
2336
  loader: () => fetchPosts(),
1750
2337
  onError: ({ error }) => {
@@ -1759,7 +2346,7 @@ export const Route = createFileRoute('/posts')({
1759
2346
  The \`routeOptions.onCatch\` option is a function that is called whenever an error was caught by the router's CatchBoundary.
1760
2347
 
1761
2348
  \`\`\`tsx
1762
- // routes/posts.tsx
2349
+ // src/routes/posts.tsx
1763
2350
  export const Route = createFileRoute('/posts')({
1764
2351
  onCatch: ({ error, errorInfo }) => {
1765
2352
  // Log the error
@@ -1776,7 +2363,7 @@ The \`routeOptions.errorComponent\` option is a component that is rendered when
1776
2363
  - \`reset\` - A function to reset the internal \`CatchBoundary\`
1777
2364
 
1778
2365
  \`\`\`tsx
1779
- // routes/posts.tsx
2366
+ // src/routes/posts.tsx
1780
2367
  export const Route = createFileRoute('/posts')({
1781
2368
  loader: () => fetchPosts(),
1782
2369
  errorComponent: ({ error }) => {
@@ -1789,7 +2376,7 @@ export const Route = createFileRoute('/posts')({
1789
2376
  The \`reset\` function can be used to allow the user to retry rendering the error boundaries normal children:
1790
2377
 
1791
2378
  \`\`\`tsx
1792
- // routes/posts.tsx
2379
+ // src/routes/posts.tsx
1793
2380
  export const Route = createFileRoute('/posts')({
1794
2381
  loader: () => fetchPosts(),
1795
2382
  errorComponent: ({ error, reset }) => {
@@ -1813,7 +2400,7 @@ export const Route = createFileRoute('/posts')({
1813
2400
  If the error was the result of a route load, you should instead call \`router.invalidate()\`, which will coordinate both a router reload and an error boundary reset:
1814
2401
 
1815
2402
  \`\`\`tsx
1816
- // routes/posts.tsx
2403
+ // src/routes/posts.tsx
1817
2404
  export const Route = createFileRoute('/posts')({
1818
2405
  loader: () => fetchPosts(),
1819
2406
  errorComponent: ({ error, reset }) => {
@@ -1841,9 +2428,7 @@ export const Route = createFileRoute('/posts')({
1841
2428
  TanStack Router provides a default \`ErrorComponent\` that is rendered when an error occurs during the route loading or rendering lifecycle. If you choose to override your routes' error components, it's still wise to always fall back to rendering any uncaught errors with the default \`ErrorComponent\`:
1842
2429
 
1843
2430
  \`\`\`tsx
1844
- // routes/posts.tsx
1845
- import { createFileRoute, ErrorComponent } from '@tanstack/react-router'
1846
-
2431
+ // src/routes/posts.tsx
1847
2432
  export const Route = createFileRoute('/posts')({
1848
2433
  loader: () => fetchPosts(),
1849
2434
  errorComponent: ({ error }) => {
@@ -2064,12 +2649,14 @@ The \`Await\` component resolves the promise by triggering the nearest suspense
2064
2649
 
2065
2650
  If the promise is rejected, the \`Await\` component will throw the serialized error, which can be caught by the nearest error boundary.
2066
2651
 
2067
- [//]: # 'DeferredWithAwaitFinalTip'
2652
+ <!-- ::start:framework -->
2653
+
2654
+ # React
2068
2655
 
2069
2656
  > [!TIP]
2070
2657
  > In React 19, you can use the \`use()\` hook instead of \`Await\`
2071
2658
 
2072
- [//]: # 'DeferredWithAwaitFinalTip'
2659
+ <!-- ::end:framework -->
2073
2660
 
2074
2661
  ## Deferred Data Loading with External libraries
2075
2662
 
@@ -2077,6 +2664,10 @@ When your strategy for fetching information for the route relies on [External Da
2077
2664
 
2078
2665
  So, instead of using \`defer\` and \`Await\`, you'll instead want to use the Route's \`loader\` to kick off the data fetching and then use the library's hooks to access the data in your components.
2079
2666
 
2667
+ <!-- ::start:framework -->
2668
+
2669
+ # React
2670
+
2080
2671
  \`\`\`tsx
2081
2672
  // src/routes/posts.$postId.tsx
2082
2673
  import { createFileRoute } from '@tanstack/react-router'
@@ -2093,8 +2684,32 @@ export const Route = createFileRoute('/posts/$postId')({
2093
2684
  })
2094
2685
  \`\`\`
2095
2686
 
2687
+ # Solid
2688
+
2689
+ \`\`\`tsx
2690
+ // src/routes/posts.$postId.tsx
2691
+ import { createFileRoute } from '@tanstack/solid-router'
2692
+ import { slowDataOptions, fastDataOptions } from '~/api/query-options'
2693
+
2694
+ export const Route = createFileRoute('/posts/$postId')({
2695
+ loader: async ({ context: { queryClient } }) => {
2696
+ // Kick off the fetching of some slower data, but do not await it
2697
+ queryClient.prefetchQuery(slowDataOptions())
2698
+
2699
+ // Fetch and await some data that resolves quickly
2700
+ await queryClient.ensureQueryData(fastDataOptions())
2701
+ },
2702
+ })
2703
+ \`\`\`
2704
+
2705
+ <!-- ::end:framework -->
2706
+
2096
2707
  Then in your component, you can use the library's hooks to access the data:
2097
2708
 
2709
+ <!-- ::start:framework -->
2710
+
2711
+ # React
2712
+
2098
2713
  \`\`\`tsx
2099
2714
  // src/routes/posts.$postId.tsx
2100
2715
  import { createFileRoute } from '@tanstack/react-router'
@@ -2125,11 +2740,47 @@ function SlowDataComponent() {
2125
2740
  }
2126
2741
  \`\`\`
2127
2742
 
2743
+ # Solid
2744
+
2745
+ \`\`\`tsx
2746
+ // src/routes/posts.$postId.tsx
2747
+ import { createFileRoute } from '@tanstack/solid-router'
2748
+ import { useSuspenseQuery } from '@tanstack/solid-query'
2749
+ import { slowDataOptions, fastDataOptions } from '~/api/query-options'
2750
+
2751
+ export const Route = createFileRoute('/posts/$postId')({
2752
+ // ...
2753
+ component: PostIdComponent,
2754
+ })
2755
+
2756
+ function PostIdComponent() {
2757
+ const fastData = useSuspenseQuery(fastDataOptions())
2758
+
2759
+ // do something with fastData
2760
+
2761
+ return (
2762
+ <Suspense fallback={<div>Loading...</div>}>
2763
+ <SlowDataComponent />
2764
+ </Suspense>
2765
+ )
2766
+ }
2767
+
2768
+ function SlowDataComponent() {
2769
+ const data = useSuspenseQuery(slowDataOptions())
2770
+
2771
+ return <div>{data()}</div>
2772
+ }
2773
+ \`\`\`
2774
+
2775
+ <!-- ::end:framework -->
2776
+
2128
2777
  ## Caching and Invalidation
2129
2778
 
2130
2779
  Streamed promises follow the same lifecycle as the loader data they are associated with. They can even be preloaded!
2131
2780
 
2132
- [//]: # 'SSRContent'
2781
+ <!-- ::start:framework -->
2782
+
2783
+ # React
2133
2784
 
2134
2785
  ## SSR & Streaming Deferred Data
2135
2786
 
@@ -2155,17 +2806,17 @@ The following is a high-level overview of how deferred data streaming works with
2155
2806
  - Client
2156
2807
  - The suspended placeholder promises within \`<Await>\` are resolved with the streamed data/error responses and either render the result or throw the error to the nearest error boundary
2157
2808
 
2158
- [//]: # 'SSRContent'
2809
+ <!-- ::end:framework -->
2159
2810
 
2160
2811
  # Document Head Management
2161
2812
 
2162
- Document head management is the process of managing the head, title, meta, link, and script tags of a document and TanStack Router provides a robust way to manage the document head for full-stack applications that use Start and for single-page applications that use \`@tanstack/react-router\`. It provides:
2813
+ Document head management is the process of managing the head, title, meta, link, and script tags of a document and TanStack Router provides a robust way to manage the document head for full-stack applications that use Start and for single-page applications that use TanStack Router. It provides:
2163
2814
 
2164
2815
  - Automatic deduping of \`title\` and \`meta\` tags
2165
2816
  - Automatic loading/unloading of tags based on route visibility
2166
2817
  - A composable way to merge \`title\` and \`meta\` tags from nested routes
2167
2818
 
2168
- For full-stack applications that use Start, and even for single-page applications that use \`@tanstack/react-router\`, managing the document head is a crucial part of any application for the following reasons:
2819
+ For full-stack applications that use Start, and even for single-page applications that use TanStack Router, managing the document head is a crucial part of any application for the following reasons:
2169
2820
 
2170
2821
  - SEO
2171
2822
  - Social media sharing
@@ -2227,6 +2878,10 @@ It should be **rendered either in the \`<head>\` tag of your root layout or as h
2227
2878
 
2228
2879
  ### Start/Full-Stack Applications
2229
2880
 
2881
+ <!-- ::start:framework -->
2882
+
2883
+ # React
2884
+
2230
2885
  \`\`\`tsx
2231
2886
  import { HeadContent } from '@tanstack/react-router'
2232
2887
 
@@ -2244,10 +2899,35 @@ export const Route = createRootRoute({
2244
2899
  })
2245
2900
  \`\`\`
2246
2901
 
2902
+ # Solid
2903
+
2904
+ \`\`\`tsx
2905
+ import { HeadContent } from '@tanstack/solid-router'
2906
+
2907
+ export const Route = createRootRoute({
2908
+ component: () => (
2909
+ <html>
2910
+ <head>
2911
+ <HeadContent />
2912
+ </head>
2913
+ <body>
2914
+ <Outlet />
2915
+ </body>
2916
+ </html>
2917
+ ),
2918
+ })
2919
+ \`\`\`
2920
+
2921
+ <!-- ::end:framework -->
2922
+
2247
2923
  ### Single-Page Applications
2248
2924
 
2249
2925
  First, remove the \`<title>\` tag from the index.html if you have set any.
2250
2926
 
2927
+ <!-- ::start:framework -->
2928
+
2929
+ # React
2930
+
2251
2931
  \`\`\`tsx
2252
2932
  import { HeadContent } from '@tanstack/react-router'
2253
2933
 
@@ -2261,6 +2941,23 @@ const rootRoute = createRootRoute({
2261
2941
  })
2262
2942
  \`\`\`
2263
2943
 
2944
+ # Solid
2945
+
2946
+ \`\`\`tsx
2947
+ import { HeadContent } from '@tanstack/solid-router'
2948
+
2949
+ const rootRoute = createRootRoute({
2950
+ component: () => (
2951
+ <>
2952
+ <HeadContent />
2953
+ <Outlet />
2954
+ </>
2955
+ ),
2956
+ })
2957
+ \`\`\`
2958
+
2959
+ <!-- ::end:framework -->
2960
+
2264
2961
  ## Managing Body Scripts
2265
2962
 
2266
2963
  In addition to scripts that can be rendered in the \`<head>\` tag, you can also render scripts in the \`<body>\` tag using the \`routeOptions.scripts\` property. This is useful for loading scripts (even inline scripts) that require the DOM to be loaded, but before the main entry point of your application (which includes hydration if you're using Start or a full-stack implementation of TanStack Router).
@@ -2286,9 +2983,13 @@ The \`<Scripts />\` component is **required** to render the body scripts of a do
2286
2983
 
2287
2984
  ### Example
2288
2985
 
2986
+ <!-- ::start:framework -->
2987
+
2988
+ # React
2989
+
2289
2990
  \`\`\`tsx
2290
- import { createFileRoute, Scripts } from '@tanstack/react-router'
2291
- export const Router = createFileRoute('/')({
2991
+ import { createRootRoute, Scripts } from '@tanstack/react-router'
2992
+ export const Route = createFileRoute('/')({
2292
2993
  component: () => (
2293
2994
  <html>
2294
2995
  <head />
@@ -2301,23 +3002,33 @@ export const Router = createFileRoute('/')({
2301
3002
  })
2302
3003
  \`\`\`
2303
3004
 
2304
- \`\`\`tsx
2305
- import { Scripts, createRootRoute } from '@tanstack/react-router'
3005
+ # Solid
2306
3006
 
2307
- export const Route = createRootRoute({
3007
+ \`\`\`tsx
3008
+ import { createFileRoute, Scripts } from '@tanstack/solid-router'
3009
+ export const Route = createRootRoute('/')({
2308
3010
  component: () => (
2309
- <>
2310
- <Outlet />
2311
- <Scripts />
2312
- </>
3011
+ <html>
3012
+ <head />
3013
+ <body>
3014
+ <Outlet />
3015
+ <Scripts />
3016
+ </body>
3017
+ </html>
2313
3018
  ),
2314
3019
  })
2315
3020
  \`\`\`
2316
3021
 
3022
+ <!-- ::end:framework -->
3023
+
2317
3024
  ## Inline Scripts with ScriptOnce
2318
3025
 
2319
3026
  For scripts that must run before React hydrates (like theme detection), use \`ScriptOnce\`. This is particularly useful for avoiding flash of unstyled content (FOUC) or theme flicker.
2320
3027
 
3028
+ <!-- ::start:framework -->
3029
+
3030
+ # React
3031
+
2321
3032
  \`\`\`tsx
2322
3033
  import { ScriptOnce } from '@tanstack/react-router'
2323
3034
 
@@ -2341,6 +3052,33 @@ function ThemeProvider({ children }) {
2341
3052
  }
2342
3053
  \`\`\`
2343
3054
 
3055
+ # Solid
3056
+
3057
+ \`\`\`tsx
3058
+ import { ScriptOnce } from '@tanstack/solid-router'
3059
+
3060
+ const themeScript = \`(function() {
3061
+ try {
3062
+ const theme = localStorage.getItem('theme') || 'auto';
3063
+ const resolved = theme === 'auto'
3064
+ ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
3065
+ : theme;
3066
+ document.documentElement.classList.add(resolved);
3067
+ } catch (e) {}
3068
+ })();\`
3069
+
3070
+ function ThemeProvider({ children }) {
3071
+ return (
3072
+ <>
3073
+ <ScriptOnce children={themeScript} />
3074
+ {children}
3075
+ </>
3076
+ )
3077
+ }
3078
+ \`\`\`
3079
+
3080
+ <!-- ::end:framework -->
3081
+
2344
3082
  ### How ScriptOnce Works
2345
3083
 
2346
3084
  1. During SSR, renders a \`<script>\` tag with the provided code
@@ -2348,6 +3086,10 @@ function ThemeProvider({ children }) {
2348
3086
  3. After execution, the script removes itself from the DOM
2349
3087
  4. On client-side navigation, nothing is rendered (prevents duplicate execution)
2350
3088
 
3089
+ <!-- ::start:framework -->
3090
+
3091
+ # React
3092
+
2351
3093
  ### Preventing Hydration Warnings
2352
3094
 
2353
3095
  If your script modifies the DOM before hydration (like adding a class to \`<html>\`), use \`suppressHydrationWarning\` to prevent React warnings:
@@ -2370,6 +3112,8 @@ export const Route = createRootRoute({
2370
3112
  })
2371
3113
  \`\`\`
2372
3114
 
3115
+ <!-- ::end:framework -->
3116
+
2373
3117
  ### Common Use Cases
2374
3118
 
2375
3119
  - **Theme/dark mode detection** - Apply theme class before hydration to prevent flash
@@ -2575,6 +3319,10 @@ If you don't create a history instance, a browser-oriented instance of this API
2575
3319
 
2576
3320
  Once you have a history instance, you can pass it to the \`Router\` constructor:
2577
3321
 
3322
+ <!-- ::start:framework -->
3323
+
3324
+ # React
3325
+
2578
3326
  \`\`\`ts
2579
3327
  import { createMemoryHistory, createRouter } from '@tanstack/react-router'
2580
3328
 
@@ -2585,6 +3333,20 @@ const memoryHistory = createMemoryHistory({
2585
3333
  const router = createRouter({ routeTree, history: memoryHistory })
2586
3334
  \`\`\`
2587
3335
 
3336
+ # Solid
3337
+
3338
+ \`\`\`ts
3339
+ import { createMemoryHistory, createRouter } from '@tanstack/solid-router'
3340
+
3341
+ const memoryHistory = createMemoryHistory({
3342
+ initialEntries: ['/'], // Pass your initial url
3343
+ })
3344
+
3345
+ const router = createRouter({ routeTree, history: memoryHistory })
3346
+ \`\`\`
3347
+
3348
+ <!-- ::end:framework -->
3349
+
2588
3350
  ## Browser Routing
2589
3351
 
2590
3352
  The \`createBrowserHistory\` is the default history type. It uses the browser's history API to manage the browser history.
@@ -2593,6 +3355,10 @@ The \`createBrowserHistory\` is the default history type. It uses the browser's
2593
3355
 
2594
3356
  Hash routing can be helpful if your server doesn't support rewrites to index.html for HTTP requests (among other environments that don't have a server).
2595
3357
 
3358
+ <!-- ::start:framework -->
3359
+
3360
+ # React
3361
+
2596
3362
  \`\`\`ts
2597
3363
  import { createHashHistory, createRouter } from '@tanstack/react-router'
2598
3364
 
@@ -2601,10 +3367,26 @@ const hashHistory = createHashHistory()
2601
3367
  const router = createRouter({ routeTree, history: hashHistory })
2602
3368
  \`\`\`
2603
3369
 
3370
+ # Solid
3371
+
3372
+ \`\`\`ts
3373
+ import { createHashHistory, createRouter } from '@tanstack/solid-router'
3374
+
3375
+ const hashHistory = createHashHistory()
3376
+
3377
+ const router = createRouter({ routeTree, history: hashHistory })
3378
+ \`\`\`
3379
+
3380
+ <!-- ::end:framework -->
3381
+
2604
3382
  ## Memory Routing
2605
3383
 
2606
3384
  Memory routing is useful in environments that are not a browser or when you do not want components to interact with the URL.
2607
3385
 
3386
+ <!-- ::start:framework -->
3387
+
3388
+ # React
3389
+
2608
3390
  \`\`\`ts
2609
3391
  import { createMemoryHistory, createRouter } from '@tanstack/react-router'
2610
3392
 
@@ -2615,7 +3397,21 @@ const memoryHistory = createMemoryHistory({
2615
3397
  const router = createRouter({ routeTree, history: memoryHistory })
2616
3398
  \`\`\`
2617
3399
 
2618
- Refer to the [SSR Guide](./ssr.md#server-history) for usage on the server for server-side rendering.
3400
+ # Solid
3401
+
3402
+ \`\`\`ts
3403
+ import { createMemoryHistory, createRouter } from '@tanstack/solid-router'
3404
+
3405
+ const memoryHistory = createMemoryHistory({
3406
+ initialEntries: ['/'], // Pass your initial url
3407
+ })
3408
+
3409
+ const router = createRouter({ routeTree, history: memoryHistory })
3410
+ \`\`\`
3411
+
3412
+ <!-- ::end:framework -->
3413
+
3414
+ Refer to the [SSR Guide](./ssr.md#automatic-server-history) for usage on the server for server-side rendering.
2619
3415
 
2620
3416
  # Internationalization (i18n)
2621
3417
 
@@ -3015,7 +3811,9 @@ There are 2 ways to use navigation blocking:
3015
3811
 
3016
3812
  Let's imagine we want to prevent navigation if a form is dirty. We can do this by using the \`useBlocker\` hook:
3017
3813
 
3018
- [//]: # 'HookBasedBlockingExample'
3814
+ <!-- ::start:framework -->
3815
+
3816
+ # React
3019
3817
 
3020
3818
  \`\`\`tsx
3021
3819
  import { useBlocker } from '@tanstack/react-router'
@@ -3036,10 +3834,35 @@ function MyComponent() {
3036
3834
  }
3037
3835
  \`\`\`
3038
3836
 
3039
- [//]: # 'HookBasedBlockingExample'
3837
+ # Solid
3838
+
3839
+ \`\`\`tsx
3840
+ import { useBlocker } from '@tanstack/solid-router'
3841
+
3842
+ function MyComponent() {
3843
+ const [formIsDirty, setFormIsDirty] = createSignal(false)
3844
+
3845
+ useBlocker({
3846
+ shouldBlockFn: () => {
3847
+ if (!formIsDirty()) return false
3848
+
3849
+ const shouldLeave = confirm('Are you sure you want to leave?')
3850
+ return !shouldLeave
3851
+ },
3852
+ })
3853
+
3854
+ // ...
3855
+ }
3856
+ \`\`\`
3857
+
3858
+ <!-- ::end:framework -->
3040
3859
 
3041
3860
  \`shouldBlockFn\` gives you type safe access to the \`current\` and \`next\` location:
3042
3861
 
3862
+ <!-- ::start:framework -->
3863
+
3864
+ # React
3865
+
3043
3866
  \`\`\`tsx
3044
3867
  import { useBlocker } from '@tanstack/react-router'
3045
3868
 
@@ -3061,9 +3884,36 @@ function MyComponent() {
3061
3884
  }
3062
3885
  \`\`\`
3063
3886
 
3887
+ # Solid
3888
+
3889
+ \`\`\`tsx
3890
+ import { useBlocker } from '@tanstack/solid-router'
3891
+
3892
+ function MyComponent() {
3893
+ // always block going from /foo to /bar/123?hello=world
3894
+ const { proceed, reset, status } = useBlocker({
3895
+ shouldBlockFn: ({ current, next }) => {
3896
+ return (
3897
+ current.routeId === '/foo' &&
3898
+ next.fullPath === '/bar/$id' &&
3899
+ next.params.id === 123 &&
3900
+ next.search.hello === 'world'
3901
+ )
3902
+ },
3903
+ withResolver: true,
3904
+ })
3905
+
3906
+ // ...
3907
+ }
3908
+ \`\`\`
3909
+
3910
+ <!-- ::end:framework -->
3911
+
3064
3912
  Note that even if \`shouldBlockFn\` returns \`false\`, the browser's \`beforeunload\` event may still be triggered on page reloads or tab closing. To gain control over this, you can use the \`enableBeforeUnload\` option to conditionally register the \`beforeunload\` handler:
3065
3913
 
3066
- [//]: # 'HookBasedBlockingExample'
3914
+ <!-- ::start:framework -->
3915
+
3916
+ # React
3067
3917
 
3068
3918
  \`\`\`tsx
3069
3919
  import { useBlocker } from '@tanstack/react-router'
@@ -3080,16 +3930,37 @@ function MyComponent() {
3080
3930
  }
3081
3931
  \`\`\`
3082
3932
 
3933
+ # Solid
3934
+
3935
+ \`\`\`tsx
3936
+ import { useBlocker } from '@tanstack/solid-router'
3937
+
3938
+ function MyComponent() {
3939
+ const [formIsDirty, setFormIsDirty] = useState(false)
3940
+
3941
+ useBlocker({
3942
+ {/* ... */}
3943
+ enableBeforeUnload: formIsDirty(),
3944
+ })
3945
+
3946
+ // ...
3947
+ }
3948
+ \`\`\`
3949
+
3950
+ <!-- ::end:framework -->
3951
+
3083
3952
  You can find more information about the \`useBlocker\` hook in the [API reference](../api/router/useBlockerHook.md).
3084
3953
 
3085
3954
  ## Component-based blocking
3086
3955
 
3087
3956
  In addition to logical/hook based blocking, you can use the \`Block\` component to achieve similar results:
3088
3957
 
3089
- [//]: # 'ComponentBasedBlockingExample'
3958
+ <!-- ::start:framework -->
3959
+
3960
+ # React
3090
3961
 
3091
3962
  \`\`\`tsx
3092
- import { Block } from '@tanstack/react-router'
3963
+ import { Block } from '@tanstack/solid-router'
3093
3964
 
3094
3965
  function MyComponent() {
3095
3966
  const [formIsDirty, setFormIsDirty] = useState(false)
@@ -3120,21 +3991,52 @@ function MyComponent() {
3120
3991
  }
3121
3992
  \`\`\`
3122
3993
 
3123
- [//]: # 'ComponentBasedBlockingExample'
3994
+ # Solid
3124
3995
 
3125
- ## How can I show a custom UI?
3996
+ \`\`\`tsx
3997
+ import { Block } from '@tanstack/solid-router'
3126
3998
 
3127
- In most cases, using \`window.confirm\` in the \`shouldBlockFn\` function with \`withResolver: false\` in the hook is enough since it will clearly show the user that the navigation is being blocked and resolve the blocking based on their response.
3999
+ function MyComponent() {
4000
+ const [formIsDirty, setFormIsDirty] = createSignal(false)
3128
4001
 
3129
- However, in some situations, you might want to show a custom UI that is intentionally less disruptive and more integrated with your app's design.
4002
+ return (
4003
+ <Block
4004
+ shouldBlockFn={() => {
4005
+ if (!formIsDirty()) return false
3130
4006
 
3131
- **Note:** The return value of \`shouldBlockFn\` does not resolve the blocking if \`withResolver\` is \`true\`.
4007
+ const shouldLeave = confirm('Are you sure you want to leave?')
4008
+ return !shouldLeave
4009
+ }}
4010
+ />
4011
+ )
3132
4012
 
3133
- ### Hook/logical-based custom UI with resolver
4013
+ // OR
3134
4014
 
3135
- [//]: # 'HookBasedCustomUIBlockingWithResolverExample'
4015
+ return (
4016
+ <Block shouldBlockFn={() => !formIsDirty} withResolver>
4017
+ {({ status, proceed, reset }) => <>{/* ... */}</>}
4018
+ </Block>
4019
+ )
4020
+ }
4021
+ \`\`\`
3136
4022
 
3137
- \`\`\`tsx
4023
+ <!-- ::end:framework -->
4024
+
4025
+ ## How can I show a custom UI?
4026
+
4027
+ In most cases, using \`window.confirm\` in the \`shouldBlockFn\` function with \`withResolver: false\` in the hook is enough since it will clearly show the user that the navigation is being blocked and resolve the blocking based on their response.
4028
+
4029
+ However, in some situations, you might want to show a custom UI that is intentionally less disruptive and more integrated with your app's design.
4030
+
4031
+ **Note:** The return value of \`shouldBlockFn\` does not resolve the blocking if \`withResolver\` is \`true\`.
4032
+
4033
+ ### Hook/logical-based custom UI with resolver
4034
+
4035
+ <!-- ::start:framework -->
4036
+
4037
+ # React
4038
+
4039
+ \`\`\`tsx
3138
4040
  import { useBlocker } from '@tanstack/react-router'
3139
4041
 
3140
4042
  function MyComponent() {
@@ -3161,11 +4063,42 @@ function MyComponent() {
3161
4063
  }
3162
4064
  \`\`\`
3163
4065
 
3164
- [//]: # 'HookBasedCustomUIBlockingWithResolverExample'
4066
+ # Solid
4067
+
4068
+ \`\`\`tsx
4069
+ import { useBlocker } from '@tanstack/solid-router'
4070
+
4071
+ function MyComponent() {
4072
+ const [formIsDirty, setFormIsDirty] = createSignal(false)
4073
+
4074
+ const { proceed, reset, status } = useBlocker({
4075
+ shouldBlockFn: () => formIsDirty(),
4076
+ withResolver: true,
4077
+ })
4078
+
4079
+ // ...
4080
+
4081
+ return (
4082
+ <>
4083
+ {/* ... */}
4084
+ {status === 'blocked' && (
4085
+ <div>
4086
+ <p>Are you sure you want to leave?</p>
4087
+ <button onClick={proceed}>Yes</button>
4088
+ <button onClick={reset}>No</button>
4089
+ </div>
4090
+ )}
4091
+ </>
4092
+ }
4093
+ \`\`\`
4094
+
4095
+ <!-- ::end:framework -->
3165
4096
 
3166
4097
  ### Hook/logical-based custom UI without resolver
3167
4098
 
3168
- [//]: # 'HookBasedCustomUIBlockingWithoutResolverExample'
4099
+ <!-- ::start:framework -->
4100
+
4101
+ # React
3169
4102
 
3170
4103
  \`\`\`tsx
3171
4104
  import { useBlocker } from '@tanstack/react-router'
@@ -3206,13 +4139,56 @@ function MyComponent() {
3206
4139
  }
3207
4140
  \`\`\`
3208
4141
 
3209
- [//]: # 'HookBasedCustomUIBlockingWithoutResolverExample'
4142
+ # Solid
4143
+
4144
+ \`\`\`tsx
4145
+ import { useBlocker } from '@tanstack/solid-router'
4146
+
4147
+ function MyComponent() {
4148
+ const [formIsDirty, setFormIsDirty] = createSignal(false)
4149
+
4150
+ useBlocker({
4151
+ shouldBlockFn: () => {
4152
+ if (!formIsDirty()) {
4153
+ return false
4154
+ }
4155
+
4156
+ const shouldBlock = new Promise<boolean>((resolve) => {
4157
+ // Using a modal manager of your choice
4158
+ modals.open({
4159
+ title: 'Are you sure you want to leave?',
4160
+ children: (
4161
+ <SaveBlocker
4162
+ confirm={() => {
4163
+ modals.closeAll()
4164
+ resolve(false)
4165
+ }}
4166
+ reject={() => {
4167
+ modals.closeAll()
4168
+ resolve(true)
4169
+ }}
4170
+ />
4171
+ ),
4172
+ onClose: () => resolve(true),
4173
+ })
4174
+ })
4175
+ return shouldBlock
4176
+ },
4177
+ })
4178
+
4179
+ // ...
4180
+ }
4181
+ \`\`\`
4182
+
4183
+ <!-- ::end:framework -->
3210
4184
 
3211
4185
  ### Component-based custom UI
3212
4186
 
3213
4187
  Similarly to the hook, the \`Block\` component returns the same state and functions as render props:
3214
4188
 
3215
- [//]: # 'ComponentBasedCustomUIBlockingExample'
4189
+ <!-- ::start:framework -->
4190
+
4191
+ # React
3216
4192
 
3217
4193
  \`\`\`tsx
3218
4194
  import { Block } from '@tanstack/react-router'
@@ -3239,7 +4215,34 @@ function MyComponent() {
3239
4215
  }
3240
4216
  \`\`\`
3241
4217
 
3242
- [//]: # 'ComponentBasedCustomUIBlockingExample'
4218
+ # Solid
4219
+
4220
+ \`\`\`tsx
4221
+ import { Block } from '@tanstack/solid-router'
4222
+
4223
+ function MyComponent() {
4224
+ const [formIsDirty, setFormIsDirty] = createSignal(false)
4225
+
4226
+ return (
4227
+ <Block shouldBlockFn={() => formIsDirty()} withResolver>
4228
+ {({ status, proceed, reset }) => (
4229
+ <>
4230
+ {/* ... */}
4231
+ {status === 'blocked' && (
4232
+ <div>
4233
+ <p>Are you sure you want to leave?</p>
4234
+ <button onClick={proceed}>Yes</button>
4235
+ <button onClick={reset}>No</button>
4236
+ </div>
4237
+ )}
4238
+ </>
4239
+ )}
4240
+ </Block>
4241
+ )
4242
+ }
4243
+ \`\`\`
4244
+
4245
+ <!-- ::end:framework -->
3243
4246
 
3244
4247
  # Navigation
3245
4248
 
@@ -3395,12 +4398,26 @@ export type LinkProps<
3395
4398
 
3396
4399
  Let's make a simple static link!
3397
4400
 
4401
+ <!-- ::start:framework -->
4402
+
4403
+ # React
4404
+
3398
4405
  \`\`\`tsx
3399
4406
  import { Link } from '@tanstack/react-router'
3400
4407
 
3401
4408
  const link = <Link to="/about">About</Link>
3402
4409
  \`\`\`
3403
4410
 
4411
+ # Solid
4412
+
4413
+ \`\`\`tsx
4414
+ import { Link } from '@tanstack/solid-router'
4415
+
4416
+ const link = <Link to="/about">About</Link>
4417
+ \`\`\`
4418
+
4419
+ <!-- ::end:framework -->
4420
+
3404
4421
  ### Dynamic Links
3405
4422
 
3406
4423
  Dynamic links are links that have dynamic segments in them. For example, a link to a blog post might look like this:
@@ -4254,8 +5271,6 @@ export const Route = createFileRoute('/_pathless/route-a')({
4254
5271
  You can also target the root route by passing the exported \`rootRouteId\` variable to the \`notFound\` function's \`route\` property:
4255
5272
 
4256
5273
  \`\`\`tsx
4257
- import { rootRouteId } from '@tanstack/react-router'
4258
-
4259
5274
  export const Route = createFileRoute('/posts/$postId')({
4260
5275
  loader: async ({ params: { postId } }) => {
4261
5276
  const post = await getPost(postId)
@@ -4320,8 +5335,7 @@ The main differences are:
4320
5335
 
4321
5336
  To migrate from \`NotFoundRoute\` to \`notFoundComponent\`, you'll just need to make a few changes:
4322
5337
 
4323
- \`\`\`tsx
4324
- // router.tsx
5338
+ \`\`\`tsx title='src/router.tsx'
4325
5339
  import { createRouter } from '@tanstack/react-router'
4326
5340
  import { routeTree } from './routeTree.gen.'
4327
5341
  - import { notFoundRoute } from './notFoundRoute' // [!code --]
@@ -4361,6 +5375,10 @@ The \`Outlet\` component is used to render the next potentially matching child r
4361
5375
 
4362
5376
  A great example is configuring the root route of your application. Let's give our root route a component that renders a title, then an \`<Outlet />\` for our top-level routes to render.
4363
5377
 
5378
+ <!-- ::start:framework -->
5379
+
5380
+ # React
5381
+
4364
5382
  \`\`\`tsx
4365
5383
  import { createRootRoute, Outlet } from '@tanstack/react-router'
4366
5384
 
@@ -4378,6 +5396,27 @@ function RootComponent() {
4378
5396
  }
4379
5397
  \`\`\`
4380
5398
 
5399
+ # Solid
5400
+
5401
+ \`\`\`tsx
5402
+ import { createRootRoute, Outlet } from '@tanstack/solid-router'
5403
+
5404
+ export const Route = createRootRoute({
5405
+ component: RootComponent,
5406
+ })
5407
+
5408
+ function RootComponent() {
5409
+ return (
5410
+ <div>
5411
+ <h1>My App</h1>
5412
+ <Outlet /> {/* This is where child routes will render */}
5413
+ </div>
5414
+ )
5415
+ }
5416
+ \`\`\`
5417
+
5418
+ <!-- ::end:framework -->
5419
+
4381
5420
  # Parallel Routes
4382
5421
 
4383
5422
  We haven't covered this yet. Stay tuned!
@@ -4397,9 +5436,11 @@ Because path param routes only match to the next \`/\`, child routes can be crea
4397
5436
 
4398
5437
  Let's create a post route file that uses a path param to match the post ID:
4399
5438
 
4400
- - \`posts.$postId.tsx\`
5439
+ <!-- ::start:framework -->
4401
5440
 
4402
- \`\`\`tsx
5441
+ # React
5442
+
5443
+ \`\`\`tsx title="src/routes/posts.$postId.tsx"
4403
5444
  import { createFileRoute } from '@tanstack/react-router'
4404
5445
 
4405
5446
  export const Route = createFileRoute('/posts/$postId')({
@@ -4409,6 +5450,20 @@ export const Route = createFileRoute('/posts/$postId')({
4409
5450
  })
4410
5451
  \`\`\`
4411
5452
 
5453
+ # Solid
5454
+
5455
+ \`\`\`tsx title="src/routes/posts.$postId.tsx"
5456
+ import { createFileRoute } from '@tanstack/solid-router'
5457
+
5458
+ export const Route = createFileRoute('/posts/$postId')({
5459
+ loader: async ({ params }) => {
5460
+ return fetchPost(params.postId)
5461
+ },
5462
+ })
5463
+ \`\`\`
5464
+
5465
+ <!-- ::end:framework -->
5466
+
4412
5467
  ## Path Params can be used by child routes
4413
5468
 
4414
5469
  Once a path param has been parsed, it is available to all child routes. This means that if we define a child route to our \`postRoute\`, we can use the \`postId\` variable from the URL in the child route's path!
@@ -4417,7 +5472,7 @@ Once a path param has been parsed, it is available to all child routes. This mea
4417
5472
 
4418
5473
  Path params are passed to the loader as a \`params\` object. The keys of this object are the names of the path params, and the values are the values that were parsed out of the actual URL path. For example, if we were to visit the \`/blog/123\` URL, the \`params\` object would be \`{ postId: '123' }\`:
4419
5474
 
4420
- \`\`\`tsx
5475
+ \`\`\`tsx title="src/routes/posts.$postId.tsx"
4421
5476
  export const Route = createFileRoute('/posts/$postId')({
4422
5477
  loader: async ({ params }) => {
4423
5478
  return fetchPost(params.postId)
@@ -4427,7 +5482,7 @@ export const Route = createFileRoute('/posts/$postId')({
4427
5482
 
4428
5483
  The \`params\` object is also passed to the \`beforeLoad\` option:
4429
5484
 
4430
- \`\`\`tsx
5485
+ \`\`\`tsx title="src/routes/posts.$postId.tsx"
4431
5486
  export const Route = createFileRoute('/posts/$postId')({
4432
5487
  beforeLoad: async ({ params }) => {
4433
5488
  // do something with params.postId
@@ -4439,7 +5494,11 @@ export const Route = createFileRoute('/posts/$postId')({
4439
5494
 
4440
5495
  If we add a component to our \`postRoute\`, we can access the \`postId\` variable from the URL by using the route's \`useParams\` hook:
4441
5496
 
4442
- \`\`\`tsx
5497
+ <!-- ::start:framework -->
5498
+
5499
+ # React
5500
+
5501
+ \`\`\`tsx title="src/routes/posts.$postId.tsx"
4443
5502
  export const Route = createFileRoute('/posts/$postId')({
4444
5503
  component: PostComponent,
4445
5504
  })
@@ -4450,19 +5509,49 @@ function PostComponent() {
4450
5509
  }
4451
5510
  \`\`\`
4452
5511
 
5512
+ # Solid
5513
+
5514
+ \`\`\`tsx title="src/routes/posts.$postId.tsx"
5515
+ export const Route = createFileRoute('/posts/$postId')({
5516
+ component: PostComponent,
5517
+ })
5518
+
5519
+ function PostComponent() {
5520
+ const params = Route.useParams()
5521
+ return <div>Post {params().postId}</div>
5522
+ }
5523
+ \`\`\`
5524
+
5525
+ <!-- ::end:framework -->
5526
+
4453
5527
  > 🧠 Quick tip: If your component is code-split, you can use the [getRouteApi function](./code-splitting.md#manually-accessing-route-apis-in-other-files-with-the-getrouteapi-helper) to avoid having to import the \`Route\` configuration to get access to the typed \`useParams()\` hook.
4454
5528
 
4455
5529
  ## Path Params outside of Routes
4456
5530
 
4457
5531
  You can also use the globally exported \`useParams\` hook to access any parsed path params from any component in your app. You'll need to pass the \`strict: false\` option to \`useParams\`, denoting that you want to access the params from an ambiguous location:
4458
5532
 
4459
- \`\`\`tsx
5533
+ <!-- ::start:framework -->
5534
+
5535
+ # React
5536
+
5537
+ \`\`\`tsx title="src/components/PostComponent.tsx"
4460
5538
  function PostComponent() {
4461
5539
  const { postId } = useParams({ strict: false })
4462
5540
  return <div>Post {postId}</div>
4463
5541
  }
4464
5542
  \`\`\`
4465
5543
 
5544
+ # Solid
5545
+
5546
+ \`\`\`tsx title="src/components/PostComponent.tsx"
5547
+ function PostComponent() {
5548
+ const params = useParams({ strict: false })
5549
+ return <div>Post {params().postId}</div>
5550
+ }
5551
+ \`\`\`
5552
+
5553
+ <!-- ::end:framework -->
5554
+
4466
5555
  ## Navigating with Path Params
4467
5556
 
4468
5557
  When navigating to a route with path params, TypeScript will require you to pass the params either as an object or as a function that returns an object of params.
@@ -4503,8 +5592,11 @@ When using either prefixes or suffixes, you can define them by wrapping the path
4503
5592
 
4504
5593
  Prefixes are defined by placing the prefix text outside the curly braces before the variable name. For example, if you want to match a URL that starts with \`post-\` followed by a post ID, you can define it like this:
4505
5594
 
4506
- \`\`\`tsx
4507
- // src/routes/posts/post-{$postId}.tsx
5595
+ <!-- ::start:framework -->
5596
+
5597
+ # React
5598
+
5599
+ \`\`\`tsx title="src/routes/posts/post-{$postId}.tsx"
4508
5600
  export const Route = createFileRoute('/posts/post-{$postId}')({
4509
5601
  component: PostComponent,
4510
5602
  })
@@ -4516,10 +5608,29 @@ function PostComponent() {
4516
5608
  }
4517
5609
  \`\`\`
4518
5610
 
5611
+ # Solid
5612
+
5613
+ \`\`\`tsx title="src/routes/posts/post-{$postId}.tsx"
5614
+ export const Route = createFileRoute('/posts/post-{$postId}')({
5615
+ component: PostComponent,
5616
+ })
5617
+
5618
+ function PostComponent() {
5619
+ const params = Route.useParams()
5620
+ // postId will be the value after 'post-'
5621
+ return <div>Post ID: {params().postId}</div>
5622
+ }
5623
+ \`\`\`
5624
+
5625
+ <!-- ::end:framework -->
5626
+
4519
5627
  You can even combines prefixes with wildcard routes to create more complex patterns:
4520
5628
 
4521
- \`\`\`tsx
4522
- // src/routes/on-disk/storage-{$}
5629
+ <!-- ::start:framework -->
5630
+
5631
+ # React
5632
+
5633
+ \`\`\`tsx title="src/routes/on-disk/storage-{$postId}/$.tsx"
4523
5634
  export const Route = createFileRoute('/on-disk/storage-{$postId}/$')({
4524
5635
  component: StorageComponent,
4525
5636
  })
@@ -4532,12 +5643,32 @@ function StorageComponent() {
4532
5643
  }
4533
5644
  \`\`\`
4534
5645
 
5646
+ # Solid
5647
+
5648
+ \`\`\`tsx title="src/routes/on-disk/storage-{$postId}/$.tsx"
5649
+ export const Route = createFileRoute('/on-disk/storage-{$postId}/$')({
5650
+ component: StorageComponent,
5651
+ })
5652
+
5653
+ function StorageComponent() {
5654
+ const params = Route.useParams()
5655
+ // _splat, will be value after 'storage-'
5656
+ // i.e. my-drive/documents/foo.txt
5657
+ return <div>Storage Location: /{params()._splat}</div>
5658
+ }
5659
+ \`\`\`
5660
+
5661
+ <!-- ::end:framework -->
5662
+
4535
5663
  ### Defining Suffixes
4536
5664
 
4537
5665
  Suffixes are defined by placing the suffix text outside the curly braces after the variable name. For example, if you want to match a URL a filename that ends with \`txt\`, you can define it like this:
4538
5666
 
4539
- \`\`\`tsx
4540
- // src/routes/files/{$fileName}txt
5667
+ <!-- ::start:framework -->
5668
+
5669
+ # React
5670
+
5671
+ \`\`\`tsx title="src/routes/files/{$fileName}[.]txt.tsx"
4541
5672
  export const Route = createFileRoute('/files/{$fileName}.txt')({
4542
5673
  component: FileComponent,
4543
5674
  })
@@ -4549,10 +5680,29 @@ function FileComponent() {
4549
5680
  }
4550
5681
  \`\`\`
4551
5682
 
5683
+ # Solid
5684
+
5685
+ \`\`\`tsx title="src/routes/files/{$fileName}[.]txt.tsx"
5686
+ export const Route = createFileRoute('/files/{$fileName}.txt')({
5687
+ component: FileComponent,
5688
+ })
5689
+
5690
+ function FileComponent() {
5691
+ const params = Route.useParams()
5692
+ // fileName will be the value before 'txt'
5693
+ return <div>File Name: {params().fileName}</div>
5694
+ }
5695
+ \`\`\`
5696
+
5697
+ <!-- ::end:framework -->
5698
+
4552
5699
  You can also combine suffixes with wildcards for more complex routing patterns:
4553
5700
 
4554
- \`\`\`tsx
4555
- // src/routes/files/{$}[.]txt
5701
+ <!-- ::start:framework -->
5702
+
5703
+ # React
5704
+
5705
+ \`\`\`tsx title="src/routes/files/{$}[.]txt.tsx"
4556
5706
  export const Route = createFileRoute('/files/{$fileName}[.]txt')({
4557
5707
  component: FileComponent,
4558
5708
  })
@@ -4564,12 +5714,31 @@ function FileComponent() {
4564
5714
  }
4565
5715
  \`\`\`
4566
5716
 
5717
+ # Solid
5718
+
5719
+ \`\`\`tsx title="src/routes/files/{$}[.]txt.tsx"
5720
+ export const Route = createFileRoute('/files/{$fileName}[.]txt')({
5721
+ component: FileComponent,
5722
+ })
5723
+
5724
+ function FileComponent() {
5725
+ const params = Route.useParams()
5726
+ // _splat will be the value before '.txt'
5727
+ return <div>File Splat: {params()._splat}</div>
5728
+ }
5729
+ \`\`\`
5730
+
5731
+ <!-- ::end:framework -->
5732
+
4567
5733
  ### Combining Prefixes and Suffixes
4568
5734
 
4569
5735
  You can combine both prefixes and suffixes to create very specific routing patterns. For example, if you want to match a URL that starts with \`user-\` and ends with \`.json\`, you can define it like this:
4570
5736
 
4571
- \`\`\`tsx
4572
- // src/routes/users/user-{$userId}.json
5737
+ <!-- ::start:framework -->
5738
+
5739
+ # React
5740
+
5741
+ \`\`\`tsx title="src/routes/users/user-{$userId}.json"
4573
5742
  export const Route = createFileRoute('/users/user-{$userId}.json')({
4574
5743
  component: UserComponent,
4575
5744
  })
@@ -4581,6 +5750,22 @@ function UserComponent() {
4581
5750
  }
4582
5751
  \`\`\`
4583
5752
 
5753
+ # Solid
5754
+
5755
+ \`\`\`tsx title="src/routes/users/user-{$userId}.json"
5756
+ export const Route = createFileRoute('/users/user-{$userId}.json')({
5757
+ component: UserComponent,
5758
+ })
5759
+
5760
+ function UserComponent() {
5761
+ const params = Route.useParams()
5762
+ // userId will be the value between 'user-' and '.json'
5763
+ return <div>User ID: {params().userId}</div>
5764
+ }
5765
+ \`\`\`
5766
+
5767
+ <!-- ::end:framework -->
5768
+
4584
5769
  Similar to the previous examples, you can also use wildcards with prefixes and suffixes. Go wild!
4585
5770
 
4586
5771
  ## Optional Path Parameters
@@ -4625,7 +5810,11 @@ When an optional parameter is not present in the URL, its value will be \`undefi
4625
5810
 
4626
5811
  Optional parameters work exactly like regular parameters in your components, but their values may be \`undefined\`:
4627
5812
 
4628
- \`\`\`tsx
5813
+ <!-- ::start:framework -->
5814
+
5815
+ # React
5816
+
5817
+ \`\`\`tsx title="src/routes/posts/{-$category}.tsx"
4629
5818
  function PostsComponent() {
4630
5819
  const { category } = Route.useParams()
4631
5820
 
@@ -4633,13 +5822,29 @@ function PostsComponent() {
4633
5822
  }
4634
5823
  \`\`\`
4635
5824
 
4636
- ### Optional Parameters in Loaders
5825
+ # Solid
4637
5826
 
4638
- Optional parameters are available in loaders and may be \`undefined\`:
5827
+ \`\`\`tsx title="src/routes/posts/{-$category}.tsx"
5828
+ function PostsComponent() {
5829
+ const params = Route.useParams()
4639
5830
 
4640
- \`\`\`tsx
4641
- export const Route = createFileRoute('/posts/{-$category}')({
4642
- loader: async ({ params }) => {
5831
+ return (
5832
+ <div>
5833
+ {params().category ? \`Posts in \${params().category}\` : 'All Posts'}
5834
+ </div>
5835
+ )
5836
+ }
5837
+ \`\`\`
5838
+
5839
+ <!-- ::end:framework -->
5840
+
5841
+ ### Optional Parameters in Loaders
5842
+
5843
+ Optional parameters are available in loaders and may be \`undefined\`:
5844
+
5845
+ \`\`\`tsx
5846
+ export const Route = createFileRoute('/posts/{-$category}')({
5847
+ loader: async ({ params }) => {
4643
5848
  // params.category might be undefined
4644
5849
  return fetchPosts({ category: params.category })
4645
5850
  },
@@ -4667,8 +5872,12 @@ export const Route = createFileRoute('/posts/{-$category}')({
4667
5872
 
4668
5873
  Optional parameters support prefix and suffix patterns:
4669
5874
 
4670
- \`\`\`tsx
4671
- // File route: /files/prefix{-$name}.txt
5875
+ <!-- ::start:framework -->
5876
+
5877
+ # React
5878
+
5879
+ \`\`\`tsx title="src/routes/files/prefix{-$name}.txt"
5880
+ // Route: /files/prefix{-$name}.txt
4672
5881
  // Matches: /files/prefix.txt and /files/prefixdocument.txt
4673
5882
  export const Route = createFileRoute('/files/prefix{-$name}.txt')({
4674
5883
  component: FileComponent,
@@ -4680,11 +5889,32 @@ function FileComponent() {
4680
5889
  }
4681
5890
  \`\`\`
4682
5891
 
5892
+ # Solid
5893
+
5894
+ \`\`\`tsx title="src/routes/files/prefix{-$name}.txt"
5895
+ // Route: /files/prefix{-$name}.txt
5896
+ // Matches: /files/prefix.txt and /files/prefixdocument.txt
5897
+ export const Route = createFileRoute('/files/prefix{-$name}.txt')({
5898
+ component: FileComponent,
5899
+ })
5900
+
5901
+ function FileComponent() {
5902
+ const params = Route.useParams()
5903
+ return <div>File: {params().name || 'default'}</div>
5904
+ }
5905
+ \`\`\`
5906
+
5907
+ <!-- ::end:framework -->
5908
+
4683
5909
  #### All Optional Parameters
4684
5910
 
4685
5911
  You can create routes where all parameters are optional:
4686
5912
 
4687
- \`\`\`tsx
5913
+ <!-- ::start:framework -->
5914
+
5915
+ # React
5916
+
5917
+ \`\`\`tsx title="src/routes/{-$year}/{-$month}/{-$day}.tsx"
4688
5918
  // Route: /{-$year}/{-$month}/{-$day}
4689
5919
  // Matches: /, /2023, /2023/12, /2023/12/25
4690
5920
  export const Route = createFileRoute('/{-$year}/{-$month}/{-$day}')({
@@ -4711,11 +5941,46 @@ function DateComponent() {
4711
5941
  }
4712
5942
  \`\`\`
4713
5943
 
5944
+ # Solid
5945
+
5946
+ \`\`\`tsx title="src/routes/{-$year}/{-$month}/{-$day}.tsx"
5947
+ // Route: /{-$year}/{-$month}/{-$day}
5948
+ // Matches: /, /2023, /2023/12, /2023/12/25
5949
+ export const Route = createFileRoute('/{-$year}/{-$month}/{-$day}')({
5950
+ component: DateComponent,
5951
+ })
5952
+
5953
+ function DateComponent() {
5954
+ const params = Route.useParams()
5955
+
5956
+ if (!params().year) return <div>Select a year</div>
5957
+ if (!params().month) return <div>Year: {params().year}</div>
5958
+ if (!params().day)
5959
+ return (
5960
+ <div>
5961
+ Month: {params().year}/{params().month}
5962
+ </div>
5963
+ )
5964
+
5965
+ return (
5966
+ <div>
5967
+ Date: {params().year}/{params().month}/{params().day}
5968
+ </div>
5969
+ )
5970
+ }
5971
+ \`\`\`
5972
+
5973
+ <!-- ::end:framework -->
5974
+
4714
5975
  #### Optional Parameters with Wildcards
4715
5976
 
4716
5977
  Optional parameters can be combined with wildcards for complex routing patterns:
4717
5978
 
4718
- \`\`\`tsx
5979
+ <!-- ::start:framework -->
5980
+
5981
+ # React
5982
+
5983
+ \`\`\`tsx title="src/routes/docs/{-$version}/$.tsx"
4719
5984
  // Route: /docs/{-$version}/$
4720
5985
  // Matches: /docs/extra/path, /docs/v2/extra/path
4721
5986
  export const Route = createFileRoute('/docs/{-$version}/$')({
@@ -4735,6 +6000,29 @@ function DocsComponent() {
4735
6000
  }
4736
6001
  \`\`\`
4737
6002
 
6003
+ # Solid
6004
+
6005
+ \`\`\`tsx title="src/routes/docs/{-$version}/$.tsx"
6006
+ // Route: /docs/{-$version}/$
6007
+ // Matches: /docs/extra/path, /docs/v2/extra/path
6008
+ export const Route = createFileRoute('/docs/{-$version}/$')({
6009
+ component: DocsComponent,
6010
+ })
6011
+
6012
+ function DocsComponent() {
6013
+ const params = Route.useParams()
6014
+
6015
+ return (
6016
+ <div>
6017
+ Version: {params().version || 'latest'}
6018
+ Path: {params()._splat}
6019
+ </div>
6020
+ )
6021
+ }
6022
+ \`\`\`
6023
+
6024
+ <!-- ::end:framework -->
6025
+
4738
6026
  ### Navigating with Optional Parameters
4739
6027
 
4740
6028
  When navigating to routes with optional parameters, you have fine-grained control over which parameters to include:
@@ -4769,7 +6057,11 @@ function Navigation() {
4769
6057
 
4770
6058
  TypeScript provides full type safety for optional parameters:
4771
6059
 
4772
- \`\`\`tsx
6060
+ <!-- ::start:framework -->
6061
+
6062
+ # React
6063
+
6064
+ \`\`\`tsx title="src/routes/posts/{-$category}.tsx"
4773
6065
  function PostsComponent() {
4774
6066
  // TypeScript knows category might be undefined
4775
6067
  const { category } = Route.useParams() // category: string | undefined
@@ -4796,6 +6088,37 @@ function PostsComponent() {
4796
6088
  </Link>
4797
6089
  \`\`\`
4798
6090
 
6091
+ # Solid
6092
+
6093
+ \`\`\`tsx title="src/routes/posts/{-$category}.tsx"
6094
+ function PostsComponent() {
6095
+ // TypeScript knows category might be undefined
6096
+ const params = Route.useParams() // category: string | undefined
6097
+
6098
+ // Safe navigation
6099
+ const categoryUpper = params().category?.toUpperCase()
6100
+
6101
+ return <div>{categoryUpper || 'All Categories'}</div>
6102
+ }
6103
+
6104
+ // Navigation is type-safe and flexible
6105
+ <Link
6106
+ to="/posts/{-$category}"
6107
+ params={{ category: 'tech' }} // ✅ Valid - string
6108
+ >
6109
+ Tech Posts
6110
+ </Link>
6111
+
6112
+ <Link
6113
+ to="/posts/{-$category}"
6114
+ params={{ category: 123 }} // ✅ Valid - number (auto-stringified)
6115
+ >
6116
+ Category 123
6117
+ </Link>
6118
+ \`\`\`
6119
+
6120
+ <!-- ::end:framework -->
6121
+
4799
6122
  ## Internationalization (i18n) with Optional Path Parameters
4800
6123
 
4801
6124
  Optional path parameters are excellent for implementing internationalization (i18n) routing patterns. You can use prefix patterns to handle multiple languages while maintaining clean, SEO-friendly URLs.
@@ -4804,7 +6127,11 @@ Optional path parameters are excellent for implementing internationalization (i1
4804
6127
 
4805
6128
  Use optional language prefixes to support URLs like \`/en/about\`, \`/fr/about\`, or just \`/about\` (default language):
4806
6129
 
4807
- \`\`\`tsx
6130
+ <!-- ::start:framework -->
6131
+
6132
+ # React
6133
+
6134
+ \`\`\`tsx title="src/routes/{-$locale}/about.tsx"
4808
6135
  // Route: /{-$locale}/about
4809
6136
  export const Route = createFileRoute('/{-$locale}/about')({
4810
6137
  component: AboutComponent,
@@ -4835,6 +6162,41 @@ function AboutComponent() {
4835
6162
  }
4836
6163
  \`\`\`
4837
6164
 
6165
+ # Solid
6166
+
6167
+ \`\`\`tsx title="src/routes/{-$locale}/about.tsx"
6168
+ // Route: /{-$locale}/about
6169
+ export const Route = createFileRoute('/{-$locale}/about')({
6170
+ component: AboutComponent,
6171
+ })
6172
+
6173
+ function AboutComponent() {
6174
+ const params = Route.useParams()
6175
+ const currentLocale = params().locale || 'en' // Default to English
6176
+
6177
+ const content = {
6178
+ en: { title: 'About Us', description: 'Learn more about our company.' },
6179
+ fr: {
6180
+ title: 'À Propos',
6181
+ description: 'En savoir plus sur notre entreprise.',
6182
+ },
6183
+ es: {
6184
+ title: 'Acerca de',
6185
+ description: 'Conoce más sobre nuestra empresa.',
6186
+ },
6187
+ }
6188
+
6189
+ return (
6190
+ <div>
6191
+ <h1>{content[currentLocale]?.title}</h1>
6192
+ <p>{content[currentLocale]?.description}</p>
6193
+ </div>
6194
+ )
6195
+ }
6196
+ \`\`\`
6197
+
6198
+ <!-- ::end:framework -->
6199
+
4838
6200
  This pattern matches:
4839
6201
 
4840
6202
  - \`/about\` (default locale)
@@ -4846,7 +6208,11 @@ This pattern matches:
4846
6208
 
4847
6209
  Combine optional parameters for more sophisticated i18n routing:
4848
6210
 
4849
- \`\`\`tsx
6211
+ <!-- ::start:framework -->
6212
+
6213
+ # React
6214
+
6215
+ \`\`\`tsx title="src/routes/{-$locale}/blog/{-$category}/$slug.tsx"
4850
6216
  // Route: /{-$locale}/blog/{-$category}/$slug
4851
6217
  export const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({
4852
6218
  beforeLoad: async ({ params }) => {
@@ -4886,6 +6252,51 @@ function BlogPostComponent() {
4886
6252
  }
4887
6253
  \`\`\`
4888
6254
 
6255
+ # Solid
6256
+
6257
+ \`\`\`tsx title="src/routes/{-$locale}/blog/{-$category}/$slug.tsx"
6258
+ // Route: /{-$locale}/blog/{-$category}/$slug
6259
+ export const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({
6260
+ beforeLoad: async ({ params }) => {
6261
+ const locale = params.locale || 'en'
6262
+ const category = params.category
6263
+
6264
+ // Validate locale and category
6265
+ const validLocales = ['en', 'fr', 'es', 'de']
6266
+ if (locale && !validLocales.includes(locale)) {
6267
+ throw new Error('Invalid locale')
6268
+ }
6269
+
6270
+ return { locale, category }
6271
+ },
6272
+ loader: async ({ params, context }) => {
6273
+ const { locale } = context
6274
+ const { slug, category } = params
6275
+
6276
+ return fetchBlogPost({ slug, category, locale })
6277
+ },
6278
+ component: BlogPostComponent,
6279
+ })
6280
+
6281
+ function BlogPostComponent() {
6282
+ const params = Route.useParams()
6283
+ const data = Route.useLoaderData()
6284
+
6285
+ return (
6286
+ <article>
6287
+ <h1>{data.title}</h1>
6288
+ <p>
6289
+ Category: {params().category || 'All'} | Language:{' '}
6290
+ {params().locale || 'en'}
6291
+ </p>
6292
+ <div>{data.content}</div>
6293
+ </article>
6294
+ )
6295
+ }
6296
+ \`\`\`
6297
+
6298
+ <!-- ::end:framework -->
6299
+
4889
6300
  This supports URLs like:
4890
6301
 
4891
6302
  - \`/blog/tech/my-post\` (default locale, tech category)
@@ -4897,7 +6308,11 @@ This supports URLs like:
4897
6308
 
4898
6309
  Create language switchers using optional i18n parameters with function-style params:
4899
6310
 
4900
- \`\`\`tsx
6311
+ <!-- ::start:framework -->
6312
+
6313
+ # React
6314
+
6315
+ \`\`\`tsx title="src/components/LanguageSwitcher.tsx"
4901
6316
  function LanguageSwitcher() {
4902
6317
  const currentParams = useParams({ strict: false })
4903
6318
 
@@ -4927,8 +6342,45 @@ function LanguageSwitcher() {
4927
6342
  }
4928
6343
  \`\`\`
4929
6344
 
6345
+ # Solid
6346
+
6347
+ \`\`\`tsx title="src/components/LanguageSwitcher.tsx"
6348
+ function LanguageSwitcher() {
6349
+ const currentParams = useParams({ strict: false })
6350
+
6351
+ const languages = [
6352
+ { code: 'en', name: 'English' },
6353
+ { code: 'fr', name: 'Français' },
6354
+ { code: 'es', name: 'Español' },
6355
+ ]
6356
+
6357
+ return (
6358
+ <div class="language-switcher">
6359
+ {languages.map(({ code, name }) => (
6360
+ <Link
6361
+ to="/{-$locale}/blog/{-$category}/$slug"
6362
+ params={(prev) => ({
6363
+ ...prev,
6364
+ locale: code === 'en' ? undefined : code, // Remove 'en' for clean URLs
6365
+ })}
6366
+ class={currentParams().locale === code ? 'active' : ''}
6367
+ >
6368
+ {name}
6369
+ </Link>
6370
+ ))}
6371
+ </div>
6372
+ )
6373
+ }
6374
+ \`\`\`
6375
+
6376
+ <!-- ::end:framework -->
6377
+
4930
6378
  You can also create more sophisticated language switching logic:
4931
6379
 
6380
+ <!-- ::start:framework -->
6381
+
6382
+ # React
6383
+
4932
6384
  \`\`\`tsx
4933
6385
  function AdvancedLanguageSwitcher() {
4934
6386
  const currentParams = useParams({ strict: false })
@@ -4976,10 +6428,65 @@ function AdvancedLanguageSwitcher() {
4976
6428
  }
4977
6429
  \`\`\`
4978
6430
 
6431
+ # Solid
6432
+
6433
+ \`\`\`tsx
6434
+ function AdvancedLanguageSwitcher() {
6435
+ const currentParams = useParams({ strict: false })
6436
+
6437
+ const handleLanguageChange = (newLocale: string) => {
6438
+ return (prev: any) => {
6439
+ // Preserve all existing params but update locale
6440
+ const updatedParams = { ...prev }
6441
+
6442
+ if (newLocale === 'en') {
6443
+ // Remove locale for clean English URLs
6444
+ delete updatedParams.locale
6445
+ } else {
6446
+ updatedParams.locale = newLocale
6447
+ }
6448
+
6449
+ return updatedParams
6450
+ }
6451
+ }
6452
+
6453
+ return (
6454
+ <div class="language-switcher">
6455
+ <Link
6456
+ to="/{-$locale}/blog/{-$category}/$slug"
6457
+ params={handleLanguageChange('fr')}
6458
+ >
6459
+ Français
6460
+ </Link>
6461
+
6462
+ <Link
6463
+ to="/{-$locale}/blog/{-$category}/$slug"
6464
+ params={handleLanguageChange('es')}
6465
+ >
6466
+ Español
6467
+ </Link>
6468
+
6469
+ <Link
6470
+ to="/{-$locale}/blog/{-$category}/$slug"
6471
+ params={handleLanguageChange('en')}
6472
+ >
6473
+ English
6474
+ </Link>
6475
+ </div>
6476
+ )
6477
+ }
6478
+ \`\`\`
6479
+
6480
+ <!-- ::end:framework -->
6481
+
4979
6482
  ### Advanced i18n with Optional Parameters
4980
6483
 
4981
6484
  Organize i18n routes using optional parameters for flexible locale handling:
4982
6485
 
6486
+ <!-- ::start:framework -->
6487
+
6488
+ # React
6489
+
4983
6490
  \`\`\`tsx
4984
6491
  // Route structure:
4985
6492
  // routes/
@@ -5013,11 +6520,52 @@ export const Route = createFileRoute('/{-$locale}/about')({
5013
6520
  })
5014
6521
  \`\`\`
5015
6522
 
6523
+ # Solid
6524
+
6525
+ \`\`\`tsx
6526
+ // Route structure:
6527
+ // routes/
6528
+ // {-$locale}/
6529
+ // index.tsx // /, /en, /fr
6530
+ // about.tsx // /about, /en/about, /fr/about
6531
+ // blog/
6532
+ // index.tsx // /blog, /en/blog, /fr/blog
6533
+ // $slug.tsx // /blog/post, /en/blog/post, /fr/blog/post
6534
+
6535
+ // routes/{-$locale}/index.tsx
6536
+ export const Route = createFileRoute('/{-$locale}/')({
6537
+ component: HomeComponent,
6538
+ })
6539
+
6540
+ function HomeComponent() {
6541
+ const params = Route.useParams()
6542
+ const isRTL = ['ar', 'he', 'fa'].includes(params().locale || '')
6543
+
6544
+ return (
6545
+ <div dir={isRTL ? 'rtl' : 'ltr'}>
6546
+ <h1>Welcome ({params().locale || 'en'})</h1>
6547
+ {/* Localized content */}
6548
+ </div>
6549
+ )
6550
+ }
6551
+
6552
+ // routes/{-$locale}/about.tsx
6553
+ export const Route = createFileRoute('/{-$locale}/about')({
6554
+ component: AboutComponent,
6555
+ })
6556
+ \`\`\`
6557
+
6558
+ <!-- ::end:framework -->
6559
+
5016
6560
  ### SEO and Canonical URLs
5017
6561
 
5018
6562
  Handle SEO for i18n routes properly:
5019
6563
 
5020
- \`\`\`tsx
6564
+ <!-- ::start:framework -->
6565
+
6566
+ # React
6567
+
6568
+ \`\`\`tsx title="src/routes/{-$locale}/products/$id.tsx"
5021
6569
  export const Route = createFileRoute('/{-$locale}/products/$id')({
5022
6570
  component: ProductComponent,
5023
6571
  head: ({ params, loaderData }) => {
@@ -5064,11 +6612,66 @@ export const Route = createFileRoute('/{-$locale}/products/$id')({
5064
6612
  })
5065
6613
  \`\`\`
5066
6614
 
6615
+ # Solid
6616
+
6617
+ \`\`\`tsx title="src/routes/{-$locale}/products/$id.tsx"
6618
+ export const Route = createFileRoute('/{-$locale}/products/$id')({
6619
+ component: ProductComponent,
6620
+ head: ({ params, loaderData }) => {
6621
+ const locale = params.locale || 'en'
6622
+ const product = loaderData
6623
+
6624
+ return {
6625
+ title: product.title[locale] || product.title.en,
6626
+ meta: [
6627
+ {
6628
+ name: 'description',
6629
+ content: product.description[locale] || product.description.en,
6630
+ },
6631
+ {
6632
+ property: 'og:locale',
6633
+ content: locale,
6634
+ },
6635
+ ],
6636
+ links: [
6637
+ // Canonical URL (always use default locale format)
6638
+ {
6639
+ rel: 'canonical',
6640
+ href: \`https://example.com/products/\${params.id}\`,
6641
+ },
6642
+ // Alternate language versions
6643
+ {
6644
+ rel: 'alternate',
6645
+ hreflang: 'en',
6646
+ href: \`https://example.com/products/\${params.id}\`,
6647
+ },
6648
+ {
6649
+ rel: 'alternate',
6650
+ hreflang: 'fr',
6651
+ href: \`https://example.com/fr/products/\${params.id}\`,
6652
+ },
6653
+ {
6654
+ rel: 'alternate',
6655
+ hreflang: 'es',
6656
+ href: \`https://example.com/es/products/\${params.id}\`,
6657
+ },
6658
+ ],
6659
+ }
6660
+ },
6661
+ })
6662
+ \`\`\`
6663
+
6664
+ <!-- ::end:framework -->
6665
+
5067
6666
  ### Type Safety for i18n
5068
6667
 
5069
6668
  Ensure type safety for your i18n implementations:
5070
6669
 
5071
- \`\`\`tsx
6670
+ <!-- ::start:framework -->
6671
+
6672
+ # React
6673
+
6674
+ \`\`\`tsx title="src/routes/{-$locale}/shop/{-$category}.tsx"
5072
6675
  // Define supported locales
5073
6676
  type Locale = 'en' | 'fr' | 'es' | 'de'
5074
6677
 
@@ -5118,6 +6721,60 @@ function ShopComponent() {
5118
6721
  }
5119
6722
  \`\`\`
5120
6723
 
6724
+ # Solid
6725
+
6726
+ \`\`\`tsx title="src/routes/{-$locale}/shop/{-$category}.tsx"
6727
+ // Define supported locales
6728
+ type Locale = 'en' | 'fr' | 'es' | 'de'
6729
+
6730
+ // Type-safe locale validation
6731
+ function validateLocale(locale: string | undefined): locale is Locale {
6732
+ return ['en', 'fr', 'es', 'de'].includes(locale as Locale)
6733
+ }
6734
+
6735
+ export const Route = createFileRoute('/{-$locale}/shop/{-$category}')({
6736
+ beforeLoad: async ({ params }) => {
6737
+ const { locale } = params
6738
+
6739
+ // Type-safe locale validation
6740
+ if (locale && !validateLocale(locale)) {
6741
+ throw redirect({
6742
+ to: '/shop/{-$category}',
6743
+ params: { category: params.category },
6744
+ })
6745
+ }
6746
+
6747
+ return {
6748
+ locale: (locale as Locale) || 'en',
6749
+ isDefaultLocale: !locale || locale === 'en',
6750
+ }
6751
+ },
6752
+ component: ShopComponent,
6753
+ })
6754
+
6755
+ function ShopComponent() {
6756
+ const params = Route.useParams()
6757
+ const routeContext = Route.useRouteContext()
6758
+
6759
+ // TypeScript knows locale is Locale | undefined
6760
+ // and we have validated it in beforeLoad
6761
+
6762
+ return (
6763
+ <div>
6764
+ <h1>Shop {params().category ? \`- \${params().category}\` : ''}</h1>
6765
+ <p>Language: {params().locale || 'en'}</p>
6766
+ {!routeContext().isDefaultLocale && (
6767
+ <Link to="/shop/{-$category}" params={{ category: params().category }}>
6768
+ View in English
6769
+ </Link>
6770
+ )}
6771
+ </div>
6772
+ )
6773
+ }
6774
+ \`\`\`
6775
+
6776
+ <!-- ::end:framework -->
6777
+
5121
6778
  Optional path parameters provide a powerful and flexible foundation for implementing internationalization in your TanStack Router applications. Whether you prefer prefix-based or combined approaches, you can create clean, SEO-friendly URLs while maintaining excellent developer experience and type safety.
5122
6779
 
5123
6780
  ## Allowed Characters
@@ -5171,6 +6828,10 @@ If you need more control over preloading, caching and/or garbage collection of p
5171
6828
 
5172
6829
  The simplest way to preload routes for your application is to set the \`defaultPreload\` option to \`intent\` for your entire router:
5173
6830
 
6831
+ <!-- ::start:framework -->
6832
+
6833
+ # React
6834
+
5174
6835
  \`\`\`tsx
5175
6836
  import { createRouter } from '@tanstack/react-router'
5176
6837
 
@@ -5180,12 +6841,29 @@ const router = createRouter({
5180
6841
  })
5181
6842
  \`\`\`
5182
6843
 
6844
+ # Solid
6845
+
6846
+ \`\`\`tsx
6847
+ import { createRouter } from '@tanstack/solid-router'
6848
+
6849
+ const router = createRouter({
6850
+ // ...
6851
+ defaultPreload: 'intent',
6852
+ })
6853
+ \`\`\`
6854
+
6855
+ <!-- ::end:framework -->
6856
+
5183
6857
  This will turn on \`intent\` preloading by default for all \`<Link>\` components in your application. You can also set the \`preload\` prop on individual \`<Link>\` components to override the default behavior.
5184
6858
 
5185
6859
  ## Preload Delay
5186
6860
 
5187
6861
  By default, preloading will start after **50ms** of the user hovering or touching a \`<Link>\` component. You can change this delay by setting the \`defaultPreloadDelay\` option on your router:
5188
6862
 
6863
+ <!-- ::start:framework -->
6864
+
6865
+ # React
6866
+
5189
6867
  \`\`\`tsx
5190
6868
  import { createRouter } from '@tanstack/react-router'
5191
6869
 
@@ -5195,6 +6873,19 @@ const router = createRouter({
5195
6873
  })
5196
6874
  \`\`\`
5197
6875
 
6876
+ # Solid
6877
+
6878
+ \`\`\`tsx
6879
+ import { createRouter } from '@tanstack/solid-router'
6880
+
6881
+ const router = createRouter({
6882
+ // ...
6883
+ defaultPreloadDelay: 100,
6884
+ })
6885
+ \`\`\`
6886
+
6887
+ <!-- ::end:framework -->
6888
+
5198
6889
  You can also set the \`preloadDelay\` prop on individual \`<Link>\` components to override the default behavior on a per-link basis.
5199
6890
 
5200
6891
  ## Built-in Preloading & \`preloadStaleTime\`
@@ -5203,6 +6894,10 @@ If you're using the built-in loaders, you can control how long preloaded data is
5203
6894
 
5204
6895
  To change this, you can set the \`defaultPreloadStaleTime\` option on your router:
5205
6896
 
6897
+ <!-- ::start:framework -->
6898
+
6899
+ # React
6900
+
5206
6901
  \`\`\`tsx
5207
6902
  import { createRouter } from '@tanstack/react-router'
5208
6903
 
@@ -5212,6 +6907,19 @@ const router = createRouter({
5212
6907
  })
5213
6908
  \`\`\`
5214
6909
 
6910
+ # Solid
6911
+
6912
+ \`\`\`tsx
6913
+ import { createRouter } from '@tanstack/solid-router'
6914
+
6915
+ const router = createRouter({
6916
+ // ...
6917
+ defaultPreloadStaleTime: 10_000,
6918
+ })
6919
+ \`\`\`
6920
+
6921
+ <!-- ::end:framework -->
6922
+
5215
6923
  Or, you can use the \`routeOptions.preloadStaleTime\` option on individual routes:
5216
6924
 
5217
6925
  \`\`\`tsx
@@ -5231,6 +6939,10 @@ To customize the preloading behavior in TanStack Router and fully leverage your
5231
6939
 
5232
6940
  For example:
5233
6941
 
6942
+ <!-- ::start:framework -->
6943
+
6944
+ # React
6945
+
5234
6946
  \`\`\`tsx
5235
6947
  import { createRouter } from '@tanstack/react-router'
5236
6948
 
@@ -5240,12 +6952,29 @@ const router = createRouter({
5240
6952
  })
5241
6953
  \`\`\`
5242
6954
 
6955
+ # Solid
6956
+
6957
+ \`\`\`tsx
6958
+ import { createRouter } from '@tanstack/solid-router'
6959
+
6960
+ const router = createRouter({
6961
+ // ...
6962
+ defaultPreloadStaleTime: 0,
6963
+ })
6964
+ \`\`\`
6965
+
6966
+ <!-- ::end:framework -->
6967
+
5243
6968
  This would then allow you, for instance, to use an option like React Query's \`staleTime\` to control the freshness of your preloads.
5244
6969
 
5245
6970
  ## Preloading Manually
5246
6971
 
5247
6972
  If you need to manually preload a route, you can use the router's \`preloadRoute\` method. It accepts a standard TanStack \`NavigateOptions\` object and returns a promise that resolves when the route is preloaded.
5248
6973
 
6974
+ <!-- ::start:framework -->
6975
+
6976
+ # React
6977
+
5249
6978
  \`\`\`tsx
5250
6979
  function Component() {
5251
6980
  const router = useRouter()
@@ -5269,8 +6998,39 @@ function Component() {
5269
6998
  }
5270
6999
  \`\`\`
5271
7000
 
7001
+ # Solid
7002
+
7003
+ \`\`\`tsx
7004
+ function Component() {
7005
+ const router = useRouter()
7006
+
7007
+ createEffect(() => {
7008
+ async function preload() {
7009
+ try {
7010
+ const matches = await router.preloadRoute({
7011
+ to: postRoute,
7012
+ params: { id: 1 },
7013
+ })
7014
+ } catch (err) {
7015
+ // Failed to preload route
7016
+ }
7017
+ }
7018
+
7019
+ preload()
7020
+ })
7021
+
7022
+ return <div />
7023
+ }
7024
+ \`\`\`
7025
+
7026
+ <!-- ::end:framework -->
7027
+
5272
7028
  If you need to preload only the JS chunk of a route, you can use the router's \`loadRouteChunk\` method. It accepts a route object and returns a promise that resolves when the route chunk is loaded.
5273
7029
 
7030
+ <!-- ::start:framework -->
7031
+
7032
+ # React
7033
+
5274
7034
  \`\`\`tsx
5275
7035
  function Component() {
5276
7036
  const router = useRouter()
@@ -5296,6 +7056,35 @@ function Component() {
5296
7056
  }
5297
7057
  \`\`\`
5298
7058
 
7059
+ # Solid
7060
+
7061
+ \`\`\`tsx
7062
+ function Component() {
7063
+ const router = useRouter()
7064
+
7065
+ createEffect(() => {
7066
+ async function preloadRouteChunks() {
7067
+ try {
7068
+ const postsRoute = router.routesByPath['/posts']
7069
+ await Promise.all([
7070
+ router.loadRouteChunk(router.routesByPath['/']),
7071
+ router.loadRouteChunk(postsRoute),
7072
+ router.loadRouteChunk(postsRoute.parentRoute),
7073
+ ])
7074
+ } catch (err) {
7075
+ // Failed to preload route chunk
7076
+ }
7077
+ }
7078
+
7079
+ preloadRouteChunks()
7080
+ })
7081
+
7082
+ return <div />
7083
+ }
7084
+ \`\`\`
7085
+
7086
+ <!-- ::end:framework -->
7087
+
5299
7088
  # Render Optimizations
5300
7089
 
5301
7090
  TanStack Router includes several optimizations to ensure your components only re-render when necessary. These optimizations include:
@@ -5476,7 +7265,9 @@ function onOpenPhoto() {
5476
7265
 
5477
7266
  In addition to the imperative API, you can also use the Router's \`routeMasks\` option to declaratively mask routes. Instead of needing to pass the \`mask\` option to every \`<Link>\` or \`navigate()\` call, you can instead create a route mask on the Router to mask routes that match a certain pattern. Here's an example of the same route mask from above, but using the \`routeMasks\` option instead:
5478
7267
 
5479
- // Use the following for the example below
7268
+ <!-- ::start:framework -->
7269
+
7270
+ # React
5480
7271
 
5481
7272
  \`\`\`tsx
5482
7273
  import { createRouteMask } from '@tanstack/react-router'
@@ -5496,6 +7287,28 @@ const router = createRouter({
5496
7287
  })
5497
7288
  \`\`\`
5498
7289
 
7290
+ # Solid
7291
+
7292
+ \`\`\`tsx
7293
+ import { createRouteMask } from '@tanstack/solid-router'
7294
+
7295
+ const photoModalToPhotoMask = createRouteMask({
7296
+ routeTree,
7297
+ from: '/photos/$photoId/modal',
7298
+ to: '/photos/$photoId',
7299
+ params: (prev) => ({
7300
+ photoId: prev.photoId,
7301
+ }),
7302
+ })
7303
+
7304
+ const router = createRouter({
7305
+ routeTree,
7306
+ routeMasks: [photoModalToPhotoMask],
7307
+ })
7308
+ \`\`\`
7309
+
7310
+ <!-- ::end:framework -->
7311
+
5499
7312
  When creating a route mask, you'll need to pass 1 argument with at least:
5500
7313
 
5501
7314
  - \`routeTree\` - The route tree that the route mask will be applied to
@@ -5539,6 +7352,10 @@ These are just suggested uses of the router context. You can use it for whatever
5539
7352
 
5540
7353
  Like everything else, the root router context is strictly typed. This type can be augmented via any route's \`beforeLoad\` option as it is merged down the route match tree. To constrain the type of the root router context, you must use the \`createRootRouteWithContext<YourContextTypeHere>()(routeOptions)\` function to create a new router context instead of the \`createRootRoute()\` function to create your root route. Here's an example:
5541
7354
 
7355
+ <!-- ::start:framework -->
7356
+
7357
+ # React
7358
+
5542
7359
  \`\`\`tsx
5543
7360
  import {
5544
7361
  createRootRouteWithContext,
@@ -5564,6 +7381,35 @@ const router = createRouter({
5564
7381
  })
5565
7382
  \`\`\`
5566
7383
 
7384
+ # Solid
7385
+
7386
+ \`\`\`tsx
7387
+ import {
7388
+ createRootRouteWithContext,
7389
+ createRouter,
7390
+ } from '@tanstack/solid-router'
7391
+
7392
+ interface MyRouterContext {
7393
+ user: User
7394
+ }
7395
+
7396
+ // Use the routerContext to create your root route
7397
+ const rootRoute = createRootRouteWithContext<MyRouterContext>()({
7398
+ component: App,
7399
+ })
7400
+
7401
+ const routeTree = rootRoute.addChildren([
7402
+ // ...
7403
+ ])
7404
+
7405
+ // Use the routerContext to create your router
7406
+ const router = createRouter({
7407
+ routeTree,
7408
+ })
7409
+ \`\`\`
7410
+
7411
+ <!-- ::end:framework -->
7412
+
5567
7413
  > [!TIP]
5568
7414
  > \`MyRouterContext\` only needs to contain content that will be passed directly to \`createRouter\` below. All other context added in \`beforeLoad\` will be inferred.
5569
7415
 
@@ -5574,6 +7420,10 @@ The router context is passed to the router at instantiation time. You can pass t
5574
7420
  > [!TIP]
5575
7421
  > If your context has any required properties, you will see a TypeScript error if you don't pass them in the initial router context. If all of your context properties are optional, you will not see a TypeScript error and passing the context will be optional. If you don't pass a router context, it defaults to \`{}\`.
5576
7422
 
7423
+ <!-- ::start:framework -->
7424
+
7425
+ # React
7426
+
5577
7427
  \`\`\`tsx
5578
7428
  import { createRouter } from '@tanstack/react-router'
5579
7429
 
@@ -5589,10 +7439,33 @@ const router = createRouter({
5589
7439
  })
5590
7440
  \`\`\`
5591
7441
 
7442
+ # Solid
7443
+
7444
+ \`\`\`tsx
7445
+ import { createRouter } from '@tanstack/solid-router'
7446
+
7447
+ // Use the routerContext you created to create your router
7448
+ const router = createRouter({
7449
+ routeTree,
7450
+ context: {
7451
+ user: {
7452
+ id: '123',
7453
+ name: 'John Doe',
7454
+ },
7455
+ },
7456
+ })
7457
+ \`\`\`
7458
+
7459
+ <!-- ::end:framework -->
7460
+
5592
7461
  ### Invalidating the Router Context
5593
7462
 
5594
7463
  If you need to invalidate the context state you are passing into the router, you can call the \`invalidate\` method to tell the router to recompute the context. This is useful when you need to update the context state and have the router recompute the context for all routes.
5595
7464
 
7465
+ <!-- ::start:framework -->
7466
+
7467
+ # React
7468
+
5596
7469
  \`\`\`tsx
5597
7470
  function useAuth() {
5598
7471
  const router = useRouter()
@@ -5611,6 +7484,28 @@ function useAuth() {
5611
7484
  }
5612
7485
  \`\`\`
5613
7486
 
7487
+ # Solid
7488
+
7489
+ \`\`\`tsx
7490
+ function useAuth() {
7491
+ const router = useRouter()
7492
+ const [user, setUser] = createSignal<User | null>(null)
7493
+
7494
+ createEffect(() => {
7495
+ const unsubscribe = auth.onAuthStateChanged((user) => {
7496
+ setUser(user)
7497
+ router.invalidate()
7498
+ })
7499
+
7500
+ return unsubscribe
7501
+ }, [])
7502
+
7503
+ return user()
7504
+ }
7505
+ \`\`\`
7506
+
7507
+ <!-- ::end:framework -->
7508
+
5614
7509
  ## Using the Router Context
5615
7510
 
5616
7511
  Once you have defined the router context type, you can use it in your route definitions:
@@ -5655,6 +7550,10 @@ export const Route = createFileRoute('/todos')({
5655
7550
 
5656
7551
  ### How about an external data fetching library?
5657
7552
 
7553
+ <!-- ::start:framework -->
7554
+
7555
+ # React
7556
+
5658
7557
  \`\`\`tsx
5659
7558
  import {
5660
7559
  createRootRouteWithContext,
@@ -5679,6 +7578,34 @@ const router = createRouter({
5679
7578
  })
5680
7579
  \`\`\`
5681
7580
 
7581
+ # Solid
7582
+
7583
+ \`\`\`tsx
7584
+ import {
7585
+ createRootRouteWithContext,
7586
+ createRouter,
7587
+ } from '@tanstack/solid-router'
7588
+
7589
+ interface MyRouterContext {
7590
+ queryClient: QueryClient
7591
+ }
7592
+
7593
+ const rootRoute = createRootRouteWithContext<MyRouterContext>()({
7594
+ component: App,
7595
+ })
7596
+
7597
+ const queryClient = new QueryClient()
7598
+
7599
+ const router = createRouter({
7600
+ routeTree: rootRoute,
7601
+ context: {
7602
+ queryClient,
7603
+ },
7604
+ })
7605
+ \`\`\`
7606
+
7607
+ <!-- ::end:framework -->
7608
+
5682
7609
  Then, in your route:
5683
7610
 
5684
7611
  \`\`\`tsx
@@ -5694,6 +7621,10 @@ export const Route = createFileRoute('/todos')({
5694
7621
  })
5695
7622
  \`\`\`
5696
7623
 
7624
+ <!-- ::start:framework -->
7625
+
7626
+ # React
7627
+
5697
7628
  ## How about using React Context/Hooks?
5698
7629
 
5699
7630
  When trying to use React Context or Hooks in your route's \`beforeLoad\` or \`loader\` functions, it's important to remember React's [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks). You can't use hooks in a non-React function, so you can't use hooks in your \`beforeLoad\` or \`loader\` functions.
@@ -5702,9 +7633,9 @@ So, how do we use React Context or Hooks in our route's \`beforeLoad\` or \`load
5702
7633
 
5703
7634
  Let's look at the setup for an example, where we pass down a \`useNetworkStrength\` hook to our route's \`loader\` function:
5704
7635
 
5705
- - \`src/routes/__root.tsx\`
7636
+ <!-- ::start:tabs variant="files" -->
5706
7637
 
5707
- \`\`\`tsx
7638
+ \`\`\`tsx title="src/routes/__root.tsx"
5708
7639
  // First, make sure the context for the root route is typed
5709
7640
  import { createRootRouteWithContext } from '@tanstack/react-router'
5710
7641
  import { useNetworkStrength } from '@/hooks/useNetworkStrength'
@@ -5718,11 +7649,13 @@ export const Route = createRootRouteWithContext<MyRouterContext>()({
5718
7649
  })
5719
7650
  \`\`\`
5720
7651
 
7652
+ <!-- ::end:tabs -->
7653
+
5721
7654
  In this example, we'd instantiate the hook before rendering the router using the \`<RouterProvider />\`. This way, the hook would be called in React-land, therefore adhering to the Rules of Hooks.
5722
7655
 
5723
- - \`src/router.tsx\`
7656
+ <!-- ::start:tabs variant="files" -->
5724
7657
 
5725
- \`\`\`tsx
7658
+ \`\`\`tsx title="src/router.tsx"
5726
7659
  import { createRouter } from '@tanstack/react-router'
5727
7660
 
5728
7661
  import { routeTree } from './routeTree.gen'
@@ -5735,9 +7668,13 @@ export const router = createRouter({
5735
7668
  })
5736
7669
  \`\`\`
5737
7670
 
5738
- - \`src/main.tsx\`
7671
+ <!-- ::end:tabs -->
5739
7672
 
5740
- \`\`\`tsx
7673
+ Then, we can call the \`useNetworkStrength\` hook in our \`App\` component and pass the returned value into the router context via the \`<RouterProvider />\`:
7674
+
7675
+ <!-- ::start:tabs variant="files" -->
7676
+
7677
+ \`\`\`tsx title="src/main.tsx"
5741
7678
  import { RouterProvider } from '@tanstack/react-router'
5742
7679
  import { router } from './router'
5743
7680
 
@@ -5752,11 +7689,13 @@ function App() {
5752
7689
  // ...
5753
7690
  \`\`\`
5754
7691
 
7692
+ <!-- ::end:tabs -->
7693
+
5755
7694
  So, now in our route's \`loader\` function, we can access the \`networkStrength\` hook from the router context:
5756
7695
 
5757
- - \`src/routes/posts.tsx\`
7696
+ <!-- ::start:tabs variant="files" -->
5758
7697
 
5759
- \`\`\`tsx
7698
+ \`\`\`tsx title="src/routes/posts.tsx"
5760
7699
  import { createFileRoute } from '@tanstack/react-router'
5761
7700
 
5762
7701
  export const Route = createFileRoute('/posts')({
@@ -5769,13 +7708,21 @@ export const Route = createFileRoute('/posts')({
5769
7708
  })
5770
7709
  \`\`\`
5771
7710
 
7711
+ <!-- ::end:tabs -->
7712
+
7713
+ <!-- ::end:framework -->
7714
+
5772
7715
  ## Modifying the Router Context
5773
7716
 
5774
7717
  The router context is passed down the route tree and is merged at each route. This means that you can modify the context at each route and the modifications will be available to all child routes. Here's an example:
5775
7718
 
5776
- - \`src/routes/__root.tsx\`
7719
+ <!-- ::start:framework -->
5777
7720
 
5778
- \`\`\`tsx
7721
+ # React
7722
+
7723
+ <!-- ::start:tabs variant="files" -->
7724
+
7725
+ \`\`\`tsx title="src/routes/__root.tsx"
5779
7726
  import { createRootRouteWithContext } from '@tanstack/react-router'
5780
7727
 
5781
7728
  interface MyRouterContext {
@@ -5787,9 +7734,11 @@ export const Route = createRootRouteWithContext<MyRouterContext>()({
5787
7734
  })
5788
7735
  \`\`\`
5789
7736
 
5790
- - \`src/router.tsx\`
7737
+ <!-- ::end:tabs -->
5791
7738
 
5792
- \`\`\`tsx
7739
+ <!-- ::start:tabs variant="files" -->
7740
+
7741
+ \`\`\`tsx title="src/router.tsx"
5793
7742
  import { createRouter } from '@tanstack/react-router'
5794
7743
 
5795
7744
  import { routeTree } from './routeTree.gen'
@@ -5802,9 +7751,11 @@ const router = createRouter({
5802
7751
  })
5803
7752
  \`\`\`
5804
7753
 
5805
- - \`src/routes/todos.tsx\`
7754
+ <!-- ::end:tabs -->
5806
7755
 
5807
- \`\`\`tsx
7756
+ <!-- ::start:tabs variant="files" -->
7757
+
7758
+ \`\`\`tsx title="src/routes/todos.tsx"
5808
7759
  import { createFileRoute } from '@tanstack/react-router'
5809
7760
 
5810
7761
  export const Route = createFileRoute('/todos')({
@@ -5821,12 +7772,71 @@ export const Route = createFileRoute('/todos')({
5821
7772
  })
5822
7773
  \`\`\`
5823
7774
 
7775
+ <!-- ::end:tabs -->
7776
+
7777
+ # Solid
7778
+
7779
+ <!-- ::start:tabs variant="files" -->
7780
+
7781
+ \`\`\`tsx title="src/routes/__root.tsx"
7782
+ import { createRootRouteWithContext } from '@tanstack/solid-router'
7783
+
7784
+ interface MyRouterContext {
7785
+ foo: boolean
7786
+ }
7787
+
7788
+ export const Route = createRootRouteWithContext<MyRouterContext>()({
7789
+ component: App,
7790
+ })
7791
+ \`\`\`
7792
+
7793
+ <!-- ::end:tabs -->
7794
+
7795
+ <!-- ::start:tabs variant="files" -->
7796
+
7797
+ \`\`\`tsx title="src/router.tsx"
7798
+ import { createRouter } from '@tanstack/solid-router'
7799
+
7800
+ import { routeTree } from './routeTree.gen'
7801
+
7802
+ const router = createRouter({
7803
+ routeTree,
7804
+ context: {
7805
+ foo: true,
7806
+ },
7807
+ })
7808
+ \`\`\`
7809
+
7810
+ <!-- ::end:tabs -->
7811
+
7812
+ <!-- ::start:tabs variant="files" -->
7813
+
7814
+ \`\`\`tsx title="src/routes/todos.tsx"
7815
+ import { createFileRoute } from '@tanstack/solid-router'
7816
+
7817
+ export const Route = createFileRoute('/todos')({
7818
+ component: Todos,
7819
+ beforeLoad: () => {
7820
+ return {
7821
+ bar: true,
7822
+ }
7823
+ },
7824
+ loader: ({ context }) => {
7825
+ context.foo // true
7826
+ context.bar // true
7827
+ },
7828
+ })
7829
+ \`\`\`
7830
+
7831
+ <!-- ::end:tabs -->
7832
+
7833
+ <!-- ::end:framework -->
7834
+
5824
7835
  ## Processing Accumulated Route Context
5825
7836
 
5826
7837
  Context, especially the isolated route \`context\` objects, make it trivial to accumulate and process the route context objects for all matched routes. Here's an example where we use all of the matched route contexts to generate a breadcrumb trail:
5827
7838
 
5828
- \`\`\`tsx
5829
- // src/routes/__root.tsx
7839
+ \`\`\`tsx title="src/routes/__root.tsx"
5830
7840
  export const Route = createRootRoute({
5831
7841
  component: () => {
5832
7842
  const matches = useRouterState({ select: (s) => s.matches })
@@ -5847,8 +7857,7 @@ export const Route = createRootRoute({
5847
7857
 
5848
7858
  Using that same route context, we could also generate a title tag for our page's \`<head>\`:
5849
7859
 
5850
- \`\`\`tsx
5851
- // src/routes/__root.tsx
7860
+ \`\`\`tsx title="src/routes/__root.tsx"
5852
7861
  export const Route = createRootRoute({
5853
7862
  component: () => {
5854
7863
  const matches = useRouterState({ select: (s) => s.matches })
@@ -5925,8 +7934,6 @@ It does this by:
5925
7934
  That may sound like a lot, but for you, it's as simple as this:
5926
7935
 
5927
7936
  \`\`\`tsx
5928
- import { createRouter } from '@tanstack/react-router'
5929
-
5930
7937
  const router = createRouter({
5931
7938
  scrollRestoration: true,
5932
7939
  })
@@ -5950,8 +7957,6 @@ The default \`getKey\` is \`(location) => location.state.__TSR_key!\`, where \`_
5950
7957
  You could sync scrolling to the pathname:
5951
7958
 
5952
7959
  \`\`\`tsx
5953
- import { createRouter } from '@tanstack/react-router'
5954
-
5955
7960
  const router = createRouter({
5956
7961
  getScrollRestorationKey: (location) => location.pathname,
5957
7962
  })
@@ -5960,8 +7965,6 @@ const router = createRouter({
5960
7965
  You can conditionally sync only some paths, then use the key for the rest:
5961
7966
 
5962
7967
  \`\`\`tsx
5963
- import { createRouter } from '@tanstack/react-router'
5964
-
5965
7968
  const router = createRouter({
5966
7969
  getScrollRestorationKey: (location) => {
5967
7970
  const paths = ['/', '/chat']
@@ -5988,8 +7991,6 @@ Most of the time, you won't need to do anything special to get scroll restoratio
5988
7991
 
5989
7992
  To manually control scroll restoration for virtualized lists within the whole browser window:
5990
7993
 
5991
- [//]: # 'VirtualizedWindowScrollRestorationExample'
5992
-
5993
7994
  \`\`\`tsx
5994
7995
  function Component() {
5995
7996
  const scrollEntry = useElementScrollRestoration({
@@ -6015,11 +8016,11 @@ function Component() {
6015
8016
  }
6016
8017
  \`\`\`
6017
8018
 
6018
- [//]: # 'VirtualizedWindowScrollRestorationExample'
6019
-
6020
8019
  To manually control scroll restoration for a specific element, you can use the \`useElementScrollRestoration\` hook and the \`data-scroll-restoration-id\` DOM attribute:
6021
8020
 
6022
- [//]: # 'ManualRestorationExample'
8021
+ <!-- ::start:framework -->
8022
+
8023
+ # React
6023
8024
 
6024
8025
  \`\`\`tsx
6025
8026
  function Component() {
@@ -6058,15 +8059,52 @@ function Component() {
6058
8059
  }
6059
8060
  \`\`\`
6060
8061
 
6061
- [//]: # 'ManualRestorationExample'
8062
+ # Solid
8063
+
8064
+ \`\`\`tsx
8065
+ function Component() {
8066
+ // We need a unique ID for manual scroll restoration on a specific element
8067
+ // It should be as unique as possible for this element across your app
8068
+ const scrollRestorationId = 'myVirtualizedContent'
8069
+
8070
+ // We use that ID to get the scroll entry for this element
8071
+ const scrollEntry = useElementScrollRestoration({
8072
+ id: scrollRestorationId,
8073
+ })
8074
+
8075
+ // Let's use TanStack Virtual to virtualize some content!
8076
+ let virtualizerParentRef: any
8077
+ const virtualizer = createVirtualizer({
8078
+ count: 10000,
8079
+ getScrollElement: () => virtualizerParentRef,
8080
+ estimateSize: () => 100,
8081
+ // We pass the scrollY from the scroll restoration entry to the virtualizer
8082
+ // as the initial offset
8083
+ initialOffset: scrollEntry?.scrollY,
8084
+ })
8085
+
8086
+ return (
8087
+ <div
8088
+ ref={virtualizerParentRef}
8089
+ // We pass the scroll restoration ID to the element
8090
+ // as a custom attribute that will get picked up by the
8091
+ // scroll restoration watcher
8092
+ data-scroll-restoration-id={scrollRestorationId}
8093
+ class="flex-1 border rounded-lg overflow-auto relative"
8094
+ >
8095
+ ...
8096
+ </div>
8097
+ )
8098
+ }
8099
+ \`\`\`
8100
+
8101
+ <!-- ::end:framework -->
6062
8102
 
6063
8103
  ## Scroll Behavior
6064
8104
 
6065
8105
  To control the scroll behavior when navigating between pages, you can use the \`scrollRestorationBehavior\` option. This allows you to make the transition between pages instant instead of a smooth scroll. The global configuration of scroll restoration behavior has the same options as those supported by the browser, which are \`smooth\`, \`instant\`, and \`auto\` (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#behavior) for more information).
6066
8106
 
6067
8107
  \`\`\`tsx
6068
- import { createRouter } from '@tanstack/react-router'
6069
-
6070
8108
  const router = createRouter({
6071
8109
  scrollRestorationBehavior: 'instant',
6072
8110
  })
@@ -6162,9 +8200,7 @@ Despite TanStack Router being able to parse search params into reliable JSON, th
6162
8200
 
6163
8201
  TanStack Router provides convenient APIs for validating and typing search params. This all starts with the \`Route\`'s \`validateSearch\` option:
6164
8202
 
6165
- \`\`\`tsx
6166
- // /routes/shop.products.tsx
6167
-
8203
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6168
8204
  type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'
6169
8205
 
6170
8206
  type ProductSearch = {
@@ -6193,9 +8229,7 @@ The \`validateSearch\` option is a function that is provided the JSON parsed (bu
6193
8229
 
6194
8230
  Here's an example:
6195
8231
 
6196
- \`\`\`tsx
6197
- // /routes/shop.products.tsx
6198
-
8232
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6199
8233
  type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'
6200
8234
 
6201
8235
  type ProductSearch = {
@@ -6218,9 +8252,7 @@ export const Route = createFileRoute('/shop/products')({
6218
8252
 
6219
8253
  Here's an example using the [Zod](https://zod.dev/) library (but feel free to use any validation library you want) to both validate and type the search params in a single step:
6220
8254
 
6221
- \`\`\`tsx
6222
- // /routes/shop.products.tsx
6223
-
8255
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6224
8256
  import { z } from 'zod'
6225
8257
 
6226
8258
  const productSearchSchema = z.object({
@@ -6251,7 +8283,6 @@ The underlying mechanics why this works relies on the \`validateSearch\` functio
6251
8283
  When using a library like [Zod](https://zod.dev/) to validate search params you might want to \`transform\` search params before committing the search params to the URL. A common \`zod\` \`transform\` is \`default\` for example.
6252
8284
 
6253
8285
  \`\`\`tsx
6254
- import { createFileRoute } from '@tanstack/react-router'
6255
8286
  import { z } from 'zod'
6256
8287
 
6257
8288
  const productSearchSchema = z.object({
@@ -6278,7 +8309,6 @@ For validation libraries we recommend using adapters which infer the correct \`i
6278
8309
  An adapter is provided for [Zod](https://zod.dev/) which will pipe through the correct \`input\` type and \`output\` type
6279
8310
 
6280
8311
  \`\`\`tsx
6281
- import { createFileRoute } from '@tanstack/react-router'
6282
8312
  import { zodValidator } from '@tanstack/zod-adapter'
6283
8313
  import { z } from 'zod'
6284
8314
 
@@ -6302,7 +8332,6 @@ The important part here is the following use of \`Link\` no longer requires \`se
6302
8332
  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
6303
8333
 
6304
8334
  \`\`\`tsx
6305
- import { createFileRoute } from '@tanstack/react-router'
6306
8335
  import { fallback, zodValidator } from '@tanstack/zod-adapter'
6307
8336
  import { z } from 'zod'
6308
8337
 
@@ -6351,7 +8380,6 @@ This provides flexibility in which type you want to infer for navigation and whi
6351
8380
  When using [Valibot](https://valibot.dev/) an adapter is not needed to ensure the correct \`input\` and \`output\` types are used for navigation and reading search params. This is because \`valibot\` implements [Standard Schema](https://github.com/standard-schema/standard-schema)
6352
8381
 
6353
8382
  \`\`\`tsx
6354
- import { createFileRoute } from '@tanstack/react-router'
6355
8383
  import * as v from 'valibot'
6356
8384
 
6357
8385
  const productSearchSchema = v.object({
@@ -6376,7 +8404,6 @@ export const Route = createFileRoute('/shop/products/')({
6376
8404
  When using [ArkType](https://arktype.io/) an adapter is not needed to ensure the correct \`input\` and \`output\` types are used for navigation and reading search params. This is because [ArkType](https://arktype.io/) implements [Standard Schema](https://github.com/standard-schema/standard-schema)
6377
8405
 
6378
8406
  \`\`\`tsx
6379
- import { createFileRoute } from '@tanstack/react-router'
6380
8407
  import { type } from 'arktype'
6381
8408
 
6382
8409
  const productSearchSchema = type({
@@ -6395,7 +8422,6 @@ export const Route = createFileRoute('/shop/products/')({
6395
8422
  When using [Effect/Schema](https://effect.website/docs/schema/introduction/) an adapter is not needed to ensure the correct \`input\` and \`output\` types are used for navigation and reading search params. This is because [Effect/Schema](https://effect.website/docs/schema/standard-schema/) implements [Standard Schema](https://github.com/standard-schema/standard-schema)
6396
8423
 
6397
8424
  \`\`\`tsx
6398
- import { createFileRoute } from '@tanstack/react-router'
6399
8425
  import { Schema as S } from 'effect'
6400
8426
 
6401
8427
  const productSearchSchema = S.standardSchemaV1(
@@ -6441,9 +8467,9 @@ Please read the [Search Params in Loaders](./data-loading.md#using-loaderdeps-to
6441
8467
 
6442
8468
  The search parameters and types of parents are merged as you go down the route tree, so child routes also have access to their parent's search params:
6443
8469
 
6444
- - \`shop.products.tsx\`
8470
+ <!-- ::start:tabs variant="files" -->
6445
8471
 
6446
- \`\`\`tsx
8472
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6447
8473
  const productSearchSchema = z.object({
6448
8474
  page: z.number().catch(1),
6449
8475
  filter: z.string().catch(''),
@@ -6457,9 +8483,7 @@ export const Route = createFileRoute('/shop/products')({
6457
8483
  })
6458
8484
  \`\`\`
6459
8485
 
6460
- - \`shop.products.$productId.tsx\`
6461
-
6462
- \`\`\`tsx
8486
+ \`\`\`tsx title="src/routes/shop/products/$productId.tsx"
6463
8487
  export const Route = createFileRoute('/shop/products/$productId')({
6464
8488
  beforeLoad: ({ search }) => {
6465
8489
  search
@@ -6468,13 +8492,13 @@ export const Route = createFileRoute('/shop/products/$productId')({
6468
8492
  })
6469
8493
  \`\`\`
6470
8494
 
8495
+ <!-- ::end:tabs -->
8496
+
6471
8497
  ### Search Params in Components
6472
8498
 
6473
8499
  You can access your route's validated search params in your route's \`component\` via the \`useSearch\` hook.
6474
8500
 
6475
- \`\`\`tsx
6476
- // /routes/shop.products.tsx
6477
-
8501
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6478
8502
  export const Route = createFileRoute('/shop/products')({
6479
8503
  validateSearch: productSearchSchema,
6480
8504
  })
@@ -6494,7 +8518,7 @@ const ProductList = () => {
6494
8518
  You can access your route's validated search params anywhere in your app using the \`useSearch\` hook. By passing the \`from\` id/path of your origin route, you'll get even better type safety:
6495
8519
 
6496
8520
  \`\`\`tsx
6497
- // /routes/shop.products.tsx
8521
+ // src/routes/shop.products.tsx
6498
8522
  export const Route = createFileRoute('/shop/products')({
6499
8523
  validateSearch: productSearchSchema,
6500
8524
  // ...
@@ -6502,7 +8526,7 @@ export const Route = createFileRoute('/shop/products')({
6502
8526
 
6503
8527
  // Somewhere else...
6504
8528
 
6505
- // /components/product-list-sidebar.tsx
8529
+ // src/components/product-list-sidebar.tsx
6506
8530
  const routeApi = getRouteApi('/shop/products')
6507
8531
 
6508
8532
  const ProductList = () => {
@@ -6546,8 +8570,7 @@ The best way to update search params is to use the \`search\` prop on the \`<Lin
6546
8570
  If the search for the current page shall be updated and the \`from\` prop is specified, the \`to\` prop can be omitted.
6547
8571
  Here's an example:
6548
8572
 
6549
- \`\`\`tsx
6550
- // /routes/shop.products.tsx
8573
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6551
8574
  export const Route = createFileRoute('/shop/products')({
6552
8575
  validateSearch: productSearchSchema,
6553
8576
  })
@@ -6603,8 +8626,7 @@ const PageSelector = () => {
6603
8626
 
6604
8627
  The \`navigate\` function also accepts a \`search\` option that works the same way as the \`search\` prop on \`<Link />\`:
6605
8628
 
6606
- \`\`\`tsx
6607
- // /routes/shop.products.tsx
8629
+ \`\`\`tsx title="src/routes/shop/products.tsx"
6608
8630
  export const Route = createFileRoute('/shop/products/$productId')({
6609
8631
  validateSearch: productSearchSchema,
6610
8632
  })
@@ -6648,7 +8670,6 @@ The following example shows how to make sure that for **every** link that is bei
6648
8670
 
6649
8671
  \`\`\`tsx
6650
8672
  import { z } from 'zod'
6651
- import { createFileRoute } from '@tanstack/react-router'
6652
8673
  import { zodValidator } from '@tanstack/zod-adapter'
6653
8674
 
6654
8675
  const searchSchema = z.object({
@@ -6673,6 +8694,10 @@ export const Route = createRootRoute({
6673
8694
 
6674
8695
  Since this specific use case is quite common, TanStack Router provides a generic implementation to retain search params via \`retainSearchParams\`:
6675
8696
 
8697
+ <!-- ::start:framework -->
8698
+
8699
+ # React
8700
+
6676
8701
  \`\`\`tsx
6677
8702
  import { z } from 'zod'
6678
8703
  import { createFileRoute, retainSearchParams } from '@tanstack/react-router'
@@ -6690,8 +8715,33 @@ export const Route = createRootRoute({
6690
8715
  })
6691
8716
  \`\`\`
6692
8717
 
8718
+ # Solid
8719
+
8720
+ \`\`\`tsx
8721
+ import { z } from 'zod'
8722
+ import { createFileRoute, retainSearchParams } from '@tanstack/solid-router'
8723
+ import { zodValidator } from '@tanstack/zod-adapter'
8724
+
8725
+ const searchSchema = z.object({
8726
+ rootValue: z.string().optional(),
8727
+ })
8728
+
8729
+ export const Route = createRootRoute({
8730
+ validateSearch: zodValidator(searchSchema),
8731
+ search: {
8732
+ middlewares: [retainSearchParams(['rootValue'])],
8733
+ },
8734
+ })
8735
+ \`\`\`
8736
+
8737
+ <!-- ::end:framework -->
8738
+
6693
8739
  Another common use case is to strip out search params from links if their default value is set. TanStack Router provides a generic implementation for this use case via \`stripSearchParams\`:
6694
8740
 
8741
+ <!-- ::start:framework -->
8742
+
8743
+ # React
8744
+
6695
8745
  \`\`\`tsx
6696
8746
  import { z } from 'zod'
6697
8747
  import { createFileRoute, stripSearchParams } from '@tanstack/react-router'
@@ -6716,8 +8766,40 @@ export const Route = createFileRoute('/hello')({
6716
8766
  })
6717
8767
  \`\`\`
6718
8768
 
8769
+ # Solid
8770
+
8771
+ \`\`\`tsx
8772
+ import { z } from 'zod'
8773
+ import { createFileRoute, stripSearchParams } from '@tanstack/solid-router'
8774
+ import { zodValidator } from '@tanstack/zod-adapter'
8775
+
8776
+ const defaultValues = {
8777
+ one: 'abc',
8778
+ two: 'xyz',
8779
+ }
8780
+
8781
+ const searchSchema = z.object({
8782
+ one: z.string().default(defaultValues.one),
8783
+ two: z.string().default(defaultValues.two),
8784
+ })
8785
+
8786
+ export const Route = createFileRoute('/hello')({
8787
+ validateSearch: zodValidator(searchSchema),
8788
+ search: {
8789
+ // strip default values
8790
+ middlewares: [stripSearchParams(defaultValues)],
8791
+ },
8792
+ })
8793
+ \`\`\`
8794
+
8795
+ <!-- ::end:framework -->
8796
+
6719
8797
  Multiple middlewares can be chained. The following example shows how to combine both \`retainSearchParams\` and \`stripSearchParams\`.
6720
8798
 
8799
+ <!-- ::start:framework -->
8800
+
8801
+ # React
8802
+
6721
8803
  \`\`\`tsx
6722
8804
  import {
6723
8805
  Link,
@@ -6747,6 +8829,39 @@ export const Route = createFileRoute('/search')({
6747
8829
  })
6748
8830
  \`\`\`
6749
8831
 
8832
+ # Solid
8833
+
8834
+ \`\`\`tsx
8835
+ import {
8836
+ Link,
8837
+ createFileRoute,
8838
+ retainSearchParams,
8839
+ stripSearchParams,
8840
+ } from '@tanstack/solid-router'
8841
+ import { z } from 'zod'
8842
+ import { zodValidator } from '@tanstack/zod-adapter'
8843
+
8844
+ const defaultValues = ['foo', 'bar']
8845
+
8846
+ export const Route = createFileRoute('/search')({
8847
+ validateSearch: zodValidator(
8848
+ z.object({
8849
+ retainMe: z.string().optional(),
8850
+ arrayWithDefaults: z.string().array().default(defaultValues),
8851
+ required: z.string(),
8852
+ }),
8853
+ ),
8854
+ search: {
8855
+ middlewares: [
8856
+ retainSearchParams(['retainMe']),
8857
+ stripSearchParams({ arrayWithDefaults: defaultValues }),
8858
+ ],
8859
+ },
8860
+ })
8861
+ \`\`\`
8862
+
8863
+ <!-- ::end:framework -->
8864
+
6750
8865
  # SSR
6751
8866
 
6752
8867
  > [!WARNING]
@@ -6770,6 +8885,10 @@ Non-Streaming server-side rendering is the classic process of rendering the mark
6770
8885
 
6771
8886
  To implement non-streaming SSR with TanStack Router, you will need the following utilities:
6772
8887
 
8888
+ <!-- ::start:framework -->
8889
+
8890
+ # React
8891
+
6773
8892
  - \`RouterClient\` from \`@tanstack/react-router\`
6774
8893
  - e.g. \`<RouterClient router={router} />\`
6775
8894
  - Rendering this component in your client entry will render your application and also automatically implement the \`Wrap\` component option on \`Router\`
@@ -6782,6 +8901,22 @@ To implement non-streaming SSR with TanStack Router, you will need the following
6782
8901
  - \`RouterServer\` from \`@tanstack/react-router\`
6783
8902
  - This implements the \`Wrap\` component option on \`Router\`
6784
8903
 
8904
+ # Solid
8905
+
8906
+ - \`RouterClient\` from \`@tanstack/solid-router\`
8907
+ - e.g. \`<RouterClient router={router} />\`
8908
+ - Rendering this component in your client entry will render your application and also automatically implement the \`Wrap\` component option on \`Router\`
8909
+ - And, either:
8910
+ - \`defaultRenderHandler\` from \`@tanstack/solid-router\`
8911
+ - This will render your application in your server entry and also automatically handle application-level hydration/dehydration and also automatically implement the RouterServer component.
8912
+ or:
8913
+ - \`renderRouterToString\` from \`@tanstack/solid-router\`
8914
+ - This differs from defaultRenderHandler in that it allows you to manually specify the \`Wrap\` component option on \`Router\` together with any other providers you may need to wrap it with.
8915
+ - \`RouterServer\` from \`@tanstack/solid-router\`
8916
+ - This implements the \`Wrap\` component option on \`Router\`
8917
+
8918
+ <!-- ::end:framework -->
8919
+
6785
8920
  ### Automatic Server History
6786
8921
 
6787
8922
  On the client, Router defaults to using an instance of \`createBrowserHistory\`, which is the preferred type of history to use on the client. On the server, however, you will want to use an instance of \`createMemoryHistory\` instead. This is because \`createBrowserHistory\` uses the \`window\` object, which does not exist on the server. This is handled automatically for you in the RouterServer component.
@@ -6798,8 +8933,11 @@ For more information on how to utilize data loading, see the [Data Loading](./da
6798
8933
 
6799
8934
  Since your router will exist both on the server and the client, it's important that you create your router in a way that is consistent between both of these environments. The easiest way to do this is to expose a \`createRouter\` function in a shared file that can be imported and called by both your server and client entry files.
6800
8935
 
6801
- \`\`\`tsx
6802
- // src/router.tsx
8936
+ <!-- ::start:framework -->
8937
+
8938
+ # React
8939
+
8940
+ \`\`\`tsx title='src/router.tsx'
6803
8941
  import { createRouter as createTanstackRouter } from '@tanstack/react-router'
6804
8942
  import { routeTree } from './routeTree.gen'
6805
8943
 
@@ -6814,14 +8952,36 @@ declare module '@tanstack/react-router' {
6814
8952
  }
6815
8953
  \`\`\`
6816
8954
 
8955
+ # Solid
8956
+
8957
+ \`\`\`tsx title='src/router.tsx'
8958
+ import { createRouter as createTanstackRouter } from '@tanstack/solid-router'
8959
+ import { routeTree } from './routeTree.gen'
8960
+
8961
+ export function createRouter() {
8962
+ return createTanstackRouter({ routeTree })
8963
+ }
8964
+
8965
+ declare module '@tanstack/solid-router' {
8966
+ interface Register {
8967
+ router: ReturnType<typeof createRouter>
8968
+ }
8969
+ }
8970
+ \`\`\`
8971
+
8972
+ <!-- ::end:framework -->
8973
+
6817
8974
  ### Rendering the Application on the Server
6818
8975
 
6819
8976
  Now that you have a router instance that has loaded all the critical data for the current URL, you can render your application on the server:
6820
8977
 
6821
8978
  using \`defaultRenderHandler\`
6822
8979
 
6823
- \`\`\`tsx
6824
- // src/entry-server.tsx
8980
+ <!-- ::start:framework -->
8981
+
8982
+ # React
8983
+
8984
+ \`\`\`tsx title="src/entry-server.tsx"
6825
8985
  import {
6826
8986
  createRequestHandler,
6827
8987
  defaultRenderToString,
@@ -6835,10 +8995,55 @@ export async function render({ request }: { request: Request }) {
6835
8995
  }
6836
8996
  \`\`\`
6837
8997
 
8998
+ # Solid
8999
+
9000
+ \`\`\`tsx title="src/entry-server.tsx"
9001
+ import {
9002
+ createRequestHandler,
9003
+ defaultRenderToString,
9004
+ } from '@tanstack/solid-router/ssr/server'
9005
+ import { createRouter } from './router'
9006
+
9007
+ export async function render({ request }: { request: Request }) {
9008
+ const handler = createRequestHandler({ request, createRouter })
9009
+
9010
+ return await handler(defaultRenderHandler)
9011
+ }
9012
+ \`\`\`
9013
+
9014
+ <!-- ::end:framework -->
9015
+
6838
9016
  using \`renderRouterToString\`
6839
9017
 
6840
- \`\`\`tsx
6841
- // src/entry-server.tsx
9018
+ <!-- ::start:framework -->
9019
+
9020
+ # React
9021
+
9022
+ \`\`\`tsx title="src/entry-server.tsx"
9023
+ import {
9024
+ createRequestHandler,
9025
+ renderRouterToString,
9026
+ RouterServer,
9027
+ } from '@tanstack/react-router/ssr/server'
9028
+ import { createRouter } from './router'
9029
+
9030
+ export function render({ request }: { request: Request }) {
9031
+ const handler = createRequestHandler({ request, createRouter })
9032
+
9033
+ return handler(({ request, responseHeaders, router }) =>
9034
+ renderRouterToString({
9035
+ request,
9036
+ responseHeaders,
9037
+ router,
9038
+ children: <RouterServer router={router} />,
9039
+ }),
9040
+ )
9041
+ }
9042
+ \`\`\`
9043
+
9044
+ # Solid
9045
+
9046
+ \`\`\`tsx title="src/entry-server.tsx"
6842
9047
  import {
6843
9048
  createRequestHandler,
6844
9049
  renderRouterToString,
@@ -6860,6 +9065,8 @@ export function render({ request }: { request: Request }) {
6860
9065
  }
6861
9066
  \`\`\`
6862
9067
 
9068
+ <!-- ::end:framework -->
9069
+
6863
9070
  NOTE: The createRequestHandler method requires a web api standard Request object, while the handler method will return a web api standard Response promise.
6864
9071
 
6865
9072
  Should you be using a server framework like Express that uses its own Request and Response objects you would need to convert from the one to the other. Please have a look at the examples for how such an implementation might look like.
@@ -6871,10 +9078,11 @@ On the client, things are much simpler.
6871
9078
  - Create your router instance
6872
9079
  - Render your application using the \`<RouterClient />\` component
6873
9080
 
6874
- [//]: # 'ClientEntryFileExample'
9081
+ <!-- ::start:framework -->
6875
9082
 
6876
- \`\`\`tsx
6877
- // src/entry-client.tsx
9083
+ # React
9084
+
9085
+ \`\`\`tsx title="src/entry-client.tsx"
6878
9086
  import { hydrateRoot } from 'react-dom/client'
6879
9087
  import { RouterClient } from '@tanstack/react-router/ssr/client'
6880
9088
  import { createRouter } from './router'
@@ -6884,45 +9092,105 @@ const router = createRouter()
6884
9092
  hydrateRoot(document, <RouterClient router={router} />)
6885
9093
  \`\`\`
6886
9094
 
6887
- [//]: # 'ClientEntryFileExample'
9095
+ # Solid
9096
+
9097
+ \`\`\`tsx title="src/entry-client.tsx"
9098
+ import { hydrate } from 'solid-js/web'
9099
+ import { RouterClient } from '@tanstack/solid-router/ssr/client'
9100
+ import { createRouter } from './router'
9101
+
9102
+ const router = createRouter()
9103
+
9104
+ hydrate(() => <RouterClient router={router} />, document.body)
9105
+ \`\`\`
9106
+
9107
+ <!-- ::end:framework -->
9108
+
9109
+ With this setup, your application will be rendered on the server and then hydrated on the client!
9110
+
9111
+ ## Streaming SSR
9112
+
9113
+ Streaming SSR is the most modern flavor of SSR and is the process of continuously and incrementally sending HTML markup to the client as it is rendered on the server. This is slightly different from traditional SSR in concept because beyond being able to dehydrate and rehydrate a critical first paint, markup and data with lower priority or slower response times can be streamed to the client after the initial render, but in the same request.
9114
+
9115
+ This pattern can be useful for pages that have slow or high-latency data fetching requirements. For example, if you have a page that needs to fetch data from a third-party API, you can stream the critical initial markup and data to the client and then stream the less-critical third-party data to the client as it is resolved.
9116
+
9117
+ > [!NOTE]
9118
+ > This streaming pattern is all automatic as long as you are using either \`defaultStreamHandler\` or \`renderRouterToStream\`.
9119
+
9120
+ using \`defaultStreamHandler\`
9121
+
9122
+ <!-- ::start:framework -->
9123
+
9124
+ # React
9125
+
9126
+ \`\`\`tsx title="src/entry-server.tsx"
9127
+ import {
9128
+ createRequestHandler,
9129
+ defaultStreamHandler,
9130
+ } from '@tanstack/react-router/ssr/server'
9131
+ import { createRouter } from './router'
9132
+
9133
+ export async function render({ request }: { request: Request }) {
9134
+ const handler = createRequestHandler({ request, createRouter })
9135
+
9136
+ return await handler(defaultStreamHandler)
9137
+ }
9138
+ \`\`\`
9139
+
9140
+ # Solid
6888
9141
 
6889
- With this setup, your application will be rendered on the server and then hydrated on the client!
9142
+ \`\`\`tsx title="src/entry-server.tsx"
9143
+ import {
9144
+ createRequestHandler,
9145
+ defaultStreamHandler,
9146
+ } from '@tanstack/solid-router/ssr/server'
9147
+ import { createRouter } from './router'
6890
9148
 
6891
- ## Streaming SSR
9149
+ export async function render({ request }: { request: Request }) {
9150
+ const handler = createRequestHandler({ request, createRouter })
6892
9151
 
6893
- Streaming SSR is the most modern flavor of SSR and is the process of continuously and incrementally sending HTML markup to the client as it is rendered on the server. This is slightly different from traditional SSR in concept because beyond being able to dehydrate and rehydrate a critical first paint, markup and data with lower priority or slower response times can be streamed to the client after the initial render, but in the same request.
9152
+ return await handler(defaultStreamHandler)
9153
+ }
9154
+ \`\`\`
6894
9155
 
6895
- This pattern can be useful for pages that have slow or high-latency data fetching requirements. For example, if you have a page that needs to fetch data from a third-party API, you can stream the critical initial markup and data to the client and then stream the less-critical third-party data to the client as it is resolved.
9156
+ <!-- ::end:framework -->
6896
9157
 
6897
- > [!NOTE]
6898
- > This streaming pattern is all automatic as long as you are using either \`defaultStreamHandler\` or \`renderRouterToStream\`.
9158
+ using \`renderRouterToStream\`
6899
9159
 
6900
- using \`defaultStreamHandler\`
9160
+ <!-- ::start:framework -->
6901
9161
 
6902
- \`\`\`tsx
6903
- // src/entry-server.tsx
9162
+ # React
9163
+
9164
+ \`\`\`tsx title="src/entry-server.tsx"
6904
9165
  import {
6905
9166
  createRequestHandler,
6906
- defaultStreamHandler,
9167
+ renderRouterToStream,
9168
+ RouterServer,
6907
9169
  } from '@tanstack/react-router/ssr/server'
6908
9170
  import { createRouter } from './router'
6909
9171
 
6910
- export async function render({ request }: { request: Request }) {
9172
+ export function render({ request }: { request: Request }) {
6911
9173
  const handler = createRequestHandler({ request, createRouter })
6912
9174
 
6913
- return await handler(defaultStreamHandler)
9175
+ return handler(({ request, responseHeaders, router }) =>
9176
+ renderRouterToStream({
9177
+ request,
9178
+ responseHeaders,
9179
+ router,
9180
+ children: <RouterServer router={router} />,
9181
+ }),
9182
+ )
6914
9183
  }
6915
9184
  \`\`\`
6916
9185
 
6917
- using \`renderRouterToStream\`
9186
+ # Solid
6918
9187
 
6919
- \`\`\`tsx
6920
- // src/entry-server.tsx
9188
+ \`\`\`tsx title="src/entry-server.tsx"
6921
9189
  import {
6922
9190
  createRequestHandler,
6923
9191
  renderRouterToStream,
6924
9192
  RouterServer,
6925
- } from '@tanstack/react-router/ssr/server'
9193
+ } from '@tanstack/solid-router/ssr/server'
6926
9194
  import { createRouter } from './router'
6927
9195
 
6928
9196
  export function render({ request }: { request: Request }) {
@@ -6939,6 +9207,8 @@ export function render({ request }: { request: Request }) {
6939
9207
  }
6940
9208
  \`\`\`
6941
9209
 
9210
+ <!-- ::end:framework -->
9211
+
6942
9212
  ## Streaming Dehydration/Hydration
6943
9213
 
6944
9214
  Streaming dehydration/hydration is an advanced pattern that goes beyond markup and allows you to dehydrate and stream any supporting data from the server to the client and rehydrate it on arrival. This is useful for applications that may need to further use/manage the underlying data that was used to render the initial markup on the server.
@@ -6966,9 +9236,13 @@ In addition to being able to access this data from the route itself, you can als
6966
9236
 
6967
9237
  ## Example
6968
9238
 
6969
- - \`posts.tsx\`
9239
+ <!-- ::start:framework -->
6970
9240
 
6971
- \`\`\`tsx
9241
+ # React
9242
+
9243
+ <!-- ::start:tabs variant="files" -->
9244
+
9245
+ \`\`\`tsx title='src/routes/posts.tsx'
6972
9246
  import { createFileRoute } from '@tanstack/react-router'
6973
9247
 
6974
9248
  export const Route = createFileRoute('/posts')({
@@ -6978,11 +9252,13 @@ export const Route = createFileRoute('/posts')({
6978
9252
  })
6979
9253
  \`\`\`
6980
9254
 
9255
+ <!-- ::end:tabs -->
9256
+
6981
9257
  You can then access this data anywhere you have access to your routes, including matches that can be mapped back to their routes.
6982
9258
 
6983
- - \`__root.tsx\`
9259
+ <!-- ::start:tabs variant="files" -->
6984
9260
 
6985
- \`\`\`tsx
9261
+ \`\`\`tsx title='src/routes/__root.tsx'
6986
9262
  import { createRootRoute } from '@tanstack/react-router'
6987
9263
 
6988
9264
  export const Route = createRootRoute({
@@ -7000,10 +9276,59 @@ export const Route = createRootRoute({
7000
9276
  })
7001
9277
  \`\`\`
7002
9278
 
9279
+ <!-- ::end:tabs -->
9280
+
9281
+ # Solid
9282
+
9283
+ <!-- ::start:tabs variant="files" -->
9284
+
9285
+ \`\`\`tsx title='src/routes/posts.tsx'
9286
+ import { createFileRoute } from '@tanstack/solid-router'
9287
+
9288
+ export const Route = createFileRoute('/posts')({
9289
+ staticData: {
9290
+ customData: 'Hello!',
9291
+ },
9292
+ })
9293
+ \`\`\`
9294
+
9295
+ <!-- ::end:tabs -->
9296
+
9297
+ You can then access this data anywhere you have access to your routes, including matches that can be mapped back to their routes.
9298
+
9299
+ <!-- ::start:tabs variant="files" -->
9300
+
9301
+ \`\`\`tsx title='src/routes/__root.tsx'
9302
+ import { createRootRoute, useMatches } from '@tanstack/solid-router'
9303
+ import { For } from 'solid-js'
9304
+
9305
+ export const Route = createRootRoute({
9306
+ component: () => {
9307
+ const matches = useMatches()
9308
+
9309
+ return (
9310
+ <div>
9311
+ <For each={matches()}>
9312
+ {(match) => <div>{match.staticData.customData}</div>}
9313
+ </For>
9314
+ </div>
9315
+ )
9316
+ },
9317
+ })
9318
+ \`\`\`
9319
+
9320
+ <!-- ::end:tabs -->
9321
+
9322
+ <!-- ::end:framework -->
9323
+
7003
9324
  ## Enforcing Static Data
7004
9325
 
7005
9326
  If you want to enforce that a route has static data, you can use declaration merging to add a type to the route's static option:
7006
9327
 
9328
+ <!-- ::start:framework -->
9329
+
9330
+ # React
9331
+
7007
9332
  \`\`\`tsx
7008
9333
  declare module '@tanstack/react-router' {
7009
9334
  interface StaticDataRouteOption {
@@ -7012,11 +9337,21 @@ declare module '@tanstack/react-router' {
7012
9337
  }
7013
9338
  \`\`\`
7014
9339
 
7015
- Now, if you try to create a route without the \`customData\` property, you'll get a type error:
9340
+ # Solid
7016
9341
 
7017
9342
  \`\`\`tsx
7018
- import { createFileRoute } from '@tanstack/react-router'
9343
+ declare module '@tanstack/solid-router' {
9344
+ interface StaticDataRouteOption {
9345
+ customData: string
9346
+ }
9347
+ }
9348
+ \`\`\`
9349
+
9350
+ <!-- ::end:framework -->
7019
9351
 
9352
+ Now, if you try to create a route without the \`customData\` property, you'll get a type error:
9353
+
9354
+ \`\`\`tsx
7020
9355
  export const Route = createFileRoute('/posts')({
7021
9356
  staticData: {
7022
9357
  // Property 'customData' is missing in type '{ customData: number; }' but required in type 'StaticDataRouteOption'.ts(2741)
@@ -7028,6 +9363,10 @@ export const Route = createFileRoute('/posts')({
7028
9363
 
7029
9364
  If you want to make static data optional, simply add a \`?\` to the property:
7030
9365
 
9366
+ <!-- ::start:framework -->
9367
+
9368
+ # React
9369
+
7031
9370
  \`\`\`tsx
7032
9371
  declare module '@tanstack/react-router' {
7033
9372
  interface StaticDataRouteOption {
@@ -7036,6 +9375,18 @@ declare module '@tanstack/react-router' {
7036
9375
  }
7037
9376
  \`\`\`
7038
9377
 
9378
+ # Solid
9379
+
9380
+ \`\`\`tsx
9381
+ declare module '@tanstack/solid-router' {
9382
+ interface StaticDataRouteOption {
9383
+ customData?: string
9384
+ }
9385
+ }
9386
+ \`\`\`
9387
+
9388
+ <!-- ::end:framework -->
9389
+
7039
9390
  As long as there are any required properties on the \`StaticDataRouteOption\`, you'll be required to pass in an object.
7040
9391
 
7041
9392
  ## Common Patterns
@@ -7044,14 +9395,19 @@ As long as there are any required properties on the \`StaticDataRouteOption\`, y
7044
9395
 
7045
9396
  Use staticData to control which routes show or hide layout elements:
7046
9397
 
7047
- \`\`\`tsx
7048
- // routes/admin/route.tsx
9398
+ <!-- ::start:tabs variant="files" -->
9399
+
9400
+ \`\`\`tsx title='src/routes/admin/route.tsx'
7049
9401
  export const Route = createFileRoute('/admin')({
7050
9402
  staticData: { showNavbar: false },
7051
9403
  component: AdminLayout,
7052
9404
  })
7053
9405
  \`\`\`
7054
9406
 
9407
+ <!-- ::end:tabs -->
9408
+
9409
+ <!-- ::start:tabs variant="files" -->
9410
+
7055
9411
  \`\`\`tsx
7056
9412
  // routes/__root.tsx
7057
9413
  function RootComponent() {
@@ -7070,10 +9426,17 @@ function RootComponent() {
7070
9426
  }
7071
9427
  \`\`\`
7072
9428
 
9429
+ <!-- ::end:tabs -->
9430
+
7073
9431
  ### Route Titles for Breadcrumbs
7074
9432
 
7075
- \`\`\`tsx
7076
- // routes/posts/$postId.tsx
9433
+ <!-- ::start:framework -->
9434
+
9435
+ # React
9436
+
9437
+ <!-- ::start:tabs variant="files" -->
9438
+
9439
+ \`\`\`tsx title='src/routes/posts/$postId.tsx'
7077
9440
  export const Route = createFileRoute('/posts/$postId')({
7078
9441
  staticData: {
7079
9442
  getTitle: () => 'Post Details',
@@ -7081,8 +9444,11 @@ export const Route = createFileRoute('/posts/$postId')({
7081
9444
  })
7082
9445
  \`\`\`
7083
9446
 
7084
- \`\`\`tsx
7085
- // In a Breadcrumb component
9447
+ <!-- ::end:tabs -->
9448
+
9449
+ <!-- ::start:tabs variant="files" -->
9450
+
9451
+ \`\`\`tsx title='src/components/Breadcrumbs.tsx'
7086
9452
  function Breadcrumbs() {
7087
9453
  const matches = useMatches()
7088
9454
 
@@ -7098,6 +9464,45 @@ function Breadcrumbs() {
7098
9464
  }
7099
9465
  \`\`\`
7100
9466
 
9467
+ <!-- ::end:tabs -->
9468
+
9469
+ # Solid
9470
+
9471
+ <!-- ::start:tabs variant="files" -->
9472
+
9473
+ \`\`\`tsx title='src/routes/posts/$postId.tsx'
9474
+ export const Route = createFileRoute('/posts/$postId')({
9475
+ staticData: {
9476
+ getTitle: () => 'Post Details',
9477
+ },
9478
+ })
9479
+ \`\`\`
9480
+
9481
+ <!-- ::end:tabs -->
9482
+
9483
+ <!-- ::start:tabs variant="files" -->
9484
+
9485
+ \`\`\`tsx title='src/components/Breadcrumbs.tsx'
9486
+ import { useMatches } from '@tanstack/solid-router'
9487
+ import { For } from 'solid-js'
9488
+
9489
+ function Breadcrumbs() {
9490
+ const matches = useMatches()
9491
+
9492
+ return (
9493
+ <nav>
9494
+ <For each={matches().filter((m) => m.staticData?.getTitle)}>
9495
+ {(m) => <span>{m.staticData.getTitle()}</span>}
9496
+ </For>
9497
+ </nav>
9498
+ )
9499
+ }
9500
+ \`\`\`
9501
+
9502
+ <!-- ::end:tabs -->
9503
+
9504
+ <!-- ::end:framework -->
9505
+
7101
9506
  ### When to Use staticData vs Context
7102
9507
 
7103
9508
  | staticData | context |
@@ -7136,6 +9541,10 @@ const parentRoute = createRoute({
7136
9541
 
7137
9542
  For the types of your router to work with top-level exports like \`Link\`, \`useNavigate\`, \`useParams\`, etc. they must permeate the TypeScript module boundary and be registered right into the library. To do this, we use declaration merging on the exported \`Register\` interface.
7138
9543
 
9544
+ <!-- ::start:framework -->
9545
+
9546
+ # React
9547
+
7139
9548
  \`\`\`ts
7140
9549
  const router = createRouter({
7141
9550
  // ...
@@ -7148,6 +9557,22 @@ declare module '@tanstack/react-router' {
7148
9557
  }
7149
9558
  \`\`\`
7150
9559
 
9560
+ # Solid
9561
+
9562
+ \`\`\`ts
9563
+ const router = createRouter({
9564
+ // ...
9565
+ })
9566
+
9567
+ declare module '@tanstack/solid-router' {
9568
+ interface Register {
9569
+ router: typeof router
9570
+ }
9571
+ }
9572
+ \`\`\`
9573
+
9574
+ <!-- ::end:framework -->
9575
+
7151
9576
  By registering your router with the module, you can now use the exported hooks, components, and utilities with your router's exact types.
7152
9577
 
7153
9578
  ## Fixing the Component Context Problem
@@ -7402,6 +9827,10 @@ Most types exposed by TanStack Router are internal, subject to breaking changes
7402
9827
 
7403
9828
  \`ValidateLinkOptions\` type checks object literal types to ensure they conform to \`Link\` options at inference sites. For example, you may have a generic \`HeadingLink\` component which accepts a \`title\` prop along with \`linkOptions\`, the idea being this component can be re-used for any navigation.
7404
9829
 
9830
+ <!-- ::start:framework -->
9831
+
9832
+ # React
9833
+
7405
9834
  \`\`\`tsx
7406
9835
  export interface HeaderLinkProps<
7407
9836
  TRouter extends RegisteredRouter = RegisteredRouter,
@@ -7424,6 +9853,32 @@ export function HeadingLink(props: HeaderLinkProps): React.ReactNode {
7424
9853
  }
7425
9854
  \`\`\`
7426
9855
 
9856
+ # Solid
9857
+
9858
+ \`\`\`tsx
9859
+ export interface HeaderLinkProps<
9860
+ TRouter extends RegisteredRouter = RegisteredRouter,
9861
+ TOptions = unknown,
9862
+ > {
9863
+ title: string
9864
+ linkOptions: ValidateLinkOptions<TRouter, TOptions>
9865
+ }
9866
+
9867
+ export function HeadingLink<TRouter extends RegisteredRouter, TOptions>(
9868
+ props: HeaderLinkProps<TRouter, TOptions>,
9869
+ ): Solid.JSX.Element
9870
+ export function HeadingLink(props: HeaderLinkProps): Solid.JSX.Element {
9871
+ return (
9872
+ <>
9873
+ <h1>{props.title}</h1>
9874
+ <Link {...props.linkOptions} />
9875
+ </>
9876
+ )
9877
+ }
9878
+ \`\`\`
9879
+
9880
+ <!-- ::end:framework -->
9881
+
7427
9882
  A more permissive overload of \`HeadingLink\` is used to avoid type assertions you would otherwise have to do with the generic signature. Using a looser signature without type parameters is an easy way to avoid type assertions in the implementation of \`HeadingLink\`
7428
9883
 
7429
9884
  All type parameters for utilities are optional but for the best TypeScript performance \`TRouter\` should always be specified for the public facing signature. And \`TOptions\` should always be used at inference sites like \`HeadingLink\` to infer the \`linkOptions\` to correctly narrow \`params\` and \`search\`
@@ -7439,6 +9894,10 @@ The result of this is that \`linkOptions\` in the following is completely type-s
7439
9894
 
7440
9895
  All navigation type utilities have an array variant. \`ValidateLinkOptionsArray\` enables type checking of an array of \`Link\` options. For example, you might have a generic \`Menu\` component where each item is a \`Link\`.
7441
9896
 
9897
+ <!-- ::start:framework -->
9898
+
9899
+ # React
9900
+
7442
9901
  \`\`\`tsx
7443
9902
  export interface MenuProps<
7444
9903
  TRouter extends RegisteredRouter = RegisteredRouter,
@@ -7464,6 +9923,39 @@ export function Menu(props: MenuProps): React.ReactNode {
7464
9923
  }
7465
9924
  \`\`\`
7466
9925
 
9926
+ # Solid
9927
+
9928
+ \`\`\`tsx
9929
+ import { For } from 'solid-js'
9930
+
9931
+ export interface MenuProps<
9932
+ TRouter extends RegisteredRouter = RegisteredRouter,
9933
+ TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
9934
+ > {
9935
+ items: ValidateLinkOptionsArray<TRouter, TItems>
9936
+ }
9937
+
9938
+ export function Menu<
9939
+ TRouter extends RegisteredRouter = RegisteredRouter,
9940
+ TItems extends ReadonlyArray<unknown>,
9941
+ >(props: MenuProps<TRouter, TItems>): Solid.JSX.Element
9942
+ export function Menu(props: MenuProps): Solid.JSX.Element {
9943
+ return (
9944
+ <ul>
9945
+ <For each={props.items}>
9946
+ {(item) => (
9947
+ <li>
9948
+ <Link {...item} />
9949
+ </li>
9950
+ )}
9951
+ </For>
9952
+ </ul>
9953
+ )
9954
+ }
9955
+ \`\`\`
9956
+
9957
+ <!-- ::end:framework -->
9958
+
7467
9959
  This of course allows the following \`items\` prop to be completely type-safe
7468
9960
 
7469
9961
  \`\`\`tsx
@@ -7477,7 +9969,11 @@ This of course allows the following \`items\` prop to be completely type-safe
7477
9969
 
7478
9970
  It is also possible to fix \`from\` for each \`Link\` options in the array. This would allow all \`Menu\` items to navigate relative to \`from\`. Additional type checking of \`from\` can be provided by the \`ValidateFromPath\` utility
7479
9971
 
7480
- \`\`\`tsx
9972
+ <!-- ::start:framework -->
9973
+
9974
+ # React
9975
+
9976
+ \`\`\`ts
7481
9977
  export interface MenuProps<
7482
9978
  TRouter extends RegisteredRouter = RegisteredRouter,
7483
9979
  TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
@@ -7505,6 +10001,42 @@ export function Menu(props: MenuProps): React.ReactNode {
7505
10001
  }
7506
10002
  \`\`\`
7507
10003
 
10004
+ # Solid
10005
+
10006
+ \`\`\`ts
10007
+ import { For } from 'solid-js'
10008
+
10009
+ export interface MenuProps<
10010
+ TRouter extends RegisteredRouter = RegisteredRouter,
10011
+ TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
10012
+ TFrom extends string = string,
10013
+ > {
10014
+ from: ValidateFromPath<TRouter, TFrom>
10015
+ items: ValidateLinkOptionsArray<TRouter, TItems, TFrom>
10016
+ }
10017
+
10018
+ export function Menu<
10019
+ TRouter extends RegisteredRouter = RegisteredRouter,
10020
+ TItems extends ReadonlyArray<unknown>,
10021
+ TFrom extends string = string,
10022
+ >(props: MenuProps<TRouter, TItems, TFrom>): Solid.JSX.Element
10023
+ export function Menu(props: MenuProps): Solid.JSX.Element {
10024
+ return (
10025
+ <ul>
10026
+ <For each={props.items}>
10027
+ {(item) => (
10028
+ <li>
10029
+ <Link {...item} from={props.from} />
10030
+ </li>
10031
+ )}
10032
+ </For>
10033
+ </ul>
10034
+ )
10035
+ }
10036
+ \`\`\`
10037
+
10038
+ <!-- ::end:framework -->
10039
+
7508
10040
  \`ValidateLinkOptionsArray\` allows you to fix \`from\` by providing an extra type parameter. The result is a type safe array of \`Link\` options providing navigation relative to \`from\`
7509
10041
 
7510
10042
  \`\`\`tsx
@@ -7550,7 +10082,9 @@ fetchOrRedirect('http://example.com/', { to: '/login' })
7550
10082
 
7551
10083
  \`ValidateNavigateOptions\` type checks object literal types to ensure they conform to navigate options at inference sites. For example, you may want to write a custom hook to enable/disable navigation.
7552
10084
 
7553
- [//]: # 'TypeCheckingNavigateOptionsWithValidateNavigateOptionsImpl'
10085
+ <!-- ::start:framework -->
10086
+
10087
+ # React
7554
10088
 
7555
10089
  \`\`\`tsx
7556
10090
  export interface UseConditionalNavigateResult {
@@ -7582,7 +10116,41 @@ export function useConditionalNavigate(
7582
10116
  }
7583
10117
  \`\`\`
7584
10118
 
7585
- [//]: # 'TypeCheckingNavigateOptionsWithValidateNavigateOptionsImpl'
10119
+ # Solid
10120
+
10121
+ \`\`\`tsx
10122
+ import { createSignal } from 'solid-js'
10123
+
10124
+ export interface UseConditionalNavigateResult {
10125
+ enable: () => void
10126
+ disable: () => void
10127
+ navigate: () => void
10128
+ }
10129
+
10130
+ export function useConditionalNavigate<
10131
+ TRouter extends RegisteredRouter = RegisteredRouter,
10132
+ TOptions = unknown,
10133
+ >(
10134
+ navigateOptions: ValidateNavigateOptions<TRouter, TOptions>,
10135
+ ): UseConditionalNavigateResult
10136
+ export function useConditionalNavigate(
10137
+ navigateOptions: ValidateNavigateOptions,
10138
+ ): UseConditionalNavigateResult {
10139
+ const [enabled, setEnabled] = createSignal(false)
10140
+ const navigate = useNavigate()
10141
+ return {
10142
+ enable: () => setEnabled(true),
10143
+ disable: () => setEnabled(false),
10144
+ navigate: () => {
10145
+ if (enabled()) {
10146
+ navigate(navigateOptions)
10147
+ }
10148
+ },
10149
+ }
10150
+ }
10151
+ \`\`\`
10152
+
10153
+ <!-- ::end:framework -->
7586
10154
 
7587
10155
  The result of this is that \`navigateOptions\` passed to \`useConditionalNavigate\` is completely type-safe and we can enable/disable navigation based on react state
7588
10156