@tanstack/react-router
Version:
Modern and scalable routing for React applications
1 lines • 265 kB
TypeScript
declare const _default: "# Authenticated Routes\n\nAuthentication is an extremely common requirement for web applications. In this guide, we'll walk through how to use TanStack Router to build protected routes, and how to redirect users to login if they try to access them.\n\n## The `route.beforeLoad` Option\n\nThe `route.beforeLoad` option allows you to specify a function that will be called before a route is loaded. It receives all of the same arguments that the `route.loader` function does. This is a great place to check if a user is authenticated, and redirect them to a login page if they are not.\n\nThe `beforeLoad` function runs in relative order to these other route loading functions:\n\n- Route Matching (Top-Down)\n - `route.params.parse`\n - `route.validateSearch`\n- Route Loading (including Preloading)\n - **`route.beforeLoad`**\n - `route.onError`\n- Route Loading (Parallel)\n - `route.component.preload?`\n - `route.load`\n\n**It's important to know that the `beforeLoad` function for a route is called _before any of its child routes' `beforeLoad` functions_.** It is essentially a middleware function for the route and all of its children.\n\n**If you throw an error in `beforeLoad`, none of its children will attempt to load**.\n\n## Redirecting\n\nWhile not required, some authentication flows require redirecting to a login page. To do this, you can **throw a `redirect()`** from `beforeLoad`:\n\n```tsx\n// src/routes/_authenticated.tsx\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: async ({ location }) => {\n if (!isAuthenticated()) {\n throw redirect({\n to: '/login',\n search: {\n // Use the current location to power a redirect after login\n // (Do not use `router.state.resolvedLocation` as it can\n // potentially lag behind the actual current location)\n redirect: location.href,\n },\n })\n }\n },\n})\n```\n\n> [!TIP]\n> The `redirect()` function takes all of the same options as the `navigate` function, so you can pass options like `replace: true` if you want to replace the current history entry instead of adding a new one.\n\nOnce 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`:\n\n```tsx\nrouter.history.push(search.redirect)\n```\n\n## Non-Redirected Authentication\n\nSome applications choose to not redirect users to a login page, and instead keep the user on the same page and show a login form that either replaces the main content or hides it via a modal. This is also possible with TanStack Router by simply short circuiting rendering the `<Outlet />` that would normally render the child routes:\n\n```tsx\n// src/routes/_authenticated.tsx\nexport const Route = createFileRoute('/_authenticated')({\n component: () => {\n if (!isAuthenticated()) {\n return <Login />\n }\n\n return <Outlet />\n },\n})\n```\n\nThis keeps the user on the same page, but still allows you to render a login form. Once the user is authenticated, you can simply render the `<Outlet />` and the child routes will be rendered.\n\n## Authentication using React context/hooks\n\nIf your authentication flow relies on interactions with React context and/or hooks, you'll need to pass down your authentication state to TanStack Router using `router.context` option.\n\n> [!IMPORTANT]\n> React hooks are not meant to be consumed outside of React components. If you need to use a hook outside of a React component, you need to extract the returned state from the hook in a component that wraps your `<RouterProvider />` and then pass the returned value down to TanStack Router.\n\nWe'll cover the `router.context` options in-detail in the [Router Context](../router-context.md) section.\n\nHere'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).\n\n- `src/routes/__root.tsx`\n\n```tsx\nimport { createRootRouteWithContext } from '@tanstack/react-router'\n\ninterface MyRouterContext {\n // The ReturnType of your useAuth hook or the value of your AuthContext\n auth: AuthState\n}\n\nexport const Route = createRootRouteWithContext<MyRouterContext>()({\n component: () => <Outlet />,\n})\n```\n\n- `src/router.tsx`\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nimport { routeTree } from './routeTree.gen'\n\nexport const router = createRouter({\n routeTree,\n context: {\n // auth will initially be undefined\n // We'll be passing down the auth state from within a React component\n auth: undefined!,\n },\n})\n```\n\n- `src/App.tsx`\n\n```tsx\nimport { RouterProvider } from '@tanstack/react-router'\n\nimport { AuthProvider, useAuth } from './auth'\n\nimport { router } from './router'\n\nfunction InnerApp() {\n const auth = useAuth()\n return <RouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n <AuthProvider>\n <InnerApp />\n </AuthProvider>\n )\n}\n```\n\nThen 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.\n\n- `src/routes/dashboard.route.tsx`\n\n```tsx\nimport { createFileRoute, redirect } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/dashboard')({\n beforeLoad: ({ context, location }) => {\n if (!context.auth.isAuthenticated) {\n throw redirect({\n to: '/login',\n search: {\n redirect: location.href,\n },\n })\n }\n },\n})\n```\n\nYou can _optionally_, also use the [Non-Redirected Authentication](#non-redirected-authentication) approach to show a login form instead of calling a **redirect**.\n\nThis approach can also be used in conjunction with Pathless or Layout Route to protect all routes under their parent route.\n\n## Related How-To Guides\n\nFor detailed, step-by-step implementation guides, see:\n\n- [How to Set Up Basic Authentication](../../how-to/setup-authentication.md) - Complete setup with React Context and protected routes\n- [How to Integrate Authentication Providers](../../how-to/setup-auth-providers.md) - Use Auth0, Clerk, or Supabase\n- [How to Set Up Role-Based Access Control](../../how-to/setup-rbac.md) - Implement permissions and role-based routing\n\n## Examples\n\nWorking authentication examples are available in the repository:\n\n- [Basic Authentication Example](https://github.com/TanStack/router/tree/main/examples/react/authenticated-routes) - Simple authentication with context\n- [Firebase Authentication](https://github.com/TanStack/router/tree/main/examples/react/authenticated-routes-firebase) - Firebase Auth integration\n- [TanStack Start Auth Examples](https://github.com/TanStack/router/tree/main/examples/react) - Various auth implementations with TanStack Start\n\n# Automatic Code Splitting\n\nThe automatic code splitting feature in TanStack Router allows you to optimize your application's bundle size by lazily loading route components and their associated data. This is particularly useful for large applications where you want to minimize the initial load time by only loading the necessary code for the current route.\n\nTo turn this feature on, simply set the `autoCodeSplitting` option to `true` in your bundler plugin configuration. This enables the router to automatically handle code splitting for your routes without requiring any additional setup.\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n autoCodeSplitting: true, // Enable automatic code splitting\n }),\n ],\n})\n```\n\nBut that's just the beginning! TanStack Router's automatic code splitting is not only easy to enable, but it also provides powerful customization options to tailor how your routes are split into chunks. This allows you to optimize your application's performance based on your specific needs and usage patterns.\n\n## How does it work?\n\nTanStack Router's automatic code splitting works by transforming your route files both during 'development' and at 'build' time. It rewrites the route definitions to use lazy-loading wrappers for components and loaders, which allows the bundler to group these properties into separate chunks.\n\n> [!TIP]\n> A **chunk** is a file that contains a portion of your application's code, which can be loaded on demand. This helps reduce the initial load time of your application by only loading the code that is needed for the current route.\n\nSo when your application loads, it doesn't include all the code for every route. Instead, it only includes the code for the routes that are initially needed. As users navigate through your application, additional chunks are loaded on demand.\n\nThis happens seamlessly, without requiring you to manually split your code or manage lazy loading. The TanStack Router bundler plugin takes care of everything, ensuring that your routes are optimized for performance right out of the box.\n\n### The transformation process\n\nWhen you enable automatic code splitting, the bundler plugin does this by using static code analysis look at your the code in your route files to transform them into optimized outputs.\n\nThis transformation process produces two key outputs when each of your route files are processed:\n\n1. **Reference File**: The bundler plugin takes your original route file (e.g., `posts.route.tsx`) and modifies the values for properties like `component` or `pendingComponent` to use special lazy-loading wrappers that'll fetch the actual code later. These wrappers point to a \"virtual\" file that the bundler will resolve later on.\n2. **Virtual File**: When the bundler sees a request for one of these virtual files (e.g., `posts.route.tsx?tsr-split=component`), it intercepts it to generate a new, minimal on-the-fly file that _only_ contains the code for the requested properties (e.g., just the `PostsComponent`).\n\nThis process ensures that your original code remains clean and readable, while the actual bundled output is optimized for initial bundle size.\n\n### What gets code split?\n\nThe decision of what to split into separate chunks is crucial for optimizing your application's performance. TanStack Router uses a concept called \"**Split Groupings**\" to determine how different parts of your route should be bundled together.\n\nSplit groupings are arrays of properties that tell TanStack Router how to bundle different parts of your route together. Each grouping is an list of property names that you want to bundle together into a single lazy-loaded chunk.\n\nThe available properties to split are:\n\n- `component`\n- `errorComponent`\n- `pendingComponent`\n- `notFoundComponent`\n- `loader`\n\nBy default, TanStack Router uses the following split groupings:\n\n```sh\n[\n ['component'],\n ['errorComponent'],\n ['notFoundComponent']\n]\n```\n\nThis means that it creates three separate lazy-loaded chunks for each route. Resulting in:\n\n- One for the main component\n- One for the error component\n- And one for the not-found component.\n\n### Rules of Splitting\n\nFor automatic code splitting to work, there are some rules in-place to make sure that this process can reliably and predictably happen.\n\n#### Do not export route properties\n\nRoute 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.\n\n```tsx\nimport { createRoute } from '@tanstack/react-router'\n\nexport const Route = createRoute('/posts')({\n // ...\n notFoundComponent: PostsNotFoundComponent,\n})\n\n// \u274C Do NOT do this!\n// Exporting the notFoundComponent will prevent it from being code-split\n// and will be included in the main bundle.\nexport function PostsNotFoundComponent() {\n // \u274C\n // ...\n}\n\nfunction PostsNotFoundComponent() {\n // \u2705\n // ...\n}\n```\n\n**That's it!** There are no other restrictions. You can use any other JavaScript or TypeScript features in your route files as you normally would. If you run into any issues, please [open an issue](https://github.com/tanstack/router/issues) on GitHub.\n\n## Granular control\n\nFor most applications, the default behavior of using `autoCodeSplitting: true` is sufficient. However, TanStack Router provides several options to customize how your routes are split into chunks, allowing you to optimize for specific use cases or performance needs.\n\n### Global code splitting behavior (`defaultBehavior`)\n\nYou can change how TanStack Router splits your routes by changing the `defaultBehavior` option in your bundler plugin configuration. This allows you to define how different properties of your routes should be bundled together.\n\nFor example, to bundle all UI-related components into a single chunk, you could configure it like this:\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n autoCodeSplitting: true,\n codeSplittingOptions: {\n defaultBehavior: [\n [\n 'component',\n 'pendingComponent',\n 'errorComponent',\n 'notFoundComponent',\n ], // Bundle all UI components together\n ],\n },\n }),\n ],\n})\n```\n\n### Advanced programmatic control (`splitBehavior`)\n\nFor 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.\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n autoCodeSplitting: true,\n codeSplittingOptions: {\n splitBehavior: ({ routeId }) => {\n // For all routes under /posts, bundle the loader and component together\n if (routeId.startsWith('/posts')) {\n return [['loader', 'component']]\n }\n // All other routes will use the `defaultBehavior`\n },\n },\n }),\n ],\n})\n```\n\n### Per-route overrides (`codeSplitGroupings`)\n\nFor 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.\n\n```tsx\n// src/routes/posts.route.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { loadPostsData } from './-heavy-posts-utils'\n\nexport const Route = createFileRoute('/posts')({\n // For this specific route, bundle the loader and component together.\n codeSplitGroupings: [['loader', 'component']],\n loader: () => loadPostsData(),\n component: PostsComponent,\n})\n\nfunction PostsComponent() {\n // ...\n}\n```\n\nThis will create a single chunk that includes both the `loader` and the `component` for this specific route, overriding both the default behavior and any programmatic split behavior defined in your bundler config.\n\n### Configuration order matters\n\nThis guide has so far describe three different ways to configure how TanStack Router splits your routes into chunks.\n\nTo make sure that the different configurations do not conflict with each other, TanStack Router uses the following order of precedence:\n\n1. **Per-route overrides**: The `codeSplitGroupings` property inside a route file takes the highest precedence. This allows you to define specific split groupings for individual routes.\n2. **Programmatic split behavior**: The `splitBehavior` function in your bundler config allows you to define custom logic for how routes should be split based on their `routeId`.\n3. **Default behavior**: The `defaultBehavior` option in your bundler config serves as the fallback for any routes that do not have specific overrides or custom logic defined. This is the base configuration that applies to all routes unless overridden.\n\n### Splitting the Data Loader\n\nThe `loader` function is responsible for fetching data needed by the route. By default, it is bundled with into your \"reference file\" and loaded in the initial bundle. However, you can also split the `loader` into its own chunk if you want to optimize further.\n\n> [!CAUTION]\n> Moving the `loader` into its own chunk is a **performance trade-off**. It introduces an additional trip to the server before the data can be fetched, which can lead to slower initial page loads. This is because the `loader` **must** be fetched and executed before the route can render its component.\n> Therefore, we recommend keeping the `loader` in the initial bundle unless you have a specific reason to split it.\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n autoCodeSplitting: true,\n codeSplittingOptions: {\n defaultBehavior: [\n ['loader'], // The loader will be in its own chunk\n ['component'],\n // ... other component groupings\n ],\n },\n }),\n ],\n})\n```\n\nWe highly discourage splitting the `loader` unless you have a specific use case that requires it. In most cases, not splitting off the `loader` and keep it in the main bundle is the best choice for performance.\n\n# Code Splitting\n\nCode splitting and lazy loading is a powerful technique for improving the bundle size and load performance of an application.\n\n- Reduces the amount of code that needs to be loaded on initial page load\n- Code is loaded on-demand when it is needed\n- Results in more chunks that are smaller in size that can be cached more easily by the browser.\n\n## How does TanStack Router split code?\n\nTanStack Router separates code into two categories:\n\n- **Critical Route Configuration** - The code that is required to render the current route and kick off the data loading process as early as possible.\n - Path Parsing/Serialization\n - Search Param Validation\n - Loaders, Before Load\n - Route Context\n - Static Data\n - Links\n - Scripts\n - Styles\n - All other route configuration not listed below\n\n- **Non-Critical/Lazy Route Configuration** - The code that is not required to match the route, and can be loaded on-demand.\n - Route Component\n - Error Component\n - Pending Component\n - Not-found Component\n\n> \uD83E\uDDE0 **Why is the loader not split?**\n>\n> - The loader is already an asynchronous boundary, so you pay double to both get the chunk _and_ wait for the loader to execute.\n> - Categorically, it is less likely to contribute to a large bundle size than a component.\n> - The loader is one of the most important preloadable assets for a route, especially if you're using a default preload intent, like hovering over a link, so it's important for the loader to be available without any additional async overhead.\n>\n> Knowing the disadvantages of splitting the loader, if you still want to go ahead with it, head over to the [Data Loader Splitting](#data-loader-splitting) section.\n\n## Encapsulating a route's files into a directory\n\nSince TanStack Router's file-based routing system is designed to support both flat and nested file structures, it's possible to encapsulate a route's files into a single directory without any additional configuration.\n\nTo encapsulate a route's files into a directory, move the route file itself into a `.route` file within a directory with the same name as the route file.\n\nFor example, if you have a route file named `posts.tsx`, you would create a new directory named `posts` and move the `posts.tsx` file into that directory, renaming it to `route.tsx`.\n\n**Before**\n\n- `posts.tsx`\n\n**After**\n\n- `posts`\n - `route.tsx`\n\n## Approaches to code splitting\n\nTanStack Router supports multiple approaches to code splitting. If you are using code-based routing, skip to the [Code-Based Splitting](#code-based-splitting) section.\n\nWhen you are using file-based routing, you can use the following approaches to code splitting:\n\n- [Using automatic code-splitting \u2728](#using-automatic-code-splitting)\n- [Using the `.lazy.tsx` suffix](#using-the-lazytsx-suffix)\n- [Using Virtual Routes](#using-virtual-routes)\n\n## Using automatic code-splitting\u2728\n\nThis is the easiest and most powerful way to code split your route files.\n\nWhen using the `autoCodeSplitting` feature, TanStack Router will automatically code split your route files based on the non-critical route configuration mentioned above.\n\n> [!IMPORTANT]\n> The automatic code-splitting feature is **ONLY** available when you are using file-based routing with one of our [supported bundlers](../../routing/file-based-routing.md#getting-started-with-file-based-routing).\n> This will **NOT** work if you are **only** using the CLI (`@tanstack/router-cli`).\n\nTo enable automatic code-splitting, you just need to add the following to the configuration of your TanStack Router Bundler Plugin:\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n // ...\n autoCodeSplitting: true,\n }),\n react(), // Make sure to add this plugin after the TanStack Router Bundler plugin\n ],\n})\n```\n\nThat's it! TanStack Router will automatically code-split all your route files by their critical and non-critical route configurations.\n\nIf you want more control over the code-splitting process, head over to the [Automatic Code Splitting](../automatic-code-splitting.md) guide to learn more about the options available.\n\n## Using the `.lazy.tsx` suffix\n\nIf you are not able to use the automatic code-splitting feature, you can still code-split your route files using the `.lazy.tsx` suffix. It is **as easy as moving your code into a separate file with a `.lazy.tsx` suffix** and using the `createLazyFileRoute` function instead of `createFileRoute`.\n\n> [!IMPORTANT]\n> The `__root.tsx` route file, using either `createRootRoute` or `createRootRouteWithContext`, does not support code splitting, since it's always rendered regardless of the current route.\n\nThese are the only options that `createLazyFileRoute` support:\n\n| Export Name | Description |\n| ------------------- | --------------------------------------------------------------------- |\n| `component` | The component to render for the route. |\n| `errorComponent` | The component to render when an error occurs while loading the route. |\n| `pendingComponent` | The component to render while the route is loading. |\n| `notFoundComponent` | The component to render if a not-found error gets thrown. |\n\n### Example code splitting with `.lazy.tsx`\n\nWhen you are using `.lazy.tsx` you can split your route into two files to enable code splitting:\n\n**Before (Single File)**\n\n```tsx\n// src/routes/posts.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { fetchPosts } from './api'\n\nexport const Route = createFileRoute('/posts')({\n loader: fetchPosts,\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\n```\n\n**After (Split into two files)**\n\nThis file would contain the critical route configuration:\n\n```tsx\n// src/routes/posts.tsx\n\nimport { createFileRoute } from '@tanstack/react-router'\nimport { fetchPosts } from './api'\n\nexport const Route = createFileRoute('/posts')({\n loader: fetchPosts,\n})\n```\n\nWith the non-critical route configuration going into the file with the `.lazy.tsx` suffix:\n\n```tsx\n// src/routes/posts.lazy.tsx\nimport { createLazyFileRoute } from '@tanstack/react-router'\n\nexport const Route = createLazyFileRoute('/posts')({\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\n```\n\n## Using Virtual Routes\n\nYou 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.\n\n**Before (Virtual Routes)**\n\n```tsx\n// src/routes/posts.tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts')({\n // Hello?\n})\n```\n\n```tsx\n// src/routes/posts.lazy.tsx\nimport { createLazyFileRoute } from '@tanstack/react-router'\n\nexport const Route = createLazyFileRoute('/posts')({\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\n```\n\n**After (Virtual Routes)**\n\n```tsx\n// src/routes/posts.lazy.tsx\nimport { createLazyFileRoute } from '@tanstack/react-router'\n\nexport const Route = createLazyFileRoute('/posts')({\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\n```\n\nTada! \uD83C\uDF89\n\n## Code-Based Splitting\n\nIf you are using code-based routing, you can still code-split your routes using the `Route.lazy()` method and the `createLazyRoute` function. You'll need to split your route configuration into two parts:\n\nCreate a lazy route using the `createLazyRoute` function.\n\n```tsx\n// src/posts.lazy.tsx\nexport const Route = createLazyRoute('/posts')({\n component: MyComponent,\n})\n\nfunction MyComponent() {\n return <div>My Component</div>\n}\n```\n\nThen, 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.\n\n```tsx\n// src/app.tsx\nconst postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/posts',\n}).lazy(() => import('./posts.lazy').then((d) => d.Route))\n```\n\n## Data Loader Splitting\n\n**Be warned!!!** Splitting a route loader is a dangerous game.\n\nIt can be a powerful tool to reduce bundle size, but it comes with a cost as mentioned in the [How does TanStack Router split code?](#how-does-tanstack-router-split-code) section.\n\nYou 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:\n\n```tsx\nimport { lazyFn } from '@tanstack/react-router'\n\nconst route = createRoute({\n path: '/my-route',\n component: MyComponent,\n loader: lazyFn(() => import('./loader'), 'loader'),\n})\n\n// In another file...a\nexport const loader = async (context: LoaderContext) => {\n /// ...\n}\n```\n\nIf 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.\n\n## Manually accessing Route APIs in other files with the `getRouteApi` helper\n\nAs 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.\n\n- `my-route.tsx`\n\n```tsx\nimport { createRoute } from '@tanstack/react-router'\nimport { MyComponent } from './MyComponent'\n\nconst route = createRoute({\n path: '/my-route',\n loader: () => ({\n foo: 'bar',\n }),\n component: MyComponent,\n})\n```\n\n- `MyComponent.tsx`\n\n```tsx\nimport { getRouteApi } from '@tanstack/react-router'\n\nconst route = getRouteApi('/my-route')\n\nexport function MyComponent() {\n const loaderData = route.useLoaderData()\n // ^? { foo: string }\n\n return <div>...</div>\n}\n```\n\nThe `getRouteApi` function is useful for accessing other type-safe APIs:\n\n- `useLoaderData`\n- `useLoaderDeps`\n- `useMatch`\n- `useParams`\n- `useRouteContext`\n- `useSearch`\n\n# Creating a Router\n\n## The `Router` Class\n\nWhen 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.\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n})\n```\n\n## Route Tree\n\nYou'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.\n\nWhether you used [file-based routing](../../routing/file-based-routing.md) or [code-based routing](../../routing/code-based-routing.md), you'll need to pass your route tree to the `createRouter` function:\n\n### Filesystem Route Tree\n\nIf you used our recommended file-based routing, then it's likely your generated route tree file was created at the default `src/routeTree.gen.ts` location. If you used a custom location, then you'll need to import your route tree from that location.\n\n```tsx\nimport { routeTree } from './routeTree.gen'\n```\n\n### Code-Based Route Tree\n\nIf you used code-based routing, then you likely created your route tree manually using the root route's `addChildren` method:\n\n```tsx\nconst routeTree = rootRoute.addChildren([\n // ...\n])\n```\n\n## Router Type Safety\n\n> [!IMPORTANT]\n> DO NOT SKIP THIS SECTION! \u26A0\uFE0F\n\nTanStack 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:\n\n```tsx\ndeclare module '@tanstack/react-router' {\n interface Register {\n // This infers the type of our router and registers it across your entire project\n router: typeof router\n }\n}\n```\n\nWith your router registered, you'll now get type-safety across your entire project for anything related to routing.\n\n## 404 Not Found Route\n\nAs promised in earlier guides, we'll now cover the `notFoundRoute` option. This option is used to configure a route that will render when no other suitable match is found. This is useful for rendering a 404 page or redirecting to a default route.\n\nIf you are using either file-based or code-based routing, then you'll need to add a `notFoundComponent` key to `createRootRoute`:\n\n```tsx\nexport const Route = createRootRoute({\n component: () => (\n // ...\n ),\n notFoundComponent: () => <div>404 Not Found</div>,\n});\n```\n\n## Other Options\n\nThere are many other options that can be passed to the `Router` constructor. You can find a full list of them in the [API Reference](../../api/router/RouterOptionsType.md).\n\n# Custom Link\n\nWhile repeating yourself can be acceptable in many situations, you might find that you do it too often. At times, you may want to create cross-cutting components with additional behavior or styles. You might also consider using third-party libraries in combination with TanStack Router's type safety.\n\n## `createLink` for cross-cutting concerns\n\n`createLink` creates a custom `Link` component with the same type parameters as `Link`. This means you can create your own component which provides the same type safety and typescript performance as `Link`.\n\n### Basic example\n\nIf you want to create a basic custom link component, you can do so with the following:\n\n[//]: # 'BasicExampleImplementation'\n\n```tsx\nimport * as React from 'react'\nimport { createLink, LinkComponent } from '@tanstack/react-router'\n\ninterface BasicLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n // Add any additional props you want to pass to the anchor element\n}\n\nconst BasicLinkComponent = React.forwardRef<HTMLAnchorElement, BasicLinkProps>(\n (props, ref) => {\n return (\n <a ref={ref} {...props} className={'block px-3 py-2 text-blue-700'} />\n )\n },\n)\n\nconst CreatedLinkComponent = createLink(BasicLinkComponent)\n\nexport const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {\n return <CreatedLinkComponent preload={'intent'} {...props} />\n}\n```\n\n[//]: # 'BasicExampleImplementation'\n\nYou can then use your newly created `Link` component as any other `Link`\n\n```tsx\n<CustomLink to={'/dashboard/invoices/$invoiceId'} params={{ invoiceId: 0 }} />\n```\n\n[//]: # 'ExamplesUsingThirdPartyLibs'\n\n## `createLink` with third party libraries\n\nHere are some examples of how you can use `createLink` with third-party libraries.\n\n### React Aria Components example\n\nReact 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.\n\n```tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Link as RACLink, MenuItem } from 'react-aria-components'\n\nexport const Link = createLink(RACLink)\nexport const MenuItemLink = createLink(MenuItem)\n```\n\nTo use React Aria's render props, including the `className`, `style`, and `children` functions, create a wrapper component and pass that to `createLink`.\n\n```tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Link as RACLink, type LinkProps } from 'react-aria-components'\n\ninterface MyLinkProps extends LinkProps {\n // your props\n}\n\nfunction MyLink(props: MyLinkProps) {\n return (\n <RACLink\n {...props}\n style={({ isHovered }) => ({\n color: isHovered ? 'red' : 'blue',\n })}\n />\n )\n}\n\nexport const Link = createLink(MyLink)\n```\n\n### Chakra UI example\n\n```tsx\nimport * as React from 'react'\nimport { createLink, LinkComponent } from '@tanstack/react-router'\nimport { Link } from '@chakra-ui/react'\n\ninterface ChakraLinkProps\n extends Omit<React.ComponentPropsWithoutRef<typeof Link>, 'href'> {\n // Add any additional props you want to pass to the link\n}\n\nconst ChakraLinkComponent = React.forwardRef<\n HTMLAnchorElement,\n ChakraLinkProps\n>((props, ref) => {\n return <Link ref={ref} {...props} />\n})\n\nconst CreatedLinkComponent = createLink(ChakraLinkComponent)\n\nexport const CustomLink: LinkComponent<typeof ChakraLinkComponent> = (\n props,\n) => {\n return (\n <CreatedLinkComponent\n textDecoration={'underline'}\n _hover={{ textDecoration: 'none' }}\n _focus={{ textDecoration: 'none' }}\n preload={'intent'}\n {...props}\n />\n )\n}\n```\n\n### MUI example\n\nThere is an [example](https://github.com/TanStack/router/tree/main/examples/react/start-material-ui) available which uses these patterns.\n\n#### `Link`\n\nIf the MUI `Link` should simply behave like the router `Link`, it can be just wrapped with `createLink`:\n\n```tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Link } from '@mui/material'\n\nexport const CustomLink = createLink(Link)\n```\n\nIf the `Link` should be customized this approach can be used:\n\n```tsx\nimport React from 'react'\nimport { createLink } from '@tanstack/react-router'\nimport { Link } from '@mui/material'\nimport type { LinkProps } from '@mui/material'\nimport type { LinkComponent } from '@tanstack/react-router'\n\ninterface MUILinkProps extends LinkProps {\n // Add any additional props you want to pass to the Link\n}\n\nconst MUILinkComponent = React.forwardRef<HTMLAnchorElement, MUILinkProps>(\n (props, ref) => <Link ref={ref} {...props} />,\n)\n\nconst CreatedLinkComponent = createLink(MUILinkComponent)\n\nexport const CustomLink: LinkComponent<typeof MUILinkComponent> = (props) => {\n return <CreatedLinkComponent preload={'intent'} {...props} />\n}\n\n// Can also be styled\n```\n\n#### `Button`\n\nIf a `Button` should be used as a router `Link`, the `component` should be set as `a`:\n\n```tsx\nimport React from 'react'\nimport { createLink } from '@tanstack/react-router'\nimport { Button } from '@mui/material'\nimport type { ButtonProps } from '@mui/material'\nimport type { LinkComponent } from '@tanstack/react-router'\n\ninterface MUIButtonLinkProps extends ButtonProps<'a'> {\n // Add any additional props you want to pass to the Button\n}\n\nconst MUIButtonLinkComponent = React.forwardRef<\n HTMLAnchorElement,\n MUIButtonLinkProps\n>((props, ref) => <Button ref={ref} component=\"a\" {...props} />)\n\nconst CreatedButtonLinkComponent = createLink(MUIButtonLinkComponent)\n\nexport const CustomButtonLink: LinkComponent<typeof MUIButtonLinkComponent> = (\n props,\n) => {\n return <CreatedButtonLinkComponent preload={'intent'} {...props} />\n}\n```\n\n#### Usage with `styled`\n\nAny of these MUI approaches can then be used with `styled`:\n\n```tsx\nimport { css, styled } from '@mui/material'\nimport { CustomLink } from './CustomLink'\n\nconst StyledCustomLink = styled(CustomLink)(\n ({ theme }) => css`\n color: ${theme.palette.common.white};\n `,\n)\n```\n\n### Mantine example\n\n```tsx\nimport * as React from 'react'\nimport { createLink, LinkComponent } from '@tanstack/react-router'\nimport { Anchor, AnchorProps } from '@mantine/core'\n\ninterface MantineAnchorProps extends Omit<AnchorProps, 'href'> {\n // Add any additional props you want to pass to the anchor\n}\n\nconst MantineLinkComponent = React.forwardRef<\n HTMLAnchorElement,\n MantineAnchorProps\n>((props, ref) => {\n return <Anchor ref={ref} {...props} />\n})\n\nconst CreatedLinkComponent = createLink(MantineLinkComponent)\n\nexport const CustomLink: LinkComponent<typeof MantineLinkComponent> = (\n props,\n) => {\n return <CreatedLinkComponent preload=\"intent\" {...props} />\n}\n```\n\n[//]: # 'ExamplesUsingThirdPartyLibs'\n\n# Custom Search Param Serialization\n\nBy default, TanStack Router parses and serializes your URL Search Params automatically using `JSON.stringify` and `JSON.parse`. This process involves escaping and unescaping the search string, which is a common practice for URL search params, in addition to the serialization and deserialization of the search object.\n\nFor instance, using the default configuration, if you have the following search object:\n\n```tsx\nconst search = {\n page: 1,\n sort: 'asc',\n filters: { author: 'tanner', min_words: 800 },\n}\n```\n\nIt would be serialized and escaped into the following search string:\n\n```txt\n?page=1&sort=asc&filters=%7B%22author%22%3A%22tanner%22%2C%22min_words%22%3A800%7D\n```\n\nWe can implement the default behavior with the following code:\n\n```tsx\nimport {\n createRouter,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n parseSearch: parseSearchWith(JSON.parse),\n stringifySearch: stringifySearchWith(JSON.stringify),\n})\n```\n\nHowever, 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/).\n\nThis 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.\n\n> [!TIP]\n> An important aspect of serialization and deserialization, is that you are able to get the same object back after deserialization. This is important because if the serialization and deserialization process is not done correctly, you may lose some information. For example, if you are using a library that does not support nested objects, you may lose the nested object when deserializing the search string.\n\n\n\nHere are some examples of how you can customize the search param serialization in TanStack Router:\n\n## Using Base64\n\nIt'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:\n\n```tsx\nimport {\n Router,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/react-router'\n\nconst router = createRouter({\n parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),\n stringifySearch: stringifySearchWith((value) =>\n encodeToBinary(JSON.stringify(value)),\n ),\n})\n\nfunction decodeFromBinary(str: string): string {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(str), function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join(''),\n )\n}\n\nfunction encodeToBinary(str: string): string {\n return btoa(\n encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {\n return String.fromCharCode(parseInt(p1, 16))\n }),\n )\n}\n```\n\n> [\u26A0\uFE0F Why does this snippet not use atob/btoa?](#safe-binary-encodingdecoding)\n\nSo, if we were to turn the previous object into a search string using this configuration, it would look like this:\n\n```txt\n?page=1&sort=asc&filters=eyJhdXRob3IiOiJ0YW5uZXIiLCJtaW5fd29yZHMiOjgwMH0%3D\n```\n\n> [!WARNING]\n> If you are serializing user input into Base64, you run the risk of causing a collision with the URL deserialization. This can lead to unexpected behavior, such as the URL not being parsed correctly or being interpreted as a different value. To avoid this, you should encode the search params using a safe binary encoding/decoding method (see below).\n\n## Using the query-string library\n\nThe [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:\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\nimport qs from 'query-string'\n\nconst router = createRouter({\n // ...\n stringifySearch: stringifySearchWith((value) =>\n qs.stringify(value, {\n // ...options\n }),\n ),\n parseSearch: parseSearchWith((value) =>\n qs.parse(value, {\n // ...options\n }),\n ),\n})\n```\n\nSo, if we were to turn the previous object into a search string using this configuration, it would look like this:\n\n```txt\n?page=1&sort=asc&filters=author%3Dtanner%26min_words%3D800\n```\n\n## Using the JSURL2 library\n\n[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:\n\n```tsx\nimport {\n Router,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/react-router'\nimport { parse, stringify } from 'jsurl2'\n\nconst router = createRouter({\n // ...\n parseSearch: parseSearchWith(parse),\n stringifySearch: stringifySearchWith(stringify),\n})\n```\n\nSo, if we were to turn the previous object into a search string using this configuration, it would look like this:\n\n```txt\n?page=1&sort=asc&filters=(author~tanner~min*_words~800)~\n```\n\n## Using the Zipson library\n\n[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:\n\n```tsx\nimport {\n Router,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/react-router'\nimport { stringify, parse } from 'zipson'\n\nconst router = createRouter({\n parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),\n stringifySearch: stringifySearchWith((value) =>\n encodeToBinary(stringify(value)),\n ),\n})\n\nfunction decodeFromBinary(str: string): string {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(str), function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join(''),\n )\n}\n\nfunction encodeToBinary(str: string): string {\n return btoa(\n encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {\n return String.fromCharCode(parseInt(p1, 16))\n }),\n )\n}\n```\n\n> [\u26A0\uFE0F Why does this snippet not use atob/btoa?](#safe-binary-encodingdecoding)\n\nSo, if we were to turn the previous object into a search string using this configuration, it would look like this:\n\n```txt\n?page=1&sort=asc&filters=JTdCJUMyJUE4YXV0aG9yJUMyJUE4JUMyJUE4dGFubmVyJUMyJUE4JUMyJUE4bWluX3dvcmRzJUMyJUE4JUMyJUEyQ3UlN0Q%3D\n```\n\n<hr>\n\n### Safe Binary Encoding/Decoding\n\nIn 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:\n\nTo encode from a string to a binary string:\n\n```typescript\nexport function encodeToBinary(str: string): string {\n return btoa(\n encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {\n return String.fromCharCode(parseInt(p1, 16))\n }),\n )\n}\n```\n\nTo decode from a binary string to a string:\n\n```typescript\nexport function decodeFromBinary(str: string): string {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(str), function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join(''),\n )\n}\n```\n\n# Data Loading\n\nData loading is a common concern for web applications and is related to routing. When loading a page for your app, it's ideal if all of the page's async requirements are fetched and fulfilled as early as possible, in parallel. The router is the best place to coordinate these async dependencies as it's usually the only place in your app that knows where users are headed before content is rendered.\n\nYou may be familiar with `getServerSideProps` from Next.js or `loader`s from Remix/React-Router. TanStack Router has similar functionality to preload/load assets on a per-route basis in parallel allowing it to render as quickly as possible as it fetches via suspense.\n\nBeyond these normal expectations of a router, TanStack Router goes above and beyond and provides **built-in SWR Caching**, a long-term in-memory caching layer for route loaders. This means that you can use TanStack Router to both preload data for your routes so they load instantaneously or temporarily cache route data for previously visited routes to use again later.\n\n## The route loading lifecycle\n\nEvery time a URL/history update is detected, the router executes the following sequence:\n\n- Route Matching (Top-Down)\n - `route.params.parse`\n - `route.validateSearch`\n- Route Pre-Loading (Serial)\n - `route.beforeLoad`\n - `route.onError`\n - `route.errorComponent` / `parentRoute.errorComponent` / `router.defaultErrorComponent`\n- Route Loading (Parallel)\n - `route.component.preload?`\n - `route.loader`\n - `route.pendingComponent` (Optional)\n - `route.component`\n - `route.onError`\n - `route.errorComponent` / `parentRoute.errorComponent` / `router.defaultErrorComponent`\n\n## To Router Cache or not to Router Cache?\n\nThere is a high possibility that TanStack's router cache will be a good fit for most smaller to medium size applications, but it's important to understand the tradeoffs of using it vs a more robust caching solution like TanStack Query:\n\nTanStack Router Cache Pros:\n\n- Built-in, easy to use, no extra dependencies\n- Handles deduping, preloading, loading, stale-while-revalidate, background refetching on a per-route basis\n- Coarse invalidation (invalidate all routes and cache at once)\n- Automatic garbage collection\n- Works great for apps that share little data between routes\n- \"Just works\" for SSR\n\nTanStack Router Cache Cons:\n\n- No persistence adapters/model\n- No shared caching/deduping between routes\n- No built-in mutation APIs (a basic `useMutation` hook is provided in many examples that may be sufficient for many use cases)\n- No built-in cache-level optimistic update APIs (you can still use ephemeral state from something like a `useMutation` hook to achieve this at the component level)\n\n> [!TIP]\n> If you know right away that you'd like to or need to use something more robust like TanStack Que