@tanstack/react-router 1.133.3 → 1.133.7

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.
@@ -187,20 +187,20 @@ To install TanStack Router in your project, run the following command using your
187
187
  [//]: # 'installCommand'
188
188
 
189
189
  \`\`\`sh
190
- npm install @tanstack/router
190
+ npm install @tanstack/react-router
191
191
  # or
192
- pnpm add @tanstack/router
192
+ pnpm add @tanstack/react-router
193
193
  #or
194
- yarn add @tanstack/router
194
+ yarn add @tanstack/react-router
195
195
  # or
196
- bun add @tanstack/router
196
+ bun add @tanstack/react-router
197
197
  # or
198
- deno add npm:@tanstack/router
198
+ deno add npm:@tanstack/react-router
199
199
  \`\`\`
200
200
 
201
201
  [//]: # 'installCommand'
202
202
 
203
- Once installed, you can verify the installation by checking your \`package.json\` file for the \`@tanstack/router\` dependency.
203
+ Once installed, you can verify the installation by checking your \`package.json\` file for the dependency.
204
204
 
205
205
  [//]: # 'packageJson'
206
206
 
@@ -295,6 +295,242 @@ npm run dev
295
295
 
296
296
  With either approach, you can now start building your React application with TanStack Router!
297
297
 
298
+ # Decisions on Developer Experience
299
+
300
+ When people first start using TanStack Router, they often have a lot of questions that revolve around the following themes:
301
+
302
+ > Why do I have to do things this way?
303
+
304
+ > Why is it done this way? and not that way?
305
+
306
+ > I'm used to doing it this way, why should I change?
307
+
308
+ And they are all valid questions. For the most part, people are used to using routing libraries that are very similar to each other. They all have a similar API, similar concepts, and similar ways of doing things.
309
+
310
+ But TanStack Router is different. It's not your average routing library. It's not your average state management library. It's not your average anything.
311
+
312
+ ## TanStack Router's origin story
313
+
314
+ It's important to remember that TanStack Router's origins stem from [Nozzle.io](https://nozzle.io)'s need for a client-side routing solution that offered a first-in-class _URL Search Parameters_ experience without compromising on the **_type-safety_** that was required to power its complex dashboards.
315
+
316
+ And so, from TanStack Router's very inception, every facet of its design was meticulously thought out to ensure that its type-safety and developer experience were second to none.
317
+
318
+ ## How does TanStack Router achieve this?
319
+
320
+ > TypeScript! TypeScript! TypeScript!
321
+
322
+ Every aspect of TanStack Router is designed to be as type-safe as possible, and this is achieved by leveraging TypeScript's type system to its fullest extent. This involves using some very advanced and complex types, type inference, and other features to ensure that the developer experience is as smooth as possible.
323
+
324
+ But to achieve this, we had to make some decisions that deviate from the norms in the routing world.
325
+
326
+ 1. [**Route configuration boilerplate?**](#1-why-is-the-routers-configuration-done-this-way): You have to define your routes in a way that allows TypeScript to infer the types of your routes as much as possible.
327
+ 2. [**TypeScript module declaration for the router?**](#2-declaring-the-router-instance-for-type-inference): You have to pass the \`Router\` instance to the rest of your application using TypeScript's module declaration.
328
+ 3. [**Why push for file-based routing over code-based?**](#3-why-is-file-based-routing-the-preferred-way-to-define-routes): We push for file-based routing as the preferred way to define your routes.
329
+
330
+ > TLDR; All the design decisions in the developer experience of using TanStack Router are made so that you can have a best-in-class type-safety experience without compromising on the control, flexibility, and maintainability of your route configurations.
331
+
332
+ ## 1. Why is the Router's configuration done this way?
333
+
334
+ When you want to leverage the TypeScript's inference features to its fullest, you'll quickly realize that _Generics_ are your best friend. And so, TanStack Router uses Generics everywhere to ensure that the types of your routes are inferred as much as possible.
335
+
336
+ This means that you have to define your routes in a way that allows TypeScript to infer the types of your routes as much as possible.
337
+
338
+ > Can I use JSX to define my routes?
339
+
340
+ Using JSX for defining your routes is **out of the question**, as TypeScript will not be able to infer the route configuration types of your router.
341
+
342
+ \`\`\`tsx
343
+ // ⛔️ This is not possible
344
+ function App() {
345
+ return (
346
+ <Router>
347
+ <Route path="/posts" component={PostsPage} />
348
+ <Route path="/posts/$postId" component={PostIdPage} />
349
+ {/* ... */}
350
+ </Router>
351
+ // ^? TypeScript cannot infer the routes in this configuration
352
+ )
353
+ }
354
+ \`\`\`
355
+
356
+ And since this would mean that you'd have to manually type the \`to\` prop of the \`<Link>\` component and wouldn't catch any errors until runtime, it's not a viable option.
357
+
358
+ > Maybe I could define my routes as a tree of nested objects?
359
+
360
+ \`\`\`tsx
361
+ // ⛔️ This file will just keep growing and growing...
362
+ const router = createRouter({
363
+ routes: {
364
+ posts: {
365
+ component: PostsPage, // /posts
366
+ children: {
367
+ $postId: {
368
+ component: PostIdPage, // /posts/$postId
369
+ },
370
+ },
371
+ },
372
+ // ...
373
+ },
374
+ })
375
+ \`\`\`
376
+
377
+ At first glance, this seems like a good idea. It's easy to visualize the entire route hierarchy in one go. But this approach has a couple of big downsides that make it not ideal for large applications:
378
+
379
+ - **It's not very scalable**: As your application grows, the tree will grow and become harder to manage. And since it's all defined in one file, it can become very hard to maintain.
380
+ - **It's not great for code-splitting**: You'd have to manually code-split each component and then pass it into the \`component\` property of the route, further complicating the route configuration with an ever-growing route configuration file.
381
+
382
+ This only gets worse as you begin to use more features of the router, such as nested context, loaders, search param validation, etc.
383
+
384
+ > So, what's the best way to define my routes?
385
+
386
+ What we found to be the best way to define your routes is to abstract the definition of the route configuration outside of the route-tree. Then stitch together your route configurations into a single cohesive route-tree that is then passed into the \`createRouter\` function.
387
+
388
+ You can read more about [code-based routing](../routing/code-based-routing.md) to see how to define your routes in this way.
389
+
390
+ > [!TIP]
391
+ > Finding Code-based routing to be a bit too cumbersome? See why [file-based routing](#3-why-is-file-based-routing-the-preferred-way-to-define-routes) is the preferred way to define your routes.
392
+
393
+ ## 2. Declaring the Router instance for type inference
394
+
395
+ > Why do I have to declare the \`Router\`?
396
+
397
+ > This declaration stuff is way too complicated for me...
398
+
399
+ Once you've constructed your routes into a tree and passed it into your Router instance (using \`createRouter\`) with all the generics working correctly, you then need to somehow pass this information to the rest of your application.
400
+
401
+ There were two approaches we considered for this:
402
+
403
+ 1. **Imports**: You could import the \`Router\` instance from the file where you created it and use it directly in your components.
404
+
405
+ \`\`\`tsx
406
+ import { router } from '@/src/app'
407
+ export const PostsIdLink = () => {
408
+ return (
409
+ <Link<typeof router> to="/posts/$postId" params={{ postId: '123' }}>
410
+ Go to post 123
411
+ </Link>
412
+ )
413
+ }
414
+ \`\`\`
415
+
416
+ A downside to this approach is that you'd have to import the entire \`Router\` instance into every file where you want to use it. This can lead to increased bundle sizes and can be cumbersome to manage, and only get worse as your application grows and you use more features of the router.
417
+
418
+ 2. **Module declaration**: You can use TypeScript's module declaration to declare the \`Router\` instance as a module that can be used for type inference anywhere in your application without having to import it.
419
+
420
+ You'll do this once in your application.
421
+
422
+ \`\`\`tsx
423
+ // src/app.tsx
424
+ declare module '@tanstack/react-router' {
425
+ interface Register {
426
+ router: typeof router
427
+ }
428
+ }
429
+ \`\`\`
430
+
431
+ And then you can benefit from its auto-complete anywhere in your app without having to import it.
432
+
433
+ \`\`\`tsx
434
+ export const PostsIdLink = () => {
435
+ return (
436
+ <Link
437
+ to="/posts/$postId"
438
+ // ^? TypeScript will auto-complete this for you
439
+ params={{ postId: '123' }} // and this too!
440
+ >
441
+ Go to post 123
442
+ </Link>
443
+ )
444
+ }
445
+ \`\`\`
446
+
447
+ We went with **module declaration**, as it is what we found to be the most scalable and maintainable approach with the least amount of overhead and boilerplate.
448
+
449
+ ## 3. Why is file-based routing the preferred way to define routes?
450
+
451
+ > Why are the docs pushing for file-based routing?
452
+
453
+ > I'm used to defining my routes in a single file, why should I change?
454
+
455
+ Something you'll notice (quite soon) in the TanStack Router documentation is that we push for **file-based routing** as the preferred method for defining your routes. This is because we've found that file-based routing is the most scalable and maintainable way to define your routes.
456
+
457
+ > [!TIP]
458
+ > Before you continue, it's important you have a good understanding of [code-based routing](../routing/code-based-routing.md) and [file-based routing](../routing/file-based-routing.md).
459
+
460
+ As mentioned in the beginning, TanStack Router was designed for complex applications that require a high degree of type-safety and maintainability. And to achieve this, the configuration of the router has been done in a precise way that allows TypeScript to infer the types of your routes as much as possible.
461
+
462
+ A key difference in the set-up of a _basic_ application with TanStack Router, is that your route configurations require a function to be provided to \`getParentRoute\`, that returns the parent route of the current route.
463
+
464
+ \`\`\`tsx
465
+ import { createRoute } from '@tanstack/react-router'
466
+ import { postsRoute } from './postsRoute'
467
+
468
+ export const postsIndexRoute = createRoute({
469
+ getParentRoute: () => postsRoute,
470
+ path: '/',
471
+ })
472
+ \`\`\`
473
+
474
+ At this stage, this is done so the definition of \`postsIndexRoute\` can be aware of its location in the route tree and so that it can correctly infer the types of the \`context\`, \`path params\`, \`search params\` returned by the parent route. Incorrectly defining the \`getParentRoute\` function means that the properties of the parent route will not be correctly inferred by the child route.
475
+
476
+ As such, this is a critical part of the route configuration and a point of failure if not done correctly.
477
+
478
+ But this is only one part of setting up a basic application. TanStack Router requires all the routes (including the root route) to be stitched into a **_route-tree_** so that it may be passed into the \`createRouter\` function before declaring the \`Router\` instance on the module for type inference. This is another critical part of the route configuration and a point of failure if not done correctly.
479
+
480
+ > 🤯 If this route-tree were in its own file for an application with ~40-50 routes, it can easily grow up to 700+ lines.
481
+
482
+ \`\`\`tsx
483
+ const routeTree = rootRoute.addChildren([
484
+ postsRoute.addChildren([postsIndexRoute, postsIdRoute]),
485
+ ])
486
+ \`\`\`
487
+
488
+ This complexity only increases as you begin to use more features of the router, such as nested context, loaders, search param validation, etc. As such, it no longer becomes feasible to define your routes in a single file. And so, users end up building their own _semi consistent_ way of defining their routes across multiple files. This can lead to inconsistencies and errors in the route configuration.
489
+
490
+ Finally, comes the issue of code-splitting. As your application grows, you'll want to code-split your components to reduce the initial bundle size of your application. This can be a bit of a headache to manage when you're defining your routes in a single file or even across multiple files.
491
+
492
+ \`\`\`tsx
493
+ import { createRoute, lazyRouteComponent } from '@tanstack/react-router'
494
+ import { postsRoute } from './postsRoute'
495
+
496
+ export const postsIndexRoute = createRoute({
497
+ getParentRoute: () => postsRoute,
498
+ path: '/',
499
+ component: lazyRouteComponent(() => import('../page-components/posts/index')),
500
+ })
501
+ \`\`\`
502
+
503
+ All of this boilerplate, no matter how essential for providing a best-in-class type-inference experience, can be a bit overwhelming and can lead to inconsistencies and errors in the route configuration.
504
+
505
+ ... and this example configuration is just for rendering a single codes-split route. Imagine having to do this for 40-50 routes. Now remember that you still haven't touched the \`context\`, \`loaders\`, \`search param validation\`, and other features of the router 🤕.
506
+
507
+ > So, why's file-based routing the preferred way?
508
+
509
+ TanStack Router's file-based routing is designed to solve all of these issues. It allows you to define your routes in a predictable way that is easy to manage and maintain, and is scalable as your application grows.
510
+
511
+ The file-based routing approach is powered by the TanStack Router Bundler Plugin. It performs 3 essential tasks that solve the pain points in route configuration when using code-based routing:
512
+
513
+ 1. **Route configuration boilerplate**: It generates the boilerplate for your route configurations.
514
+ 2. **Route tree stitching**: It stitches together your route configurations into a single cohesive route-tree. Also in the background, it correctly updates the route configurations to define the \`getParentRoute\` function match the routes with their parent routes.
515
+ 3. **Code-splitting**: It automatically code-splits your route content components and updates the route configurations with the correct component. Additionally, at runtime, it ensures that the correct component is loaded when the route is visited.
516
+
517
+ Let's take a look at how the route configuration for the previous example would look like with file-based routing.
518
+
519
+ \`\`\`tsx
520
+ // src/routes/posts/index.ts
521
+ import { createFileRoute } from '@tanstack/react-router'
522
+
523
+ export const Route = createFileRoute('/posts/')({
524
+ component: () => 'Posts index component goes here!!!',
525
+ })
526
+ \`\`\`
527
+
528
+ That's it! No need to worry about defining the \`getParentRoute\` function, stitching together the route-tree, or code-splitting your components. The TanStack Router Bundler Plugin handles all of this for you.
529
+
530
+ At no point does the TanStack Router Bundler Plugin take away your control over your route configurations. It's designed to be as flexible as possible, allowing you to define your routes in a way that suits your application whilst reducing the boilerplate and complexity of the route configuration.
531
+
532
+ Check out the guides for [file-based routing](../routing/file-based-routing.md) and [code-splitting](../guide/code-splitting.md) for a more in-depth explanation of how they work in TanStack Router.
533
+
298
534
  # Devtools
299
535
 
300
536
  > Link, take this sword... I mean Devtools!... to help you on your way!
@@ -485,1015 +721,6 @@ function App() {
485
721
  - Specifies a Shadow DOM target for the devtools.
486
722
  - By default, devtool styles are applied to the \`<head>\` tag of the main document (light DOM). When a \`shadowDOMTarget\` is provided, styles will be applied within this Shadow DOM instead.
487
723
 
488
- # How to Migrate from React Router v7
489
-
490
- This guide provides a step-by-step process to migrate your application from React Router v7 to TanStack Router. We'll cover the complete migration process from removing React Router dependencies to implementing TanStack Router's type-safe routing patterns.
491
-
492
- ## Quick Start
493
-
494
- **Time Required:** 2-4 hours depending on app complexity
495
- **Difficulty:** Intermediate
496
- **Prerequisites:** Basic React knowledge, existing React Router v7 app
497
-
498
- ### What You'll Accomplish
499
-
500
- - Remove React Router v7 dependencies and components
501
- - Install and configure TanStack Router
502
- - Convert route definitions to file-based routing
503
- - Update navigation components and hooks
504
- - Implement type-safe routing patterns
505
- - Handle search params and dynamic routes
506
- - Migrate from React Router v7's new features to TanStack Router equivalents
507
-
508
- ---
509
-
510
- ## Complete Migration Process
511
-
512
- ### Step 1: Prepare for Migration
513
-
514
- Before making any changes, prepare your environment and codebase:
515
-
516
- **1.1 Create a backup branch**
517
-
518
- \`\`\`bash
519
- git checkout -b migrate-to-tanstack-router
520
- git push -u origin migrate-to-tanstack-router
521
- \`\`\`
522
-
523
- **1.2 Install TanStack Router (keep React Router temporarily)**
524
-
525
- \`\`\`bash
526
- # Install TanStack Router
527
- npm install @tanstack/react-router
528
-
529
- # Install development dependencies
530
- npm install -D @tanstack/router-plugin @tanstack/react-router-devtools
531
- \`\`\`
532
-
533
- **1.3 Set up the router plugin for your bundler**
534
-
535
- For **Vite** users, update your \`vite.config.ts\`:
536
-
537
- \`\`\`typescript
538
- import { defineConfig } from 'vite'
539
- import react from '@vitejs/plugin-react'
540
- import { tanstackRouter } from '@tanstack/router-plugin/vite'
541
-
542
- export default defineConfig({
543
- plugins: [
544
- tanstackRouter(), // Add this before react plugin
545
- react(),
546
- ],
547
- })
548
- \`\`\`
549
-
550
- For **other bundlers**, see our [bundler configuration guides](../routing/).
551
-
552
- ### Step 2: Create TanStack Router Configuration
553
-
554
- **2.1 Create router configuration file**
555
-
556
- Create \`tsr.config.json\` in your project root:
557
-
558
- \`\`\`json
559
- {
560
- "routesDirectory": "./src/routes",
561
- "generatedRouteTree": "./src/routeTree.gen.ts",
562
- "quoteStyle": "single"
563
- }
564
- \`\`\`
565
-
566
- **2.2 Create routes directory**
567
-
568
- \`\`\`bash
569
- mkdir src/routes
570
- \`\`\`
571
-
572
- ### Step 3: Convert Your React Router v7 Structure
573
-
574
- **3.1 Identify your current React Router v7 setup**
575
-
576
- React Router v7 introduced several new patterns. Look for:
577
-
578
- - \`createBrowserRouter\` with new data APIs
579
- - Framework mode configurations
580
- - Server-side rendering setup
581
- - New \`loader\` and \`action\` functions
582
- - \`defer\` usage (simplified in v7)
583
- - Type-safe routing features
584
-
585
- **3.2 Create root route**
586
-
587
- Create \`src/routes/__root.tsx\`:
588
-
589
- \`\`\`typescript
590
- import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
591
- import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
592
-
593
- export const Route = createRootRoute({
594
- component: () => (
595
- <>
596
- {/* Your existing layout/navbar content */}
597
- <div className="p-2 flex gap-2">
598
- <Link to="/" className="[&.active]:font-bold">
599
- Home
600
- </Link>
601
- <Link to="/about" className="[&.active]:font-bold">
602
- About
603
- </Link>
604
- </div>
605
- <hr />
606
- <Outlet />
607
- <TanStackRouterDevtools />
608
- </>
609
- ),
610
- })
611
- \`\`\`
612
-
613
- **3.3 Create index route**
614
-
615
- Create \`src/routes/index.tsx\` for your home page:
616
-
617
- \`\`\`typescript
618
- import { createFileRoute } from '@tanstack/react-router'
619
-
620
- export const Route = createFileRoute('/')({
621
- component: Index,
622
- })
623
-
624
- function Index() {
625
- return (
626
- <div className="p-2">
627
- <h3>Welcome Home!</h3>
628
- </div>
629
- )
630
- }
631
- \`\`\`
632
-
633
- **3.4 Convert React Router v7 loaders**
634
-
635
- React Router v7 simplified loader patterns. Here's how to migrate them:
636
-
637
- **React Router v7:**
638
-
639
- \`\`\`typescript
640
- // app/routes/posts.tsx
641
- export async function loader() {
642
- const posts = await fetchPosts()
643
- return { posts } // v7 removed need for json() wrapper
644
- }
645
-
646
- export default function Posts() {
647
- const { posts } = useLoaderData()
648
- return <div>{/* render posts */}</div>
649
- }
650
- \`\`\`
651
-
652
- **TanStack Router equivalent:**
653
- Create \`src/routes/posts.tsx\`:
654
-
655
- \`\`\`typescript
656
- import { createFileRoute } from '@tanstack/react-router'
657
-
658
- export const Route = createFileRoute('/posts')({
659
- loader: async () => {
660
- const posts = await fetchPosts()
661
- return { posts }
662
- },
663
- component: Posts,
664
- })
665
-
666
- function Posts() {
667
- const { posts } = Route.useLoaderData()
668
- return <div>{/* render posts */}</div>
669
- }
670
- \`\`\`
671
-
672
- **3.5 Convert dynamic routes**
673
-
674
- **React Router v7:**
675
-
676
- \`\`\`typescript
677
- // app/routes/posts.$postId.tsx
678
- export async function loader({ params }) {
679
- const post = await fetchPost(params.postId)
680
- return { post }
681
- }
682
-
683
- export default function Post() {
684
- const { post } = useLoaderData()
685
- return <div>{post.title}</div>
686
- }
687
- \`\`\`
688
-
689
- **TanStack Router equivalent:**
690
- Create \`src/routes/posts/$postId.tsx\`:
691
-
692
- \`\`\`typescript
693
- import { createFileRoute } from '@tanstack/react-router'
694
-
695
- export const Route = createFileRoute('/posts/$postId')({
696
- loader: async ({ params }) => {
697
- const post = await fetchPost(params.postId)
698
- return { post }
699
- },
700
- component: Post,
701
- })
702
-
703
- function Post() {
704
- const { post } = Route.useLoaderData()
705
- const { postId } = Route.useParams()
706
- return <div>{post.title}</div>
707
- }
708
- \`\`\`
709
-
710
- **3.6 Convert React Router v7 actions**
711
-
712
- **React Router v7:**
713
-
714
- \`\`\`typescript
715
- export async function action({ request, params }) {
716
- const formData = await request.formData()
717
- const result = await updatePost(params.postId, formData)
718
- return { success: true }
719
- }
720
- \`\`\`
721
-
722
- **TanStack Router equivalent:**
723
-
724
- \`\`\`typescript
725
- export const Route = createFileRoute('/posts/$postId/edit')({
726
- component: EditPost,
727
- // Actions are typically handled differently in TanStack Router
728
- // Use mutations or form libraries like React Hook Form
729
- })
730
-
731
- function EditPost() {
732
- const navigate = useNavigate()
733
-
734
- const handleSubmit = async (formData) => {
735
- const result = await updatePost(params.postId, formData)
736
- navigate({ to: '/posts/$postId', params: { postId } })
737
- }
738
-
739
- return <form onSubmit={handleSubmit}>{/* form */}</form>
740
- }
741
- \`\`\`
742
-
743
- ### Step 4: Handle React Router v7 Framework Features
744
-
745
- **4.1 Server-Side Rendering Migration**
746
-
747
- React Router v7 introduced framework mode with SSR. If you're using this:
748
-
749
- **React Router v7 Framework Mode:**
750
-
751
- \`\`\`typescript
752
- // react-router.config.ts
753
- export default {
754
- ssr: true,
755
- prerender: ['/'],
756
- }
757
- \`\`\`
758
-
759
- **TanStack Router approach:**
760
-
761
- TanStack Router has built-in SSR capabilities. Set up your router for SSR:
762
-
763
- \`\`\`typescript
764
- // src/router.tsx
765
- import { createRouter } from '@tanstack/react-router'
766
- import { routeTree } from './routeTree.gen'
767
-
768
- const router = createRouter({
769
- routeTree,
770
- context: {
771
- // Add any SSR context here
772
- },
773
- })
774
-
775
- declare module '@tanstack/react-router' {
776
- interface Register {
777
- router: typeof router
778
- }
779
- }
780
-
781
- export { router }
782
- \`\`\`
783
-
784
- For server-side rendering, use TanStack Router's built-in SSR APIs:
785
-
786
- \`\`\`typescript
787
- // server.tsx
788
- import { createMemoryHistory } from '@tanstack/react-router'
789
- import { StartServer } from '@tanstack/start/server'
790
-
791
- export async function render(url: string) {
792
- const router = createRouter({
793
- routeTree,
794
- history: createMemoryHistory({ initialEntries: [url] }),
795
- })
796
-
797
- await router.load()
798
-
799
- return (
800
- <StartServer router={router} />
801
- )
802
- }
803
- \`\`\`
804
-
805
- **4.2 Code Splitting Migration**
806
-
807
- React Router v7 improved code splitting. TanStack Router handles this via lazy routes:
808
-
809
- **React Router v7:**
810
-
811
- \`\`\`typescript
812
- const LazyComponent = lazy(() => import('./LazyComponent'))
813
- \`\`\`
814
-
815
- **TanStack Router:**
816
-
817
- \`\`\`typescript
818
- import { createLazyFileRoute } from '@tanstack/react-router'
819
-
820
- export const Route = createLazyFileRoute('/lazy-route')({
821
- component: LazyComponent,
822
- })
823
-
824
- function LazyComponent() {
825
- return <div>Lazy loaded!</div>
826
- }
827
- \`\`\`
828
-
829
- ### Step 5: Update Navigation Components
830
-
831
- **5.1 Update Link components**
832
-
833
- **React Router v7:**
834
-
835
- \`\`\`typescript
836
- import { Link } from 'react-router'
837
-
838
- <Link to="/posts/123">View Post</Link>
839
- <Link to="/posts" state={{ from: 'home' }}>Posts</Link>
840
- \`\`\`
841
-
842
- **TanStack Router:**
843
-
844
- \`\`\`typescript
845
- import { Link } from '@tanstack/react-router'
846
-
847
- <Link to="/posts/$postId" params={{ postId: '123' }}>View Post</Link>
848
- <Link to="/posts" state={{ from: 'home' }}>Posts</Link>
849
- \`\`\`
850
-
851
- **5.2 Update navigation hooks**
852
-
853
- **React Router v7:**
854
-
855
- \`\`\`typescript
856
- import { useNavigate } from 'react-router'
857
-
858
- function Component() {
859
- const navigate = useNavigate()
860
-
861
- const handleClick = () => {
862
- navigate('/posts/123')
863
- }
864
- }
865
- \`\`\`
866
-
867
- **TanStack Router:**
868
-
869
- \`\`\`typescript
870
- import { useNavigate } from '@tanstack/react-router'
871
-
872
- function Component() {
873
- const navigate = useNavigate()
874
-
875
- const handleClick = () => {
876
- navigate({ to: '/posts/$postId', params: { postId: '123' } })
877
- }
878
- }
879
- \`\`\`
880
-
881
- ### Step 6: Handle React Router v7 Specific Features
882
-
883
- **6.1 Migrate simplified \`defer\` usage**
884
-
885
- React Router v7 simplified defer by removing the wrapper function:
886
-
887
- **React Router v7:**
888
-
889
- \`\`\`typescript
890
- export async function loader() {
891
- return {
892
- data: fetchData(), // Promise directly returned
893
- }
894
- }
895
- \`\`\`
896
-
897
- **TanStack Router:**
898
-
899
- TanStack Router uses a different approach for deferred data. Use loading states:
900
-
901
- \`\`\`typescript
902
- export const Route = createFileRoute('/deferred')({
903
- loader: async () => {
904
- const data = await fetchData()
905
- return { data }
906
- },
907
- pendingComponent: () => <div>Loading...</div>,
908
- component: DeferredComponent,
909
- })
910
- \`\`\`
911
-
912
- **6.2 Handle React Router v7's enhanced type safety**
913
-
914
- React Router v7 improved type inference. TanStack Router provides even better type safety:
915
-
916
- \`\`\`typescript
917
- // TanStack Router automatically infers types
918
- export const Route = createFileRoute('/posts/$postId')({
919
- loader: async ({ params }) => {
920
- // params.postId is automatically typed as string
921
- const post = await fetchPost(params.postId)
922
- return { post }
923
- },
924
- component: Post,
925
- })
926
-
927
- function Post() {
928
- // post is automatically typed based on loader return
929
- const { post } = Route.useLoaderData()
930
- // postId is automatically typed as string
931
- const { postId } = Route.useParams()
932
- }
933
- \`\`\`
934
-
935
- ### Step 7: Update Your Main Router Setup
936
-
937
- **7.1 Replace React Router v7 router creation**
938
-
939
- **Before (React Router v7):**
940
-
941
- \`\`\`typescript
942
- import { createBrowserRouter, RouterProvider } from 'react-router'
943
-
944
- const router = createBrowserRouter([
945
- // Your route definitions
946
- ])
947
-
948
- ReactDOM.createRoot(document.getElementById('root')!).render(
949
- <React.StrictMode>
950
- <RouterProvider router={router} />
951
- </React.StrictMode>,
952
- )
953
- \`\`\`
954
-
955
- **After (TanStack Router):**
956
-
957
- \`\`\`typescript
958
- import { RouterProvider } from '@tanstack/react-router'
959
- import { router } from './router'
960
-
961
- ReactDOM.createRoot(document.getElementById('root')!).render(
962
- <React.StrictMode>
963
- <RouterProvider router={router} />
964
- </React.StrictMode>,
965
- )
966
- \`\`\`
967
-
968
- ### Step 8: Handle Search Parameters
969
-
970
- **8.1 React Router v7 to TanStack Router search params**
971
-
972
- **React Router v7:**
973
-
974
- \`\`\`typescript
975
- import { useSearchParams } from 'react-router'
976
-
977
- function Component() {
978
- const [searchParams, setSearchParams] = useSearchParams()
979
- const page = searchParams.get('page') || '1'
980
-
981
- const updatePage = (newPage) => {
982
- setSearchParams({ page: newPage })
983
- }
984
- }
985
- \`\`\`
986
-
987
- **TanStack Router:**
988
-
989
- \`\`\`typescript
990
- import { createFileRoute } from '@tanstack/react-router'
991
- import { z } from 'zod'
992
-
993
- const searchSchema = z.object({
994
- page: z.number().catch(1),
995
- filter: z.string().optional(),
996
- })
997
-
998
- export const Route = createFileRoute('/posts')({
999
- validateSearch: searchSchema,
1000
- component: Posts,
1001
- })
1002
-
1003
- function Posts() {
1004
- const navigate = useNavigate({ from: '/posts' })
1005
- const { page, filter } = Route.useSearch()
1006
-
1007
- const updatePage = (newPage: number) => {
1008
- navigate({ search: (prev) => ({ ...prev, page: newPage }) })
1009
- }
1010
- }
1011
- \`\`\`
1012
-
1013
- ### Step 9: Remove React Router Dependencies
1014
-
1015
- Only after everything is working with TanStack Router:
1016
-
1017
- **9.1 Remove React Router v7**
1018
-
1019
- \`\`\`bash
1020
- npm uninstall react-router
1021
- \`\`\`
1022
-
1023
- **9.2 Clean up unused imports**
1024
-
1025
- Search your codebase for any remaining React Router imports:
1026
-
1027
- \`\`\`bash
1028
- # Find remaining React Router imports
1029
- grep -r "react-router" src/
1030
- \`\`\`
1031
-
1032
- Remove any remaining imports and replace with TanStack Router equivalents.
1033
-
1034
- ### Step 10: Add Advanced Type Safety
1035
-
1036
- **10.1 Configure strict TypeScript**
1037
-
1038
- Update your \`tsconfig.json\`:
1039
-
1040
- \`\`\`json
1041
- {
1042
- "compilerOptions": {
1043
- "strict": true,
1044
- "noUncheckedIndexedAccess": true
1045
- }
1046
- }
1047
- \`\`\`
1048
-
1049
- **10.2 Add search parameter validation**
1050
-
1051
- For routes with search parameters, add validation schemas:
1052
-
1053
- \`\`\`typescript
1054
- import { createFileRoute } from '@tanstack/react-router'
1055
- import { z } from 'zod'
1056
-
1057
- const postsSearchSchema = z.object({
1058
- page: z.number().min(1).catch(1),
1059
- search: z.string().optional(),
1060
- category: z.enum(['tech', 'business', 'lifestyle']).optional(),
1061
- })
1062
-
1063
- export const Route = createFileRoute('/posts')({
1064
- validateSearch: postsSearchSchema,
1065
- component: Posts,
1066
- })
1067
- \`\`\`
1068
-
1069
- ---
1070
-
1071
- ## Production Checklist
1072
-
1073
- Before deploying your migrated application:
1074
-
1075
- ### Router Configuration
1076
-
1077
- - [ ] Router instance created and properly exported
1078
- - [ ] Route tree generated successfully
1079
- - [ ] TypeScript declarations registered
1080
- - [ ] All route files follow naming conventions
1081
-
1082
- ### Route Migration
1083
-
1084
- - [ ] All React Router v7 routes converted to file-based routing
1085
- - [ ] Dynamic routes updated with proper parameter syntax
1086
- - [ ] Nested routes maintain hierarchy
1087
- - [ ] Index routes created where needed
1088
- - [ ] Layout routes preserve component structure
1089
-
1090
- ### Feature Migration
1091
-
1092
- - [ ] All React Router v7 loaders converted
1093
- - [ ] Actions migrated to appropriate patterns
1094
- - [ ] Server-side rendering configured (if applicable)
1095
- - [ ] Code splitting implemented
1096
- - [ ] Type safety enhanced
1097
-
1098
- ### Navigation Updates
1099
-
1100
- - [ ] All Link components updated to TanStack Router
1101
- - [ ] useNavigate hooks replaced and tested
1102
- - [ ] Navigation parameters properly typed
1103
- - [ ] Search parameter validation implemented
1104
-
1105
- ### Code Cleanup
1106
-
1107
- - [ ] React Router v7 dependencies removed
1108
- - [ ] Unused imports cleaned up
1109
- - [ ] No React Router references remain
1110
- - [ ] TypeScript compilation successful
1111
- - [ ] All tests passing
1112
-
1113
- ### Testing
1114
-
1115
- - [ ] All routes accessible and rendering correctly
1116
- - [ ] Navigation between routes working
1117
- - [ ] Back/forward browser buttons functional
1118
- - [ ] Search parameters persisting correctly
1119
- - [ ] Dynamic routes with parameters working
1120
- - [ ] Nested route layouts displaying properly
1121
- - [ ] Framework features (SSR, code splitting) working if applicable
1122
-
1123
- ---
1124
-
1125
- ## Common Problems
1126
-
1127
- ### Error: "Cannot use useNavigate outside of context"
1128
-
1129
- **Problem:** You have remaining React Router imports that conflict with TanStack Router.
1130
-
1131
- **Solution:**
1132
-
1133
- 1. Search for all React Router imports:
1134
- \`\`\`bash
1135
- grep -r "react-router" src/
1136
- \`\`\`
1137
- 2. Replace all imports with TanStack Router equivalents
1138
- 3. Ensure React Router is completely uninstalled
1139
-
1140
- ### TypeScript Errors: Route Parameters
1141
-
1142
- **Problem:** TypeScript showing errors about route parameters not being typed correctly.
1143
-
1144
- **Solution:**
1145
-
1146
- 1. Ensure your router is registered in the TypeScript module declaration:
1147
- \`\`\`typescript
1148
- declare module '@tanstack/react-router' {
1149
- interface Register {
1150
- router: typeof router
1151
- }
1152
- }
1153
- \`\`\`
1154
- 2. Check that your route files export the Route correctly
1155
- 3. Verify parameter names match between route definition and usage
1156
-
1157
- ### React Router v7 Framework Features Not Working
1158
-
1159
- **Problem:** Missing SSR or code splitting functionality after migration.
1160
-
1161
- **Solution:**
1162
-
1163
- 1. TanStack Router has built-in SSR capabilities - use TanStack Start for full-stack applications
1164
- 2. Use TanStack Router's lazy routes for code splitting
1165
- 3. Configure SSR using TanStack Router's native APIs
1166
- 4. Follow the [SSR setup guide](../setup-ssr.md) for detailed instructions
1167
-
1168
- ### Routes Not Matching
1169
-
1170
- **Problem:** Routes not rendering or 404 errors for valid routes.
1171
-
1172
- **Solution:**
1173
-
1174
- 1. Check file naming follows TanStack Router conventions:
1175
- - Dynamic routes: \`$paramName.tsx\`
1176
- - Index routes: \`index.tsx\`
1177
- - Nested routes: proper directory structure
1178
- 2. Verify route tree generation is working
1179
- 3. Check that the router plugin is properly configured
1180
-
1181
- ### React Router v7 Simplified APIs Not Translating
1182
-
1183
- **Problem:** v7's simplified \`defer\` or other features don't have direct equivalents.
1184
-
1185
- **Solution:**
1186
-
1187
- 1. Use TanStack Router's pending states for loading UX
1188
- 2. Implement data fetching patterns that fit TanStack Router's architecture
1189
- 3. Leverage TanStack Router's superior type safety for better DX
1190
-
1191
- ---
1192
-
1193
- ## React Router v7 vs TanStack Router Feature Comparison
1194
-
1195
- | Feature | React Router v7 | TanStack Router |
1196
- | ------------------ | ------------------- | ---------------------------- |
1197
- | Type Safety | Good | Excellent |
1198
- | File-based Routing | Framework mode only | Built-in |
1199
- | Search Params | Basic | Validated with schemas |
1200
- | Code Splitting | Good | Excellent with lazy routes |
1201
- | SSR | Framework mode | Built-in with TanStack Start |
1202
- | Bundle Size | Larger | Smaller |
1203
- | Learning Curve | Moderate | Moderate |
1204
- | Community | Large | Growing |
1205
-
1206
- ---
1207
-
1208
- ## Common Next Steps
1209
-
1210
- After successfully migrating to TanStack Router, consider these enhancements:
1211
-
1212
- ### Advanced Features to Explore
1213
-
1214
- - **Route-based code splitting** - Improve performance with lazy loading
1215
- - **Search parameter validation** - Type-safe URL state management
1216
- - **Route preloading** - Enhance perceived performance
1217
- - **Route masking** - Advanced URL management
1218
- - **Integration with TanStack Query** - Powerful data fetching
1219
-
1220
- ---
1221
-
1222
- ## Related Resources
1223
-
1224
- - [TanStack Router Documentation](https://tanstack.com/router) - Complete API reference
1225
- - [File-Based Routing Guide](../../routing/file-based-routing.md) - Detailed routing concepts
1226
- - [Navigation Guide](../../guide/navigation.md) - Complete navigation patterns
1227
- - [Search Parameters Guide](../../guide/search-params.md) - Advanced search param usage
1228
- - [Type Safety Guide](../../guide/type-safety.md) - TypeScript integration details
1229
- - [React Router v7 Changelog](https://reactrouter.com/start/changelog) - What changed in v7
1230
-
1231
- # Migration from React Location
1232
-
1233
- Before you begin your journey in migrating from React Location, it's important that you have a good understanding of the [Routing Concepts](../routing/routing-concepts.md) and [Design Decisions](../decisions-on-dx.md) used by TanStack Router.
1234
-
1235
- ## Differences between React Location and TanStack Router
1236
-
1237
- React Location and TanStack Router share much of same design decisions concepts, but there are some key differences that you should be aware of.
1238
-
1239
- - React Location uses _generics_ to infer types for routes, while TanStack Router uses _module declaration merging_ to infer types.
1240
- - Route configuration in React Location is done using a single array of route definitions, while in TanStack Router, route configuration is done using a tree of route definitions starting with the [root route](../routing/routing-concepts.md#the-root-route).
1241
- - [File-based routing](../routing/file-based-routing.md) is the recommended way to define routes in TanStack Router, while React Location only allows you to define routes in a single file using a code-based approach.
1242
- - TanStack Router does support a [code-based approach](../routing/code-based-routing.md) to defining routes, but it is not recommended for most use cases. You can read more about why, over here: [why is file-based routing the preferred way to define routes?](../decisions-on-dx.md#3-why-is-file-based-routing-the-preferred-way-to-define-routes)
1243
-
1244
- ## Migration guide
1245
-
1246
- In this guide we'll go over the process of migrating the [React Location Basic example](https://github.com/TanStack/router/tree/react-location/examples/basic) over to TanStack Router using file-based routing, with the end goal of having the same functionality as the original example (styling and other non-routing related code will be omitted).
1247
-
1248
- > [!TIP]
1249
- > To use a code-based approach for defining your routes, you can read the [code-based Routing](../routing/code-based-routing.md) guide.
1250
-
1251
- ### Step 1: Swap over to TanStack Router's dependencies
1252
-
1253
- First, we need to install the dependencies for TanStack Router. For detailed installation instructions, see our [How to Install TanStack Router](../how-to/install.md) guide.
1254
-
1255
- \`\`\`sh
1256
- npm install @tanstack/react-router @tanstack/router-devtools
1257
- \`\`\`
1258
-
1259
- And remove the React Location dependencies.
1260
-
1261
- \`\`\`sh
1262
- npm uninstall @tanstack/react-location @tanstack/react-location-devtools
1263
- \`\`\`
1264
-
1265
- ### Step 2: Use the file-based routing watcher
1266
-
1267
- If your project uses Vite (or one of the supported bundlers), you can use the TanStack Router plugin to watch for changes in your routes files and automatically update the routes configuration.
1268
-
1269
- Installation of the Vite plugin:
1270
-
1271
- \`\`\`sh
1272
- npm install -D @tanstack/router-plugin
1273
- \`\`\`
1274
-
1275
- And add it to your \`vite.config.js\`:
1276
-
1277
- \`\`\`js
1278
- import { defineConfig } from 'vite'
1279
- import react from '@vitejs/plugin-react'
1280
- import { tanstackRouter } from '@tanstack/router-plugin/vite'
1281
-
1282
- export default defineConfig({
1283
- // ...
1284
- plugins: [tanstackRouter(), react()],
1285
- })
1286
- \`\`\`
1287
-
1288
- However, if your application does not use Vite, you use one of our other [supported bundlers](../routing/file-based-routing.md#getting-started-with-file-based-routing), or you can use the \`@tanstack/router-cli\` package to watch for changes in your routes files and automatically update the routes configuration.
1289
-
1290
- ### Step 3: Add the file-based configuration file to your project
1291
-
1292
- Create a \`tsr.config.json\` file in the root of your project with the following content:
1293
-
1294
- \`\`\`json
1295
- {
1296
- "routesDirectory": "./src/routes",
1297
- "generatedRouteTree": "./src/routeTree.gen.ts"
1298
- }
1299
- \`\`\`
1300
-
1301
- You can find the full list of options for the \`tsr.config.json\` file [here](../../../api/file-based-routing.md).
1302
-
1303
- ### Step 4: Create the routes directory
1304
-
1305
- Create a \`routes\` directory in the \`src\` directory of your project.
1306
-
1307
- \`\`\`sh
1308
- mkdir src/routes
1309
- \`\`\`
1310
-
1311
- ### Step 5: Create the root route file
1312
-
1313
- \`\`\`tsx
1314
- // src/routes/__root.tsx
1315
- import { createRootRoute, Outlet, Link } from '@tanstack/react-router'
1316
- import { TanStackRouterDevtools } from '@tanstack/router-devtools'
1317
-
1318
- export const Route = createRootRoute({
1319
- component: () => {
1320
- return (
1321
- <>
1322
- <div>
1323
- <Link to="/" activeOptions={{ exact: true }}>
1324
- Home
1325
- </Link>
1326
- <Link to="/posts">Posts</Link>
1327
- </div>
1328
- <hr />
1329
- <Outlet />
1330
- <TanStackRouterDevtools />
1331
- </>
1332
- )
1333
- },
1334
- })
1335
- \`\`\`
1336
-
1337
- ### Step 6: Create the index route file
1338
-
1339
- \`\`\`tsx
1340
- // src/routes/index.tsx
1341
- import { createFileRoute } from '@tanstack/react-router'
1342
-
1343
- export const Route = createFileRoute('/')({
1344
- component: Index,
1345
- })
1346
- \`\`\`
1347
-
1348
- > You will need to move any related components and logic needed for the index route from the \`src/index.tsx\` file to the \`src/routes/index.tsx\` file.
1349
-
1350
- ### Step 7: Create the posts route file
1351
-
1352
- \`\`\`tsx
1353
- // src/routes/posts.tsx
1354
- import { createFileRoute, Link, Outlet } from '@tanstack/react-router'
1355
-
1356
- export const Route = createFileRoute('/posts')({
1357
- component: Posts,
1358
- loader: async () => {
1359
- const posts = await fetchPosts()
1360
- return {
1361
- posts,
1362
- }
1363
- },
1364
- })
1365
-
1366
- function Posts() {
1367
- const { posts } = Route.useLoaderData()
1368
- return (
1369
- <div>
1370
- <nav>
1371
- {posts.map((post) => (
1372
- <Link
1373
- key={post.id}
1374
- to={\`/posts/$postId\`}
1375
- params={{ postId: post.id }}
1376
- >
1377
- {post.title}
1378
- </Link>
1379
- ))}
1380
- </nav>
1381
- <Outlet />
1382
- </div>
1383
- )
1384
- }
1385
- \`\`\`
1386
-
1387
- > You will need to move any related components and logic needed for the posts route from the \`src/index.tsx\` file to the \`src/routes/posts.tsx\` file.
1388
-
1389
- ### Step 8: Create the posts index route file
1390
-
1391
- \`\`\`tsx
1392
- // src/routes/posts.index.tsx
1393
- import { createFileRoute } from '@tanstack/react-router'
1394
-
1395
- export const Route = createFileRoute('/posts/')({
1396
- component: PostsIndex,
1397
- })
1398
- \`\`\`
1399
-
1400
- > You will need to move any related components and logic needed for the posts index route from the \`src/index.tsx\` file to the \`src/routes/posts.index.tsx\` file.
1401
-
1402
- ### Step 9: Create the posts id route file
1403
-
1404
- \`\`\`tsx
1405
- // src/routes/posts.$postId.tsx
1406
- import { createFileRoute } from '@tanstack/react-router'
1407
-
1408
- export const Route = createFileRoute('/posts/$postId')({
1409
- component: PostsId,
1410
- loader: async ({ params: { postId } }) => {
1411
- const post = await fetchPost(postId)
1412
- return {
1413
- post,
1414
- }
1415
- },
1416
- })
1417
-
1418
- function PostsId() {
1419
- const { post } = Route.useLoaderData()
1420
- // ...
1421
- }
1422
- \`\`\`
1423
-
1424
- > You will need to move any related components and logic needed for the posts id route from the \`src/index.tsx\` file to the \`src/routes/posts.$postId.tsx\` file.
1425
-
1426
- ### Step 10: Generate the route tree
1427
-
1428
- If you are using one of the supported bundlers, the route tree will be generated automatically when you run the dev script.
1429
-
1430
- If you are not using one of the supported bundlers, you can generate the route tree by running the following command:
1431
-
1432
- \`\`\`sh
1433
- npx tsr generate
1434
- \`\`\`
1435
-
1436
- ### Step 11: Update the main entry file to render the Router
1437
-
1438
- Once you've generated the route-tree, you can then update the \`src/index.tsx\` file to create the router instance and render it.
1439
-
1440
- \`\`\`tsx
1441
- // src/index.tsx
1442
- import React from 'react'
1443
- import ReactDOM from 'react-dom'
1444
- import { createRouter, RouterProvider } from '@tanstack/react-router'
1445
-
1446
- // Import the generated route tree
1447
- import { routeTree } from './routeTree.gen'
1448
-
1449
- // Create a new router instance
1450
- const router = createRouter({ routeTree })
1451
-
1452
- // Register the router instance for type safety
1453
- declare module '@tanstack/react-router' {
1454
- interface Register {
1455
- router: typeof router
1456
- }
1457
- }
1458
-
1459
- const domElementId = 'root' // Assuming you have a root element with the id 'root'
1460
-
1461
- // Render the app
1462
- const rootElement = document.getElementById(domElementId)
1463
- if (!rootElement) {
1464
- throw new Error(\`Element with id \${domElementId} not found\`)
1465
- }
1466
-
1467
- ReactDOM.createRoot(rootElement).render(
1468
- <React.StrictMode>
1469
- <RouterProvider router={router} />
1470
- </React.StrictMode>,
1471
- )
1472
- \`\`\`
1473
-
1474
- ### Finished!
1475
-
1476
- You should now have successfully migrated your application from React Location to TanStack Router using file-based routing.
1477
-
1478
- React Location also has a few more features that you might be using in your application. Here are some guides to help you migrate those features:
1479
-
1480
- - [Search params](../guide/search-params.md)
1481
- - [Data loading](../guide/data-loading.md)
1482
- - [History types](../guide/history-types.md)
1483
- - [Wildcard / Splat / Catch-all routes](../routing/routing-concepts.md#splat--catch-all-routes)
1484
- - [Authenticated routes](../guide/authenticated-routes.md)
1485
-
1486
- TanStack Router also has a few more features that you might want to explore:
1487
-
1488
- - [Router Context](../guide/router-context.md)
1489
- - [Preloading](../guide/preloading.md)
1490
- - [Pathless Layout Routes](../routing/routing-concepts.md#pathless-layout-routes)
1491
- - [Route masking](../guide/route-masking.md)
1492
- - [SSR](../guide/ssr.md)
1493
- - ... and more!
1494
-
1495
- If you are facing any issues or have any questions, feel free to ask for help in the TanStack Discord.
1496
-
1497
724
  # Frequently Asked Questions
1498
725
 
1499
726
  Welcome to the TanStack Router FAQ! Here you'll find answers to common questions about the TanStack Router. If you have a question that isn't answered here, please feel free to ask in the [TanStack Discord](https://tlinz.com/discord).