TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

| ','>`\n- Optional\n- Configures which URI characters are allowed in path params that would ordinarily be escaped by encodeURIComponent.\n\n### `defaultStructuralSharing` property\n\n- Type: `boolean`\n- Optional\n- Defaults to `false`\n- Configures whether structural sharing is enabled by default for fine-grained selectors.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n### `defaultRemountDeps` property\n\n- Type:\n\n```tsx\ntype defaultRemountDeps = (opts: RemountDepsOptions) => any\n\ninterface RemountDepsOptions\u003c\n in out TRouteId,\n in out TFullSearchSchema,\n in out TAllParams,\n in out TLoaderDeps,\n> {\n routeId: TRouteId\n search: TFullSearchSchema\n params: TAllParams\n loaderDeps: TLoaderDeps\n}\n```\n\n- Optional\n- A default function that will be called to determine whether a route component shall be remounted after navigation. If this function returns a different value than previously, it will remount.\n- The return value needs to be JSON serializable.\n- By default, a route component will not be remounted if it stays active after a navigation\n\nExample: \nIf you want to configure to remount all route components upon `params` change, use:\n\n```tsx\nremountDeps: ({ params }) => params\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13879,"content_sha256":"a10a2f9bee870c87377543480085d6851231a6dda1899f8bc66788eb58e3d9c4"},{"filename":"references/docs/router/api/router/RouterStateType.md","content":"---\nid: RouterStateType\ntitle: RouterState type\n---\n\nThe `RouterState` type represents shape of the internal state of the router. The Router's internal state is useful, if you need to access certain internals of the router, such as any pending matches, is the router in its loading state, etc.\n\n```tsx\ntype RouterState = {\n status: 'pending' | 'idle'\n isLoading: boolean\n isTransitioning: boolean\n matches: Array\u003cRouteMatch>\n pendingMatches: Array\u003cRouteMatch>\n location: ParsedLocation\n resolvedLocation: ParsedLocation\n}\n```\n\n## RouterState properties\n\nThe `RouterState` type contains all of the properties that are available on the router state.\n\n### `status` property\n\n- Type: `'pending' | 'idle'`\n- The current status of the router. If the router is pending, it means that it is currently loading a route or the router is still transitioning to the new route.\n\n### `isLoading` property\n\n- Type: `boolean`\n- `true` if the router is currently loading a route or waiting for a route to finish loading.\n\n### `isTransitioning` property\n\n- Type: `boolean`\n- `true` if the router is currently transitioning to a new route.\n\n### `matches` property\n\n- Type: [`Array\u003cRouteMatch>`](./RouteMatchType.md)\n- An array of all of the route matches that have been resolved and are currently active.\n\n### `pendingMatches` property\n\n- Type: [`Array\u003cRouteMatch>`](./RouteMatchType.md)\n- An array of all of the route matches that are currently pending.\n\n### `location` property\n\n- Type: [`ParsedLocation`](./ParsedLocationType.md)\n- The latest location that the router has parsed from the browser history. This location may not be resolved and loaded yet.\n\n### `resolvedLocation` property\n\n- Type: [`ParsedLocation`](./ParsedLocationType.md)\n- The location that the router has resolved and loaded.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1787,"content_sha256":"5a34ef8f2e195b823ddd362a05c816e08e176dc7885b750be437c3d43bc0f552"},{"filename":"references/docs/router/api/router/RouterType.md","content":"---\nid: RouterType\ntitle: Router type\n---\n\nThe `Router` type is used to describe a router instance.\n\n## `Router` properties and methods\n\nAn instance of the `Router` has the following properties and methods:\n\n### `.update` method\n\n- Type: `(newOptions: RouterOptions) => void`\n- Updates the router instance with new options.\n\n### `state` property\n\n- Type: [`RouterState`](./RouterStateType.md)\n- The current state of the router.\n\n> **`router.state` is always up to date, but NOT REACTIVE. If you use `router.state` in a component, the component will not re-render when the router state changes. To get a reactive version of the router state, use the [`useRouterState`](./useRouterStateHook.md) hook.**\n\n### `.subscribe` method\n\n- Type: `(eventType: TType, fn: ListenerFn\u003cRouterEvents[TType]>) => (event: RouterEvent) => void`\n- Subscribes to a [`RouterEvent`](./RouterEventsType.md).\n- Returns a function that can be used to unsubscribe from the event.\n- The callback provided to the returned function will be called with the event that was emitted.\n\n### `.matchRoutes` method\n\n- Type: `(pathname: string, locationSearch?: Record\u003cstring, any>, opts?: { throwOnError?: boolean; }) => RouteMatch[]`\n- Matches a pathname and search params against the router's route tree and returns an array of route matches.\n- If `opts.throwOnError` is `true`, any errors that occur during the matching process will be thrown (in addition to being returned in the route match's `error` property).\n\n### `.cancelMatch` method\n\n- Type: `(matchId: string) => void`\n- Cancels a route match that is currently pending by calling `match.abortController.abort()`.\n\n### `.cancelMatches` method\n\n- Type: `() => void`\n- Cancels all route matches that are currently pending by calling `match.abortController.abort()` on each one.\n\n### `.buildLocation` method\n\nBuilds a new parsed location object that can be used later to navigate to a new location.\n\n- Type: `(opts: BuildNextOptions) => ParsedLocation`\n- Properties\n - `from`\n - Type: `string`\n - Optional\n - The path to navigate from. If not provided, the current path will be used.\n - `to`\n - Type: `string | number | null`\n - Optional\n - The path to navigate to. If `null`, the current path will be used.\n - `params`\n - Type: `true | Updater\u003cunknown>`\n - Optional\n - If `true`, the current params will be used. If a function is provided, it will be called with the current params and the return value will be used.\n - `search`\n - Type: `true | Updater\u003cunknown>`\n - Optional\n - If `true`, the current search params will be used. If a function is provided, it will be called with the current search params and the return value will be used.\n - `hash`\n - Type: `true | Updater\u003cstring>`\n - Optional\n - If `true`, the current hash will be used. If a function is provided, it will be called with the current hash and the return value will be used.\n - `state`\n - Type: `true | NonNullableUpdater\u003cParsedHistoryState, HistoryState>`\n - Optional\n - If `true`, the current state will be used. If a function is provided, it will be called with the current state and the return value will be used.\n - `mask`\n - Type: `object`\n - Optional\n - Contains all of the same BuildNextOptions, with the addition of `unmaskOnReload`.\n - `unmaskOnReload`\n - Type: `boolean`\n - Optional\n - If `true`, the route mask will be removed when the page is reloaded. This can be overridden on a per-navigation basis by setting `mask.unmaskOnReload` in [`NavigateOptions`](./NavigateOptionsType.md).\n\n### `.commitLocation` method\n\nCommits a new location object to the browser history.\n\n- Type\n ```tsx\n type commitLocation = (\n location: ParsedLocation & {\n replace?: boolean\n resetScroll?: boolean\n hashScrollIntoView?: boolean | ScrollIntoViewOptions\n ignoreBlocker?: boolean\n },\n ) => Promise\u003cvoid>\n ```\n- Properties\n - `location`\n - Type: [`ParsedLocation`](./ParsedLocationType.md)\n - Required\n - The location to commit to the browser history.\n - `replace`\n - Type: `boolean`\n - Optional\n - Defaults to `false`.\n - If `true`, the location will be committed to the browser history using `history.replace` instead of `history.push`.\n - `resetScroll`\n - Type: `boolean`\n - Optional\n - Defaults to `true` so that the scroll position will be reset to 0,0 after the location is committed to the browser history.\n - If `false`, the scroll position will not be reset to 0,0 after the location is committed to history.\n - `hashScrollIntoView`\n - Type: `boolean | ScrollIntoViewOptions`\n - Optional\n - Defaults to `true` so the element with an id matching the hash will be scrolled into view after the location is committed to history.\n - If `false`, the element with an id matching the hash will not be scrolled into view after the location is committed to history.\n - If an object is provided, it will be passed to the `scrollIntoView` method as options.\n - See MDN for more information on `ScrollIntoViewOptions`.\n - `ignoreBlocker`\n - Type: `boolean`\n - Optional\n - Defaults to `false`.\n - If `true`, navigation will ignore any blockers that might prevent it.\n\n### `.navigate` method\n\nNavigates to a new location.\n\n- Type\n ```tsx\n type navigate = (options: NavigateOptions) => Promise\u003cvoid>\n ```\n\n### `.invalidate` method\n\nInvalidates route matches by forcing their `beforeLoad` and `load` functions to be called again.\n\n- Type: `(opts?: {filter?: (d: MakeRouteMatchUnion\u003cTRouter>) => boolean, sync?: boolean, forcePending?: boolean }) => Promise\u003cvoid>`\n- This is useful any time your loader data might be out of date or stale. For example, if you have a route that displays a list of posts, and you have a loader function that fetches the list of posts from an API, you might want to invalidate the route matches for that route any time a new post is created so that the list of posts is always up-to-date.\n- if `filter` is not supplied, all matches will be invalidated\n- if `filter` is supplied, only matches for which `filter` returns `true` will be invalidated.\n- if `sync` is true, the promise returned by this function will only resolve once all loaders have finished.\n- if `forcePending` is true, the invalidated matches will be put into `'pending'` state regardless whether they are in `'error'` state or not.\n- You might also want to invalidate the Router if you imperatively `reset` the router's `CatchBoundary` to trigger loaders again.\n\n### `.clearCache` method\n\nRemove cached route matches.\n\n- Type: `(opts?: {filter?: (d: MakeRouteMatchUnion\u003cTRouter>) => boolean}) => void`\n- if `filter` is not supplied, all cached matches will be removed\n- if `filter` is supplied, only matches for which `filter` returns `true` will be removed.\n\n### `.load` method\n\nLoads all of the currently matched route matches and resolves when they are all loaded and ready to be rendered.\n\n> **`router.load()` respects `route.staleTime`: fresh matches stay fresh, but stale matches are revalidated even if their loader key did not change. If you need to forcefully reload all active matches regardless of freshness, use `router.invalidate()` instead.**\n\n- Type: `(opts?: {sync?: boolean}) => Promise\u003cvoid>`\n- if `sync` is true, the promise returned by this function will only resolve once all loaders have finished.\n- The most common use case for this method is to call it when doing SSR to ensure that all of the critical data for the current route is loaded before attempting to stream or render the application to the client.\n\n### `.preloadRoute` method\n\nPreloads all of the matches that match the provided `NavigateOptions`.\n\n> **Preloaded route matches are not stored long-term in the router state. They are only stored until the next attempted navigation action.**\n\n- Type: `(opts?: NavigateOptions) => Promise\u003cRouteMatch[]>`\n- Properties\n - `opts`\n - Type: `NavigateOptions`\n - Optional, defaults to the current location.\n - The options that will be used to determine which route matches to preload.\n- Returns\n - A promise that resolves with an array of all of the route matches that were preloaded.\n\n### `.loadRouteChunk` method\n\nLoads the JS chunk of the route.\n\n- Type: `(route: AnyRoute) => Promise\u003cvoid>`\n\n### `.matchRoute` method\n\nMatches a pathname and search params against the router's route tree and returns a route match's params or false if no match was found.\n\n- Type: `(dest: ToOptions, matchOpts?: MatchRouteOptions) => RouteMatch['params'] | false`\n- Properties\n - `dest`\n - Type: `ToOptions`\n - Required\n - The destination to match against.\n - `matchOpts`\n - Type: `MatchRouteOptions`\n - Optional\n - Options that will be used to match the destination.\n- Returns\n - A route match's params if a match was found.\n - `false` if no match was found.\n\n### `.dehydrate` method\n\nDehydrates the router's critical state into a serializable object that can be sent to the client in an initial request.\n\n- Type: `() => DehydratedRouter`\n- Returns\n - A serializable object that contains the router's critical state.\n\n### `.hydrate` method\n\nHydrates the router's critical state from a serializable object that was sent from the server in an initial request.\n\n- Type: `(dehydrated: DehydratedRouter) => void`\n- Properties\n - `dehydrated`\n - Type: `DehydratedRouter`\n - Required\n - The dehydrated router state that was sent from the server.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9459,"content_sha256":"09ead9fd56001e6e1167509ec346cb3fe72bb817ad3df4427e2702813c601bb8"},{"filename":"references/docs/router/api/router/RouteType.md","content":"---\nid: RouteType\ntitle: Route type\n---\n\nThe `Route` type is used to describe a route instance.\n\n## `Route` properties and methods\n\nAn instance of the `Route` has the following properties and methods:\n\n### `.addChildren` method\n\n- Type: `(children: Route[]) => this`\n- Adds child routes to the route instance and returns the route instance (but with updated types to reflect the new children).\n\n### `.update` method\n\n- Type: `(options: Partial\u003cUpdatableRouteOptions>) => this`\n- Updates the route instance with new options and returns the route instance (but with updated types to reflect the new options).\n- In some circumstances, it can be useful to update a route instance's options after it has been created to avoid circular type references.\n- ...`RouteApi` methods\n\n### `.lazy` method\n\n- Type: `(lazyImporter: () => Promise\u003cPartial\u003cUpdatableRouteOptions>>) => this`\n- Updates the route instance with a new lazy importer which will be resolved lazily when loading the route. This can be useful for code splitting.\n\n### `.redirect` method\n\n- Type: `(opts?: RedirectOptions) => Redirect`\n- A type-safe version of the [`redirect`](./redirectFunction.md) function that is pre-bound to the route's path.\n- The `from` parameter is automatically set to the route's `fullPath`, enabling type-safe relative redirects.\n- See [`RouteApi.redirect`](./RouteApiType.md#redirect-method) for more details.\n\n#### Example\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/dashboard/settings')({\n beforeLoad: ({ context }) => {\n if (!context.user) {\n // Type-safe redirect - 'from' is automatically '/dashboard/settings'\n throw Route.redirect({\n to: '../login', // Relative path to sibling route\n })\n }\n },\n})\n```\n\n### ...`RouteApi` methods\n\n- All of the methods from [`RouteApi`](./RouteApiType.md) are available.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1886,"content_sha256":"5a1a045cf83c28e953ba4246be2c99f803a719cc52d7e7f1e03ca7154b0563a8"},{"filename":"references/docs/router/api/router/stripSearchParamsFunction.md","content":"---\nid: stripSearchParams\ntitle: Search middleware to strip search params\n---\n\n`stripSearchParams` is a search middleware that allows to remove search params.\n\n## stripSearchParams props\n\n`stripSearchParams` accepts one of the following inputs:\n\n- `true`: if the search schema has no required params, `true` can be used to strip all search params\n- a list of keys of those search params that shall be removed; only keys of optional search params are allowed.\n- an object that conforms to the partial input search schema. The search params are compared against the values of this object; if the value is deeply equal, it will be removed. This is especially useful to strip out default search params.\n\n## Examples\n\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute, stripSearchParams } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst defaultValues = {\n one: 'abc',\n two: 'xyz',\n}\n\nconst searchSchema = z.object({\n one: z.string().default(defaultValues.one),\n two: z.string().default(defaultValues.two),\n})\n\nexport const Route = createFileRoute('/')({\n validateSearch: zodValidator(searchSchema),\n search: {\n // strip default values\n middlewares: [stripSearchParams(defaultValues)],\n },\n})\n```\n\n```tsx\nimport { z } from 'zod'\nimport { createRootRoute, stripSearchParams } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst searchSchema = z.object({\n hello: z.string().default('world'),\n requiredParam: z.string(),\n})\n\nexport const Route = createRootRoute({\n validateSearch: zodValidator(searchSchema),\n search: {\n // always remove `hello`\n middlewares: [stripSearchParams(['hello'])],\n },\n})\n```\n\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute, stripSearchParams } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst searchSchema = z.object({\n one: z.string().default('abc'),\n two: z.string().default('xyz'),\n})\n\nexport const Route = createFileRoute('/')({\n validateSearch: zodValidator(searchSchema),\n search: {\n // remove all search params\n middlewares: [stripSearchParams(true)],\n },\n})\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2160,"content_sha256":"d39c940b56725050b46c21a72e9aa8eb2d66d35126a6c938ea4b429b30771a4e"},{"filename":"references/docs/router/api/router/ToMaskOptionsType.md","content":"---\nid: ToMaskOptionsType\ntitle: ToMaskOptions type\n---\n\nThe `ToMaskOptions` type includes the same destination fields as [`ToOptions`](./ToOptionsType.md), excluding `mask`, and adds options specific to route masking.\n\n```tsx\ntype ToMaskOptions = {\n from?: ValidRoutePath | string\n to?: ValidRoutePath | string\n hash?: true | string | ((prev?: string) => string)\n state?: true | HistoryState | ((prev: HistoryState) => HistoryState)\n} & SearchParamOptions &\n PathParamOptions & {\n unmaskOnReload?: boolean\n }\n```\n\n- [`ToOptions`](./ToOptionsType.md)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":560,"content_sha256":"677d85ac455624380ce4de5a226f20c8c3b933224276c5eedd98d63ef7141cfa"},{"filename":"references/docs/router/api/router/ToOptionsType.md","content":"---\nid: ToOptionsType\ntitle: ToOptions type\n---\n\nThe `ToOptions` type contains several properties that can be used to describe a router destination, including `mask` for route masking.\n\n```tsx\ntype ToOptions = {\n from?: ValidRoutePath | string\n to?: ValidRoutePath | string\n hash?: true | string | ((prev?: string) => string)\n state?: true | HistoryState | ((prev: HistoryState) => HistoryState)\n} & SearchParamOptions &\n PathParamOptions &\n MaskOptions\n\ntype SearchParamOptions = {\n search?: true | TToSearch | ((prev: TFromSearch) => TToSearch)\n}\n\ntype PathParamOptions = {\n params?:\n | true\n | Record\u003cstring, TPathParam>\n | ((prev: TFromParams) => TToParams)\n}\n\ntype MaskOptions = {\n mask?: ToMaskOptions\u003cTRouter, TMaskFrom, TMaskTo>\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":761,"content_sha256":"6bb185471b4e522d4a999bafeb3346fab5758087d878a8d4a0d99eeec5a24e35"},{"filename":"references/docs/router/api/router/useAwaitedHook.md","content":"---\nid: useAwaitedHook\ntitle: useAwaited hook\n---\n\nThe `useAwaited` method is a hook that suspends until the provided promise is resolved or rejected.\n\n## useAwaited options\n\nThe `useAwaited` hook accepts a single argument, an `options` object.\n\n### `options.promise` option\n\n- Type: `Promise\u003cT>`\n- Required\n- The deferred promise to await.\n\n## useAwaited returns\n\n- Throws an error if the promise is rejected.\n- Suspends (throws a promise) if the promise is pending.\n- Returns the resolved value of a deferred promise if the promise is resolved.\n\n## Examples\n\n```tsx\nimport { useAwaited } from '@tanstack/react-router'\n\nfunction Component() {\n const { deferredPromise } = route.useLoaderData()\n\n const data = useAwaited({ promise: myDeferredPromise })\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":770,"content_sha256":"fe214136c96a0f2614ffa5c1f7bb25e6d5a91b9b014d6b066a67b55dff3fe435"},{"filename":"references/docs/router/api/router/useBlockerHook.md","content":"---\nid: useBlockerHook\ntitle: useBlocker hook\n---\n\nThe `useBlocker` method is a hook that [blocks navigation](../../guide/navigation-blocking.md) when a condition is met.\n\n> The following new `useBlocker` API is currently _experimental_.\n\n## useBlocker options\n\nThe `useBlocker` hook accepts a single _required_ argument, an option object:\n\n### `options.shouldBlockFn` option\n\n- Required\n- Type: `ShouldBlockFn`\n- This function should return a `boolean` or a `Promise\u003cboolean>` that tells the blocker if it should block the current navigation\n- The function has the argument of type `ShouldBlockFnArgs` passed to it, which tells you information about the current and next route and the action performed\n- Think of this function as telling the router if it should block the navigation, so returning `true` mean that it should block the navigation and `false` meaning that it should be allowed\n\n```ts\ninterface ShouldBlockFnLocation\u003c...> {\n routeId: TRouteId\n fullPath: TFullPath\n pathname: string\n params: TAllParams\n search: TFullSearchSchema\n}\n\ntype ShouldBlockFnArgs = {\n current: ShouldBlockFnLocation\n next: ShouldBlockFnLocation\n action: HistoryAction\n}\n```\n\n### `options.disabled` option\n\n- Optional - defaults to `false`\n- Type: `boolean`\n- Specifies if the blocker should be entirely disabled or not\n\n### `options.enableBeforeUnload` option\n\n- Optional - defaults to `true`\n- Type: `boolean | (() => boolean)`\n- Tell the blocker to sometimes or always block the browser `beforeUnload` event or not\n\n### `options.withResolver` option\n\n- Optional - defaults to `false`\n- Type: `boolean`\n- Specify if the resolver returned by the hook should be used or whether your `shouldBlockFn` function itself resolves the blocking\n\n### `options.blockerFn` option ( deprecated)\n\n- Optional\n- Type: `BlockerFn`\n- The function that returns a `boolean` or `Promise\u003cboolean>` indicating whether to allow navigation.\n\n### `options.condition` option ( deprecated)\n\n- Optional - defaults to `true`\n- Type: `boolean`\n- A navigation attempt is blocked when this condition is `true`.\n\n## useBlocker returns\n\nAn object with the controls to allow manual blocking and unblocking of navigation.\n\n- `status` - A string literal that can be either `'blocked'` or `'idle'`\n- `next` - When status is `blocked`, a type narrrowable object that contains information about the next location\n- `current` - When status is `blocked`, a type narrrowable object that contains information about the current location\n- `action` - When status is `blocked`, a `HistoryAction` string that shows the action that triggered the navigation\n- `proceed` - When status is `blocked`, a function that allows navigation to continue\n- `reset` - When status is `blocked`, a function that cancels navigation (`status` will be reset to `'idle'`)\n\nor\n\n`void` when `withResolver` is `false`\n\n## Examples\n\nTwo common use cases for the `useBlocker` hook are:\n\n### Basic usage\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n useBlocker({\n shouldBlockFn: () => formIsDirty,\n })\n\n // ...\n}\n```\n\n### Custom UI\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n const { proceed, reset, status, next } = useBlocker({\n shouldBlockFn: () => formIsDirty,\n withResolver: true,\n })\n\n // ...\n\n return (\n \u003c>\n {/* ... */}\n {status === 'blocked' && (\n \u003cdiv>\n \u003cp>You are navigating to {next.pathname}\u003c/p>\n \u003cp>Are you sure you want to leave?\u003c/p>\n \u003cbutton onClick={proceed}>Yes\u003c/button>\n \u003cbutton onClick={reset}>No\u003c/button>\n \u003c/div>\n )}\n \u003c/>\n}\n```\n\n### Conditional blocking\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const { proceed, reset, status } = useBlocker({\n shouldBlockFn: ({ next }) => {\n return !next.pathname.includes('step/')\n },\n withResolver: true,\n })\n\n // ...\n\n return (\n \u003c>\n {/* ... */}\n {status === 'blocked' && (\n \u003cdiv>\n \u003cp>Are you sure you want to leave?\u003c/p>\n \u003cbutton onClick={proceed}>Yes\u003c/button>\n \u003cbutton onClick={reset}>No\u003c/button>\n \u003c/div>\n )}\n \u003c/>\n )\n}\n```\n\n### Without resolver\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n useBlocker({\n shouldBlockFn: ({ next }) => {\n if (next.pathname.includes('step/')) {\n return false\n }\n\n const shouldLeave = confirm('Are you sure you want to leave?')\n return !shouldLeave\n },\n })\n\n // ...\n}\n```\n\n### Type narrowing\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n // block going from editor-1 to /foo/123?hello=world\n const { proceed, reset, status } = useBlocker({\n shouldBlockFn: ({ current, next }) => {\n if (\n current.routeId === '/editor-1' &&\n next.fullPath === '/foo/$id' &&\n next.params.id === '123' &&\n next.search.hello === 'world'\n ) {\n return true\n }\n return false\n },\n enableBeforeUnload: false,\n withResolver: true,\n })\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5348,"content_sha256":"3e714ae28778bcad0f2baa8f7c0e51608f6530a15b4db3bef3ee774563ec31d3"},{"filename":"references/docs/router/api/router/useCanGoBack.md","content":"---\nid: useCanGoBack\ntitle: useCanGoBack hook\n---\n\nThe `useCanGoBack` hook returns a boolean representing if the router history can safely go back without exiting the application.\n\n> The following new `useCanGoBack` API is currently _experimental_.\n\n## useCanGoBack returns\n\n- If the router history is not at index `0`, `true`.\n- If the router history is at index `0`, `false`.\n\n## Limitations\n\nThe router history index is reset after a navigation with [`reloadDocument`](./NavigateOptionsType.md#reloaddocument) set as `true`. This causes the router history to consider the new location as the initial one and will cause `useCanGoBack` to return `false`.\n\n## Examples\n\n### Showing a back button\n\n```tsx\nimport { useRouter, useCanGoBack } from '@tanstack/react-router'\n\nfunction Component() {\n const router = useRouter()\n const canGoBack = useCanGoBack()\n\n return (\n \u003cdiv>\n {canGoBack ? (\n \u003cbutton onClick={() => router.history.back()}>Go back\u003c/button>\n ) : null}\n\n {/* ... */}\n \u003c/div>\n )\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1028,"content_sha256":"0c785ca250d29391d61601bbdce110fa85d01faca5aa0c55f36564b41e7b2212"},{"filename":"references/docs/router/api/router/useChildMatchesHook.md","content":"---\nid: useChildMatchesHook\ntitle: useChildMatches hook\n---\n\nThe `useChildMatches` hook returns all of the child [`RouteMatch`](./RouteMatchType.md) objects from the closest match down to the leaf-most match. **It does not include the current match, which can be obtained using the `useMatch` hook.**\n\n> [!IMPORTANT]\n> If the router has pending matches and they are showing their pending component fallbacks, `router.state.pendingMatches` will used instead of `router.state.matches`.\n\n## useChildMatches options\n\nThe `useChildMatches` hook accepts a single _optional_ argument, an `options` object.\n\n### `opts.select` option\n\n- Optional\n- `(matches: RouteMatch[]) => TSelected`\n- If supplied, this function will be called with the route matches and the return value will be returned from `useChildMatches`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useChildMatches returns\n\n- If a `select` function is provided, the return value of the `select` function.\n- If no `select` function is provided, an array of [`RouteMatch`](./RouteMatchType.md) objects.\n\n## Examples\n\n```tsx\nimport { useChildMatches } from '@tanstack/react-router'\n\nfunction Component() {\n const childMatches = useChildMatches()\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1546,"content_sha256":"54d0b1261b2a4be2482cab8306d798aa249ce6835e6bdd7044a1c36eee1d3eb4"},{"filename":"references/docs/router/api/router/useLinkPropsHook.md","content":"---\nid: useLinkPropsHook\ntitle: useLinkProps hook\n---\n\nThe `useLinkProps` hook that takes an object as its argument and returns a `React.AnchorHTMLAttributes\u003cHTMLAnchorElement>` props object. These props can then be safely applied to an anchor element to create a link that can be used to navigate to the new location. This includes changes to the pathname, search params, hash, and location state.\n\n## useLinkProps options\n\n```tsx\ntype UseLinkPropsOptions = ActiveLinkOptions &\n React.AnchorHTMLAttributes\u003cHTMLAnchorElement>\n```\n\n- [`ActiveLinkOptions`](./ActiveLinkOptionsType.md)\n- The `useLinkProps` options are used to build a [`LinkProps`](./LinkPropsType.md) object.\n- It also extends the `React.AnchorHTMLAttributes\u003cHTMLAnchorElement>` type, so that any additional props that are passed to the `useLinkProps` hook will be merged with the [`LinkProps`](./LinkPropsType.md) object.\n\n## useLinkProps returns\n\n- A `React.AnchorHTMLAttributes\u003cHTMLAnchorElement>` object that can be applied to an anchor element to create a link that can be used to navigate to the new location\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1081,"content_sha256":"035368c4aa7a203ee522552952e0448f6b7e7f2902eb0f0177394ccd77132659"},{"filename":"references/docs/router/api/router/useLoaderDataHook.md","content":"---\nid: useLoaderDataHook\ntitle: useLoaderData hook\n---\n\nThe `useLoaderData` hook returns the loader data from the closest [`RouteMatch`](./RouteMatchType.md) in the component tree.\n\n## useLoaderData options\n\nThe `useLoaderData` hook accepts an `options` object.\n\n### `opts.from` option\n\n- Type: `string`\n- The route id of the closest parent match\n- Optional, but recommended for full type safety.\n- If `opts.strict` is `true`, TypeScript will warn for this option if it is not provided.\n- If `opts.strict` is `false`, TypeScript will provide loosened types for the returned loader data.\n\n### `opts.strict` option\n\n- Type: `boolean`\n- Optional - `default: true`\n- If `false`, the `opts.from` option will be ignored and types will be loosened to reflect the shared types of all possible loader data.\n\n### `opts.select` option\n\n- Optional\n- `(loaderData: TLoaderData) => TSelected`\n- If supplied, this function will be called with the loader data and the return value will be returned from `useLoaderData`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useLoaderData returns\n\n- If a `select` function is provided, the return value of the `select` function.\n- If no `select` function is provided, the loader data or a loosened version of the loader data if `opts.strict` is `false`.\n\n## Examples\n\n```tsx\nimport { useLoaderData } from '@tanstack/react-router'\n\nfunction Component() {\n const loaderData = useLoaderData({ from: '/posts/$postId' })\n // ^? { postId: string, body: string, ... }\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1841,"content_sha256":"b0f6b428183c4a38f20d1b738935404eb0e2eb674e4f87e0ba53dd862953ae0d"},{"filename":"references/docs/router/api/router/useLoaderDepsHook.md","content":"---\nid: useLoaderDepsHook\ntitle: useLoaderDeps hook\n---\n\nThe `useLoaderDeps` hook is a hook that returns an object with the dependencies that are used to trigger the `loader` for a given route.\n\n## useLoaderDepsHook options\n\nThe `useLoaderDepsHook` hook accepts an `options` object.\n\n### `opts.from` option\n\n- Type: `string`\n- Required\n- The RouteID or path to get the loader dependencies from.\n\n### `opts.select` option\n\n- Type: `(deps: TLoaderDeps) => TSelected`\n- Optional\n- If supplied, this function will be called with the loader dependencies object and the return value will be returned from `useLoaderDeps`.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useLoaderDeps returns\n\n- An object of the loader dependencies or `TSelected` if a `select` function is provided.\n\n## Examples\n\n```tsx\nimport { useLoaderDeps } from '@tanstack/react-router'\n\nconst routeApi = getRouteApi('/posts/$postId')\n\nfunction Component() {\n const deps = useLoaderDeps({ from: '/posts/$postId' })\n\n // OR\n\n const routeDeps = routeApi.useLoaderDeps()\n\n // OR\n\n const postId = useLoaderDeps({\n from: '/posts',\n select: (deps) => deps.view,\n })\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1362,"content_sha256":"29b4d1b816b2e8581d02c5f0a660fc69e38f86493fe5856e99c4513c55c0d0d8"},{"filename":"references/docs/router/api/router/useLocationHook.md","content":"---\nid: useLocationHook\ntitle: useLocation hook\n---\n\nThe `useLocation` method is a hook that returns the current [`location`](./ParsedLocationType.md) object. This hook is useful for when you want to perform some side effect whenever the current location changes.\n\n## useLocation options\n\nThe `useLocation` hook accepts an optional `options` object.\n\n### `opts.select` option\n\n- Type: `(state: ParsedLocationType) => TSelected`\n- Optional\n- If supplied, this function will be called with the [`location`](./ParsedLocationType.md) object and the return value will be returned from `useLocation`.\n\n## useLocation returns\n\n- The current [`location`](./ParsedLocationType.md) object or `TSelected` if a `select` function is provided.\n\n## Examples\n\n```tsx\nimport { useLocation } from '@tanstack/react-router'\n\nfunction Component() {\n const location = useLocation()\n // ^ ParsedLocation\n\n // OR\n\n const pathname = useLocation({\n select: (location) => location.pathname,\n })\n // ^ string\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1012,"content_sha256":"46aa98af85d12e7938fb4817564fd8fd81db67ca9e938a3bbed33f5de2c643ed"},{"filename":"references/docs/router/api/router/useMatchesHook.md","content":"---\nid: useMatchesHook\ntitle: useMatches hook\n---\n\nThe `useMatches` hook returns all of the [`RouteMatch`](./RouteMatchType.md) objects from the router **regardless of its callers position in the React component tree**.\n\n> [!TIP]\n> If you only want the parent or child matches, then you can use the [`useParentMatches`](./useParentMatchesHook.md) or the [`useChildMatches`](./useChildMatchesHook.md) based on the selection you need.\n\n## useMatches options\n\nThe `useMatches` hook accepts a single _optional_ argument, an `options` object.\n\n### `opts.select` option\n\n- Optional\n- `(matches: RouteMatch[]) => TSelected`\n- If supplied, this function will be called with the route matches and the return value will be returned from `useMatches`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useMatches returns\n\n- If a `select` function is provided, the return value of the `select` function.\n- If no `select` function is provided, an array of [`RouteMatch`](./RouteMatchType.md) objects.\n\n## Examples\n\n```tsx\nimport { useMatches } from '@tanstack/react-router'\n\nfunction Component() {\n const matches = useMatches()\n // ^? [RouteMatch, RouteMatch, ...]\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1502,"content_sha256":"eca5a7eab1e3c2d93f09c7f2a9d0b705470f152e42bb384aef444b8422fec1c8"},{"filename":"references/docs/router/api/router/useMatchHook.md","content":"---\nid: useMatchHook\ntitle: useMatch hook\n---\n\nThe `useMatch` hook returns a [`RouteMatch`](./RouteMatchType.md) in the component tree. The raw route match contains all of the information about a route match in the router and also powers many other hooks under the hood like `useParams`, `useLoaderData`, `useRouteContext`, and `useSearch`.\n\n## useMatch options\n\nThe `useMatch` hook accepts a single argument, an `options` object.\n\n### `opts.from` option\n\n- Type: `string`\n- The route id of a match\n- Optional, but recommended for full type safety.\n- If `opts.strict` is `true`, `from` is required and TypeScript will warn for this option if it is not provided.\n- If `opts.strict` is `false`, `from` must not be set and TypeScript will provided loosened types for the returned [`RouteMatch`](./RouteMatchType.md).\n\n### `opts.strict` option\n\n- Type: `boolean`\n- Optional\n- `default: true`\n- If `false`, the `opts.from` must not be set and types will be loosened to `Partial\u003cRouteMatch>` to reflect the shared types of all matches.\n\n### `opts.select` option\n\n- Optional\n- `(match: RouteMatch) => TSelected`\n- If supplied, this function will be called with the route match and the return value will be returned from `useMatch`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n### `opts.shouldThrow` option\n\n- Type: `boolean`\n- Optional\n- `default: true`\n- If `false`,`useMatch` will not throw an invariant exception in case a match was not found in the currently rendered matches; in this case, it will return `undefined`.\n\n## useMatch returns\n\n- If a `select` function is provided, the return value of the `select` function.\n- If no `select` function is provided, the [`RouteMatch`](./RouteMatchType.md) object or a loosened version of the `RouteMatch` object if `opts.strict` is `false`.\n\n## Examples\n\n### Accessing a route match\n\n```tsx\nimport { useMatch } from '@tanstack/react-router'\n\nfunction Component() {\n const match = useMatch({ from: '/posts/$postId' })\n // ^? strict match for RouteMatch\n // ...\n}\n```\n\n### Accessing the root route's match\n\n```tsx\nimport {\n useMatch,\n rootRouteId, // \u003c\u003c\u003c\u003c use this token!\n} from '@tanstack/react-router'\n\nfunction Component() {\n const match = useMatch({ from: rootRouteId })\n // ^? strict match for RouteMatch\n // ...\n}\n```\n\n### Checking if a specific route is currently rendered\n\n```tsx\nimport { useMatch } from '@tanstack/react-router'\n\nfunction Component() {\n const match = useMatch({ from: '/posts', shouldThrow: false })\n // ^? RouteMatch | undefined\n if (match !== undefined) {\n // ...\n }\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2900,"content_sha256":"7c2335e9ab9d2e763dbdaa028986bbd45a86b63b69f7baa715c7780b8d1f5a0c"},{"filename":"references/docs/router/api/router/useMatchRouteHook.md","content":"---\nid: useMatchRouteHook\ntitle: useMatchRoute hook\n---\n\nThe `useMatchRoute` hook is a hook that returns a `matchRoute` function that can be used to match a route against either the current or pending location.\n\n## useMatchRoute returns\n\n- A `matchRoute` function that can be used to match a route against either the current or pending location.\n\n## matchRoute function\n\nThe `matchRoute` function is a function that can be used to match a route against either the current or pending location.\n\n### matchRoute function options\n\nThe `matchRoute` function accepts a single argument, an `options` object.\n\n- Type: [`UseMatchRouteOptions`](./UseMatchRouteOptionsType.md)\n\n### matchRoute function returns\n\n- The matched route's params or `false` if no route was matched\n\n## Examples\n\n```tsx\nimport { useMatchRoute } from '@tanstack/react-router'\n\n// Current location: /posts/123\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({ to: '/posts/$postId' })\n // ^ { postId: '123' }\n}\n\n// Current location: /posts/123\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({ to: '/posts' })\n // ^ false\n}\n\n// Current location: /posts/123\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({ to: '/posts', fuzzy: true })\n // ^ {}\n}\n\n// Current location: /posts\n// Pending location: /posts/123\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({ to: '/posts/$postId', pending: true })\n // ^ { postId: '123' }\n}\n\n// Current location: /posts/123/foo/456\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({ to: '/posts/$postId/foo/$fooId' })\n // ^ { postId: '123', fooId: '456' }\n}\n\n// Current location: /posts/123/foo/456\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({\n to: '/posts/$postId/foo/$fooId',\n params: { postId: '123' },\n })\n // ^ { postId: '123', fooId: '456' }\n}\n\n// Current location: /posts/123/foo/456\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({\n to: '/posts/$postId/foo/$fooId',\n params: { postId: '789' },\n })\n // ^ false\n}\n\n// Current location: /posts/123/foo/456\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({\n to: '/posts/$postId/foo/$fooId',\n params: { fooId: '456' },\n })\n // ^ { postId: '123', fooId: '456' }\n}\n\n// Current location: /posts/123/foo/456\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({\n to: '/posts/$postId/foo/$fooId',\n params: { postId: '123', fooId: '456' },\n })\n // ^ { postId: '123', fooId: '456' }\n}\n\n// Current location: /posts/123/foo/456\nfunction Component() {\n const matchRoute = useMatchRoute()\n const params = matchRoute({\n to: '/posts/$postId/foo/$fooId',\n params: { postId: '789', fooId: '456' },\n })\n // ^ false\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2990,"content_sha256":"a9981c1c7739e0d020ee50df21b1a4d5a0c7c7f6aa9fc1310ba1abb2f87f3be2"},{"filename":"references/docs/router/api/router/UseMatchRouteOptionsType.md","content":"---\nid: UseMatchRouteOptionsType\ntitle: UseMatchRouteOptions type\n---\n\nThe `UseMatchRouteOptions` type extends the [`ToOptions`](./ToOptionsType.md) type and describes additional options available when using the [`useMatchRoute`](./useMatchRouteHook.md) hook.\n\n```tsx\nexport type UseMatchRouteOptions = ToOptions & MatchRouteOptions\n```\n\n- [`ToOptions`](./ToOptionsType.md)\n- [`MatchRouteOptions`](./MatchRouteOptionsType.md)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":426,"content_sha256":"40105b19aa2c3eadf4413bfb856a3775a121e75caf2d0b74b9c69b4ceb0a86de"},{"filename":"references/docs/router/api/router/useNavigateHook.md","content":"---\nid: useNavigateHook\ntitle: useNavigate hook\n---\n\nThe `useNavigate` hook is a hook that returns a `navigate` function that can be used to navigate to a new location. This includes changes to the pathname, search params, hash, and location state.\n\n## useNavigate options\n\nThe `useNavigate` hook accepts a single argument, an `options` object.\n\n### `opts.from` option\n\n- Type: `string`\n- Optional\n- Description: The location to navigate from. This is useful when you want to navigate to a new location from a specific location, rather than the current location.\n\n## useNavigate returns\n\n- A `navigate` function that can be used to navigate to a new location.\n\n## navigate function\n\nThe `navigate` function is a function that can be used to navigate to a new location.\n\n### navigate function options\n\nThe `navigate` function accepts a single argument, an `options` object.\n\n- Type: [`NavigateOptions`](./NavigateOptionsType.md)\n\n### navigate function returns\n\n- A `Promise` that resolves when the navigation is complete\n\n## Examples\n\n```tsx\nimport { useNavigate } from '@tanstack/react-router'\n\nfunction PostsPage() {\n const navigate = useNavigate({ from: '/posts' })\n const handleClick = () => navigate({ search: { page: 2 } })\n // ...\n}\n\nfunction Component() {\n const navigate = useNavigate()\n return (\n \u003cdiv>\n \u003cbutton\n onClick={() =>\n navigate({\n to: '/posts',\n })\n }\n >\n Posts\n \u003c/button>\n \u003cbutton\n onClick={() =>\n navigate({\n to: '/posts',\n search: { page: 2 },\n })\n }\n >\n Posts (Page 2)\n \u003c/button>\n \u003cbutton\n onClick={() =>\n navigate({\n to: '/posts',\n hash: 'my-hash',\n })\n }\n >\n Posts (Hash)\n \u003c/button>\n \u003cbutton\n onClick={() =>\n navigate({\n to: '/posts',\n state: { from: 'home' },\n })\n }\n >\n Posts (State)\n \u003c/button>\n \u003c/div>\n )\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2044,"content_sha256":"026730af7dec16484bf7dc24ddd591d1d12acdd0fa141d5e3bc80fe28bb19af3"},{"filename":"references/docs/router/api/router/useParamsHook.md","content":"---\nid: useParamsHook\ntitle: useParams hook\n---\n\nThe `useParams` method returns all of the path parameters that were parsed for the closest match and all of its parent matches.\n\n## useParams options\n\nThe `useParams` hook accepts an optional `options` object.\n\n### `opts.strict` option\n\n- Type: `boolean`\n- Optional - `default: true`\n- If `false`, the `opts.from` option will be ignored and types will be loosened to `Partial\u003cAllParams>` to reflect the shared types of all params.\n\n### `opts.shouldThrow` option\n\n- Type: `boolean`\n- Optional\n- `default: true`\n- If `false`,`useParams` will not throw an invariant exception in case a match was not found in the currently rendered matches; in this case, it will return `undefined`.\n\n### `opts.select` option\n\n- Optional\n- `(params: AllParams) => TSelected`\n- If supplied, this function will be called with the params object and the return value will be returned from `useParams`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useParams returns\n\n- An object of the match's and parent match path params or `TSelected` if a `select` function is provided.\n\n## Examples\n\n```tsx\nimport { useParams } from '@tanstack/react-router'\n\nconst routeApi = getRouteApi('/posts/$postId')\n\nfunction Component() {\n const params = useParams({ from: '/posts/$postId' })\n\n // OR\n\n const routeParams = routeApi.useParams()\n\n // OR\n\n const postId = useParams({\n from: '/posts/$postId',\n select: (params) => params.postId,\n })\n\n // OR\n\n const looseParams = useParams({ strict: false })\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1872,"content_sha256":"5da1560eb41b40c518bdccdf5483e8fbedb20502c84e68b8070a01075412b3dc"},{"filename":"references/docs/router/api/router/useParentMatchesHook.md","content":"---\nid: useParentMatchesHook\ntitle: useParentMatches hook\n---\n\nThe `useParentMatches` hook returns all of the parent [`RouteMatch`](./RouteMatchType.md) objects from the root down to the immediate parent of the current match in context. **It does not include the current match, which can be obtained using the `useMatch` hook.**\n\n> [!IMPORTANT]\n> If the router has pending matches and they are showing their pending component fallbacks, `router.state.pendingMatches` will used instead of `router.state.matches`.\n\n## useParentMatches options\n\nThe `useParentMatches` hook accepts an optional `options` object.\n\n### `opts.select` option\n\n- Optional\n- `(matches: RouteMatch[]) => TSelected`\n- If supplied, this function will be called with the route matches and the return value will be returned from `useParentMatches`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useParentMatches returns\n\n- If a `select` function is provided, the return value of the `select` function.\n- If no `select` function is provided, an array of [`RouteMatch`](./RouteMatchType.md) objects.\n\n## Examples\n\n```tsx\nimport { useParentMatches } from '@tanstack/react-router'\n\nfunction Component() {\n const parentMatches = useParentMatches()\n // ^ [RouteMatch, RouteMatch, ...]\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1591,"content_sha256":"3c7d78ea27d148de44eb8d73f3cfe42a76fd4dcac4b214cc89935075c8338720"},{"filename":"references/docs/router/api/router/useRouteContextHook.md","content":"---\nid: useRouteContextHook\ntitle: useRouteContext hook\n---\n\nThe `useRouteContext` method is a hook that returns the current context for the current route. This hook is useful for accessing the current route context in a component.\n\n## useRouteContext options\n\nThe `useRouteContext` hook accepts an `options` object.\n\n### `opts.from` option\n\n- Type: `string`\n- Required\n- The RouteID to match the route context from.\n\n### `opts.select` option\n\n- Type: `(context: RouteContext) => TSelected`\n- Optional\n- If supplied, this function will be called with the route context object and the return value will be returned from `useRouteContext`.\n\n## useRouteContext returns\n\n- The current context for the current route or `TSelected` if a `select` function is provided.\n\n## Examples\n\n```tsx\nimport { useRouteContext } from '@tanstack/react-router'\n\nfunction Component() {\n const context = useRouteContext({ from: '/posts/$postId' })\n // ^ RouteContext\n\n // OR\n\n const selected = useRouteContext({\n from: '/posts/$postId',\n select: (context) => context.postId,\n })\n // ^ string\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1103,"content_sha256":"3ff5819021295dd65d4edbcb4b4c69d21513d54ecbc7cf3e2809c1ba19670dbe"},{"filename":"references/docs/router/api/router/useRouterHook.md","content":"---\nid: useRouterHook\ntitle: useRouter hook\n---\n\nThe `useRouter` method is a hook that returns the current instance of [`Router`](./RouterType.md) from context. This hook is useful for accessing the router instance in a component.\n\n## useRouter returns\n\n- The current [`Router`](./RouterType.md) instance.\n\n> **`router.state` is always up to date, but NOT REACTIVE. If you use `router.state` in a component, the component will not re-render when the router state changes. To get a reactive version of the router state, use the [`useRouterState`](./useRouterStateHook.md) hook.**\n\n## Examples\n\n```tsx\nimport { useRouter } from '@tanstack/react-router'\n\nfunction Component() {\n const router = useRouter()\n // ^ Router\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":738,"content_sha256":"25cb5ae020a76b912c69803795d065d8c57589b7b51cd1baa4f6cbc881d29e8a"},{"filename":"references/docs/router/api/router/useRouterStateHook.md","content":"---\nid: useRouterStateHook\ntitle: useRouterState hook\n---\n\nThe `useRouterState` method is a hook that returns the current internal state of the router. This hook is useful for accessing the current state of the router in a component.\n\n> [!TIP]\n> If you want to access the current location or the current matches, you should try out the [`useLocation`](./useLocationHook.md) and [`useMatches`](./useMatchesHook.md) hooks first. These hooks are designed to be more ergonomic and easier to use than accessing the router state directly.\n\n## useRouterState options\n\nThe `useRouterState` hook accepts an optional `options` object.\n\n### `opts.select` option\n\n- Type: `(state: RouterState) => TSelected`\n- Optional\n- If supplied, this function will be called with the [`RouterState`](./RouterStateType.md) object and the return value will be returned from `useRouterState`.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n## useRouterState returns\n\n- The current [`RouterState`](./RouterStateType.md) object or `TSelected` if a `select` function is provided.\n\n## Examples\n\n```tsx\nimport { useRouterState } from '@tanstack/react-router'\n\nfunction Component() {\n const state = useRouterState()\n // ^ RouterState\n\n // OR\n\n const selected = useRouterState({\n select: (state) => state.location,\n })\n // ^ ParsedLocation\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1543,"content_sha256":"9948f3dfbb15cdf47535b8336cc21a294b62c6ee2e2e3a8121928415a27d63cb"},{"filename":"references/docs/router/api/router/useSearchHook.md","content":"---\nid: useSearchHook\ntitle: useSearch hook\n---\n\nThe `useSearch` method is a hook that returns the current search query parameters as an object for the current location. This hook is useful for accessing the current search string and query parameters in a component.\n\n## useSearch options\n\nThe `useSearch` hook accepts an `options` object.\n\n### `opts.from` option\n\n- Type: `string`\n- Required\n- The RouteID to match the search query parameters from.\n\n### `opts.shouldThrow` option\n\n- Type: `boolean`\n- Optional\n- `default: true`\n- If `false`,`useSearch` will not throw an invariant exception in case a match was not found in the currently rendered matches; in this case, it will return `undefined`.\n\n### `opts.select` option\n\n- Type: `(search: SelectedSearchSchema) => TSelected`\n- Optional\n- If supplied, this function will be called with the search object and the return value will be returned from `useSearch`.\n\n### `opts.structuralSharing` option\n\n- Type: `boolean`\n- Optional\n- Configures whether structural sharing is enabled for the value returned by `select`.\n- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information.\n\n### `opts.strict` option\n\n- Type: `boolean`\n- Optional - `default: true`\n- If `false`, the `opts.from` option will be ignored and types will be loosened to `Partial\u003cFullSearchSchema>` to reflect the shared types of all search query parameters.\n\n## useSearch returns\n\n- If `opts.from` is provided, an object of the search query parameters for the current location or `TSelected` if a `select` function is provided.\n- If `opts.strict` is `false`, an object of the search query parameters for the current location or `TSelected` if a `select` function is provided.\n\n## Examples\n\n```tsx\nimport { useSearch } from '@tanstack/react-router'\n\nfunction Component() {\n const search = useSearch({ from: '/posts/$postId' })\n // ^ FullSearchSchema\n\n // OR\n\n const selected = useSearch({\n from: '/posts/$postId',\n select: (search) => search.postView,\n })\n // ^ string\n\n // OR\n\n const looseSearch = useSearch({ strict: false })\n // ^ Partial\u003cFullSearchSchema>\n\n // ...\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2152,"content_sha256":"c9a081cfea20127abbaa3d52572b90dab0bf99839e342ac520df855d483f8a14"},{"filename":"references/docs/router/api/router/ViewTransitionOptionsType.md","content":"---\nid: ViewTransitionOptionsType\ntitle: ViewTransitionOptions type\n---\n\nThe `ViewTransitionOptions` type is used to define a\nviewTransition type.\n\n```tsx\ninterface ViewTransitionOptions {\n types:\n | Array\u003cstring>\n | ((locationChangeInfo: {\n fromLocation?: ParsedLocation\n toLocation: ParsedLocation\n pathChanged: boolean\n hrefChanged: boolean\n hashChanged: boolean\n }) => Array\u003cstring> | false)\n}\n```\n\n## ViewTransitionOptions properties\n\nThe `ViewTransitionOptions` type accepts an object with a single property:\n\n### `types` property\n\n- Type: `Array\u003cstring> | ((locationChangeInfo: {\n fromLocation?: ParsedLocation\n toLocation: ParsedLocation\n pathChanged: boolean\n hrefChanged: boolean\n hashChanged: boolean\n}) => (Array\u003cstring> | false))`\n- Required\n- Either one of:\n - An array of strings that will be passed to the `document.startViewTransition({update, types}) call`\n - A function that accepts `locationChangeInfo` object and returns either:\n - An array of strings that will be passed to the `document.startViewTransition({update, types}) call`\n - or `false` to skip the view transition\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1155,"content_sha256":"9208ee8f62bbc3ff49aa87d7b66082438a773e726babe1120a43ec2ad3363e7f"},{"filename":"references/docs/router/comparison.md","content":"---\ntitle: Comparison\ntoc: false\n---\n\nChoosing a routing solution? This side‑by‑side comparison highlights key features, trade‑offs, and common use cases to help you quickly evaluate how each option fits your project’s needs.\n\nWhile we aim to provide an accurate and fair comparison, please note that this table may not capture every nuance or recent update of each library. We recommend reviewing the official documentation and trying out each solution to make the most informed decision for your specific use case.\n\nIf you find any discrepancies or have suggestions for improvement, please don't hesitate to contribute via the \"Edit this page on GitHub\" link at the bottom of this page or open an issue in the TanStack Router GitHub repository.\n\nFeature/Capability Key:\n\n- 1st-class, built-in, and ready to use with no added configuration or code\n- Partial Support (on a scale of 5)\n- Supported via addon/community package\n- Possible, but requires custom code/implementation/casting\n- Not officially supported\n\n\n\n# React\n\n## Comparison | TanStack Router & TanStack Start vs Next.js vs React Router / Remix\n\n| | TanStack Router / Start | React Router DOM [_(Website)_][router] | Next.JS [_(Website)_][nextjs] |\n| ---------------------------------------------- | ------------------------------------------------ | ----------------------------------------------------- | ----------------------------------------------------- |\n| Github Repo / Stars | [![][stars-tanstack-router]][gh-tanstack-router] | [![][stars-router]][gh-router] | [![][stars-nextjs]][gh-nextjs] |\n| Bundle Size | [![][bp-tanstack-router]][bpl-tanstack-router] | [![][bp-router]][bpl-router] | |\n| History, Memory & Hash Routers | | | |\n| Nested / Layout Routes | | | |\n| Suspense-like Route Transitions | | | |\n| Typesafe Routes | | (1/5) | |\n| Code-based Routes | | | |\n| File-based Routes | | | |\n| Virtual/Programmatic File-based Routes | | | |\n| Router Loaders | | | |\n| SWR Loader Caching | | | |\n| Route Prefetching | | | |\n| Auto Route Prefetching | | | |\n| Route Prefetching Delay | | | |\n| Path Params | | | |\n| Typesafe Path Params | | | |\n| Typesafe Route Context | | | |\n| Path Param Validation | | | |\n| Custom Path Param Parsing/Serialization | | | |\n| Ranked Routes | | | |\n| Active Link Customization | | | |\n| Optimistic UI | | | |\n| Typesafe Absolute + Relative Navigation | | (1/5 via `buildHref` util) | (IDE plugin) |\n| Route Mount/Transition/Unmount Events | | | |\n| Devtools | | | |\n| Basic Search Params | | | |\n| Search Param Hooks | | | |\n| `\u003cLink/>`/`useNavigate` Search Param API | | (search-string only via the `to`/`search` options) | (search-string only via the `to`/`search` options) |\n| JSON Search Params | | | |\n| TypeSafe Search Params | | | |\n| Search Param Schema Validation | | | |\n| Search Param Immutability + Structural Sharing | | | |\n| Custom Search Param parsing/serialization | | | |\n| Search Param Middleware | | | |\n| Suspense Route Elements | | | |\n| Route Error Elements | | | |\n| Route Pending Elements | | | |\n| `\u003cBlock>`/`useBlocker` | | (no hard reloads or cross-origin navigation) | |\n| Deferred Primitives | | | |\n| Navigation Scroll Restoration | | | |\n| ElementScroll Restoration | | | |\n| Async Scroll Restoration | | | |\n| Router Invalidation | | | |\n| Runtime Route Manipulation (Fog of War) | | | |\n| Parallel Routes | | | |\n| -- | -- | -- | -- |\n| **Full Stack** | -- | -- | -- |\n| SSR | | | |\n| Streaming SSR | | | |\n| Generic RPCs | | | |\n| Generic RPC Middleware | | | |\n| React Server Functions | | | |\n| React Server Function Middleware | | | |\n| API Routes | | | |\n| API Middleware | | | |\n| React Server Components | | (Experimental) | |\n| `\u003cForm>` API | | | |\n\n[bp-tanstack-router]: https://badgen.net/bundlephobia/minzip/@tanstack/react-router\n[bpl-tanstack-router]: https://bundlephobia.com/result?p=@tanstack/react-router\n[gh-tanstack-router]: https://github.com/tanstack/router\n[stars-tanstack-router]: https://img.shields.io/github/stars/tanstack/router?label=%F0%9F%8C%9F\n[_]: _\n[router]: https://github.com/remix-run/react-router\n[bp-router]: https://badgen.net/bundlephobia/minzip/react-router\n[gh-router]: https://github.com/remix-run/react-router\n[stars-router]: https://img.shields.io/github/stars/remix-run/react-router?label=%F0%9F%8C%9F\n[bpl-router]: https://bundlephobia.com/result?p=react-router\n[bpl-history]: https://bundlephobia.com/result?p=history\n[_]: _\n[nextjs]: https://nextjs.org/docs/routing/introduction\n[bp-nextjs]: https://badgen.net/bundlephobia/minzip/next.js?label=All\n[gh-nextjs]: https://github.com/vercel/next.js\n[stars-nextjs]: https://img.shields.io/github/stars/vercel/next.js?label=%F0%9F%8C%9F\n[bpl-nextjs]: https://bundlephobia.com/result?p=next\n\n# Solid\n\n---\n\nWe don't have a comparison table for Solid just yet. If you're interested in helping us create one, please reach out in the TanStack Discord or open a PR with your proposed comparison!\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14657,"content_sha256":"9b3e9d04da4e6fef01d785dbbde02866748848228a53ea7628a4fc8f5efccec4"},{"filename":"references/docs/router/decisions-on-dx.md","content":"---\ntitle: Decisions on Developer Experience\n---\n\nWhen people first start using TanStack Router, they often have a lot of questions that revolve around the following themes:\n\n> Why do I have to do things this way?\n\n> Why is it done this way? and not that way?\n\n> I'm used to doing it this way, why should I change?\n\nAnd 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.\n\nBut 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.\n\n> [!TIP]\n> The examples in this guide use React for components and code snippets, but the same principles apply to Solid. The only difference is in the syntax and API of the framework, but the underlying concepts and design decisions are the same.\n\n## TanStack Router's origin story\n\nIt's important to remember that TanStack Router's origins stem from 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.\n\nAnd 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.\n\n## How does TanStack Router achieve this?\n\n> TypeScript! TypeScript! TypeScript!\n\nEvery 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.\n\nBut to achieve this, we had to make some decisions that deviate from the norms in the routing world.\n\n1. [**Route configuration boilerplate?**](#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.\n2. [**TypeScript module declaration for the router?**](#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.\n3. [**Why push for file-based routing over code-based?**](#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.\n\n> 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.\n\n## Why is the Router's configuration done this way?\n\nWhen 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.\n\nThis 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.\n\n> Can I use JSX to define my routes?\n\nUsing 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.\n\n```tsx\n// ⛔️ This is not possible\nfunction App() {\n return (\n \u003cRouter>\n \u003cRoute path=\"/posts\" component={PostsPage} />\n \u003cRoute path=\"/posts/$postId\" component={PostIdPage} />\n {/* ... */}\n \u003c/Router>\n // ^? TypeScript cannot infer the routes in this configuration\n )\n}\n```\n\nAnd since this would mean that you'd have to manually type the `to` prop of the `\u003cLink>` component and wouldn't catch any errors until runtime, it's not a viable option.\n\n> Maybe I could define my routes as a tree of nested objects?\n\n```tsx\n// ⛔️ This file will just keep growing and growing...\nconst router = createRouter({\n routes: {\n posts: {\n component: PostsPage, // /posts\n children: {\n $postId: {\n component: PostIdPage, // /posts/$postId\n },\n },\n },\n // ...\n },\n})\n```\n\nAt 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:\n\n- **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.\n- **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.\n\nThis only gets worse as you begin to use more features of the router, such as nested context, loaders, search param validation, etc.\n\n> So, what's the best way to define my routes?\n\nWhat 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.\n\nYou can read more about [code-based routing](./routing/code-based-routing.md) to see how to define your routes in this way.\n\n> [!TIP]\n> Finding Code-based routing to be a bit too cumbersome? See why [file-based routing](#why-is-file-based-routing-the-preferred-way-to-define-routes) is the preferred way to define your routes.\n\n## Declaring the Router instance for type inference\n\n> Why do I have to declare the `Router`?\n\n> This declaration stuff is way too complicated for me...\n\nOnce 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.\n\nThere were two approaches we considered for this:\n\n1. **Imports**: You could import the `Router` instance from the file where you created it and use it directly in your components.\n\n```tsx\nimport { router } from '@/src/app'\nexport const PostsIdLink = () => {\n return (\n \u003cLink\u003ctypeof router> to=\"/posts/$postId\" params={{ postId: '123' }}>\n Go to post 123\n \u003c/Link>\n )\n}\n```\n\nA 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.\n\n2. **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.\n\nYou'll do this once in your application.\n\n```tsx\n// src/app.tsx\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n```\n\nAnd then you can benefit from its auto-complete anywhere in your app without having to import it.\n\n```tsx\nexport const PostsIdLink = () => {\n return (\n \u003cLink\n to=\"/posts/$postId\"\n // ^? TypeScript will auto-complete this for you\n params={{ postId: '123' }} // and this too!\n >\n Go to post 123\n \u003c/Link>\n )\n}\n```\n\nWe 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.\n\n## Why is file-based routing the preferred way to define routes?\n\n> Why are the docs pushing for file-based routing?\n\n> I'm used to defining my routes in a single file, why should I change?\n\nSomething 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.\n\n> [!TIP]\n> 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).\n\nAs 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.\n\nA 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.\n\n```tsx\nimport { createRoute } from '@tanstack/react-router'\nimport { postsRoute } from './postsRoute'\n\nexport const postsIndexRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '/',\n})\n```\n\nAt 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.\n\nAs such, this is a critical part of the route configuration and a point of failure if not done correctly.\n\nBut 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.\n\n> If this route-tree were in its own file for an application with ~40-50 routes, it can easily grow up to 700+ lines.\n\n```tsx\nconst routeTree = rootRoute.addChildren([\n postsRoute.addChildren([postsIndexRoute, postsIdRoute]),\n])\n```\n\nThis 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.\n\nFinally, 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.\n\n```tsx\nimport { createRoute, lazyRouteComponent } from '@tanstack/react-router'\nimport { postsRoute } from './postsRoute'\n\nexport const postsIndexRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '/',\n component: lazyRouteComponent(() => import('../page-components/posts/index')),\n})\n```\n\nAll 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.\n\n... 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 .\n\n> So, why's file-based routing the preferred way?\n\nTanStack 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.\n\nThe 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:\n\n1. **Route configuration boilerplate**: It generates the boilerplate for your route configurations.\n2. **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.\n3. **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.\n\nLet's take a look at how the route configuration for the previous example would look like with file-based routing.\n\n```tsx\n// src/routes/posts/index.ts\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/')({\n component: () => 'Posts index component goes here!!!',\n})\n```\n\nThat'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.\n\nAt 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.\n\nCheck 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.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13758,"content_sha256":"7cb7b25a49dc70c463f437cb70b3c47cc062e7485064d654125ff961e46f73ee"},{"filename":"references/docs/router/devtools.md","content":"---\ntitle: Devtools\n---\n\n> Link, take this sword... I mean Devtools!... to help you on your way!\n\nWave your hands in the air and shout hooray because TanStack Router comes with dedicated devtools! \n\nWhen you begin your TanStack Router journey, you'll want these devtools by your side. They help visualize all of the inner workings of TanStack Router and will likely save you hours of debugging if you find yourself in a pinch!\n\n## Installation\n\nThe devtools are a separate package that you need to install:\n\n\n\nreact: @tanstack/react-router-devtools\nsolid: @tanstack/solid-router-devtools\n\n\n\n## Import the Devtools\n\n\n\n# React\n\n```tsx\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n```\n\n# Solid\n\n```tsx\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n```\n\n\n\n## Using Devtools in production\n\nThe Devtools, if imported as `TanStackRouterDevtools` will not be shown in production. If you want to have devtools in an environment with `process.env.NODE_ENV === 'production'`, use instead `TanStackRouterDevtoolsInProd`, which has all the same options:\n\n\n\n# React\n\n```tsx\nimport { TanStackRouterDevtoolsInProd } from '@tanstack/react-router-devtools'\n```\n\n# Solid\n\n```tsx\nimport { TanStackRouterDevtoolsInProd } from '@tanstack/solid-router-devtools'\n```\n\n\n\n## Using the Devtools in the root route\n\nThe easiest way for the devtools to work is to render them inside of your root route (or any other route). This will automatically connect the devtools to the router instance.\n\n\n\n# React\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003c>\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/>\n ),\n})\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRoute, Outlet } from '@tanstack/solid-router'\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003c>\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/>\n ),\n})\n```\n\n\n\n\n\n## Manually passing the Router Instance\n\nIf rendering the devtools inside of the `RouterProvider` isn't your cup of tea, a `router` prop for the devtools accepts the same `router` instance you pass to the `Router` component. This makes it possible to place the devtools anywhere on the page, not just inside the provider:\n\n\n\n# React\n\n```tsx\nfunction App() {\n return (\n \u003c>\n \u003cRouterProvider router={router} />\n \u003cTanStackRouterDevtools router={router} />\n \u003c/>\n )\n}\n```\n\n# Solid\n\n```tsx\nfunction App() {\n return (\n \u003c>\n \u003cRouterProvider router={router} />\n \u003cTanStackRouterDevtools router={router} />\n \u003c/>\n )\n}\n```\n\n\n\n## Floating Mode\n\nFloating Mode will mount the devtools as a fixed, floating element in your app and provide a toggle in the corner of the screen to show and hide the devtools. This toggle state will be stored and remembered in localStorage across reloads.\n\nPlace the following code as high in your app as you can. The closer it is to the root of the page, the better it will work!\n\n\n\n# React\n\n```tsx\nfunction App() {\n return (\n \u003c>\n \u003cRouterProvider router={router} />\n \u003cTanStackRouterDevtools initialIsOpen={false} />\n \u003c/>\n )\n}\n```\n\n# Solid\n\n```tsx\nfunction App() {\n return (\n \u003c>\n \u003cRouterProvider router={router} />\n \u003cTanStackRouterDevtools initialIsOpen={false} />\n \u003c/>\n )\n}\n```\n\n\n\n### Devtools Options\n\n- `router: Router`\n - The router instance to connect to.\n- `initialIsOpen: Boolean`\n - Set this `true` if you want the devtools to default to being open.\n- `panelProps: PropsObject`\n - Use this to add props to the panel. For example, you can add `className`, `style` (merge and override default style), etc.\n- `closeButtonProps: PropsObject`\n - Use this to add props to the close button. For example, you can add `className`, `style` (merge and override default style), `onClick` (extend default handler), etc.\n- `toggleButtonProps: PropsObject`\n - Use this to add props to the toggle button. For example, you can add `className`, `style` (merge and override default style), `onClick` (extend default handler), etc.\n- `position?: \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\"`\n - Defaults to `bottom-left`.\n - The position of the TanStack Router logo to open and close the devtools panel.\n- `shadowDOMTarget?: ShadowRoot`\n - Specifies a Shadow DOM target for the devtools.\n - By default, devtool styles are applied to the `\u003chead>` tag of the main document (light DOM). When a `shadowDOMTarget` is provided, styles will be applied within this Shadow DOM instead.\n- `containerElement?: string | any`\n - Use this to render the devtools inside a different type of container element for ally purposes.\n - Any string which corresponds to a valid intrinsic JSX element is allowed.\n - Defaults to 'footer'.\n\n## Fixed Mode\n\nTo control the position of the devtools, import the `TanStackRouterDevtoolsPanel`:\n\n\n\n# React\n\n```tsx\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'\n```\n\n# Solid\n\n```tsx\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/solid-router-devtools'\n```\n\n\n\nIt can then be attached to provided shadow DOM target:\n\n\n\n# React\n\n```tsx\n\u003cTanStackRouterDevtoolsPanel\n shadowDOMTarget={shadowContainer}\n router={router}\n/>\n```\n\n# Solid\n\n```tsx\n\u003cTanStackRouterDevtoolsPanel\n shadowDOMTarget={shadowContainer}\n router={router}\n/>\n```\n\n\n\nClick here to see a live example of this in StackBlitz.\n\n## Embedded Mode\n\nEmbedded Mode will embed the devtools as a regular component in your application. You can style it however you'd like after that!\n\n\n\n# React\n\n```tsx\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'\n\nfunction App() {\n return (\n \u003c>\n \u003cRouterProvider router={router} />\n \u003cTanStackRouterDevtoolsPanel\n router={router}\n style={styles}\n className={className}\n />\n \u003c/>\n )\n}\n```\n\n# Solid\n\n```tsx\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/solid-router-devtools'\n\nfunction App() {\n return (\n \u003c>\n \u003cRouterProvider router={router} />\n \u003cTanStackRouterDevtoolsPanel\n router={router}\n style={styles}\n class={className}\n />\n \u003c/>\n )\n}\n```\n\n\n\n### DevtoolsPanel Options\n\n- `router: Router`\n - The router instance to connect to.\n\n\n\n# React\n\n- `style?: StyleObject`\n - The standard React style object used to style a component with inline styles.\n- `className?: string`\n - The standard React className property used to style a component with classes.\n\n# Solid\n\n- `style?: StyleObject`\n - The standard Solid style object used to style a component with inline styles.\n- `class?: string`\n - The standard Solid class property used to style a component with classes.\n\n\n\n- `isOpen?: boolean`\n - A boolean variable indicating whether the panel is open or closed.\n- `setIsOpen?: (isOpen: boolean) => void`\n - A function that toggles the open and close state of the panel.\n- `handleDragStart?: (e: any) => void`\n - Handles the opening and closing the devtools panel.\n- `shadowDOMTarget?: ShadowRoot`\n - Specifies a Shadow DOM target for the devtools.\n - By default, devtool styles are applied to the `\u003chead>` tag of the main document (light DOM). When a `shadowDOMTarget` is provided, styles will be applied within this Shadow DOM instead.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7487,"content_sha256":"4b1fac6a5fdc7d5661899c9d1374357e4f604e7aedf2c19c415fa347afba7d16"},{"filename":"references/docs/router/eslint/create-route-property-order.md","content":"---\nid: create-route-property-order\ntitle: Ensure correct order of inference sensitive properties for createRoute functions\n---\n\nFor the following functions, the property order of the passed in object matters due to type inference:\n\n- `createRoute`\n- `createFileRoute`\n- `createRootRoute`\n- `createRootRouteWithContext`\n\nThe correct property order is as follows\n\n- `params`, `validateSearch`\n- `loaderDeps`, `search.middlewares`, `ssr`\n- `context`\n- `beforeLoad`\n- `loader`\n- `onEnter`, `onStay`, `onLeave`, `head`, `scripts`, `headers`, `remountDeps`\n\nAll other properties are insensitive to the order as they do not depend on type inference.\n\n## Rule Details\n\nExamples of **incorrect** code for this rule:\n\n\n\n# React\n\n\n\n```tsx title=\"src/routes/path.tsx\"\n/* eslint \"@tanstack/router/create-route-property-order\": \"warn\" */\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/path')({\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(getQueryOptions(context.hello))\n },\n beforeLoad: () => ({ hello: 'world' }),\n})\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/routes/path.tsx\"\n/* eslint \"@tanstack/router/create-route-property-order\": \"warn\" */\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/path')({\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(getQueryOptions(context.hello))\n },\n beforeLoad: () => ({ hello: 'world' }),\n})\n```\n\n\n\n\n\nExamples of **correct** code for this rule:\n\n\n\n# React\n\n\n\n```tsx title=\"src/routes/path.tsx\"\n/* eslint \"@tanstack/router/create-route-property-order\": \"warn\" */\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/path')({\n beforeLoad: () => ({ hello: 'world' }),\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(getQueryOptions(context.hello))\n },\n})\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/routes/path.tsx\"\n/* eslint \"@tanstack/router/create-route-property-order\": \"warn\" */\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/path')({\n beforeLoad: () => ({ hello: 'world' }),\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(getQueryOptions(context.hello))\n },\n})\n```\n\n\n\n\n\n## Attributes\n\n- [x] Recommended\n- [x] Fixable\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2371,"content_sha256":"f6dd6fb9435351fb4f61bf2fa840699c46af860acf0711858072d07facbe185e"},{"filename":"references/docs/router/eslint/eslint-plugin-router.md","content":"---\nid: eslint-plugin-router\ntitle: ESLint Plugin Router\n---\n\nTanStack Router comes with its own ESLint plugin. This plugin is used to enforce best practices and to help you avoid common mistakes.\n\n## Installation\n\nThe plugin is a separate package that you need to install:\n\n\n\nreact: @tanstack/eslint-plugin-router\nsolid: @tanstack/eslint-plugin-router\n\n\n\n## Flat Config (`eslint.config.js`)\n\nThe release of ESLint 9.0 introduced a new way to configure ESLint using a flat config format. This new format is more flexible and allows you to configure ESLint in a more granular way than the legacy `.eslintrc` format. The TanStack Router ESLint Plugin supports this new format and provides a recommended config that you can use to enable all of the recommended rules for the plugin\n.\n\n### Recommended Flat Config setup\n\nTo enable all of the recommended rules for our plugin, add the following config:\n\n\n\n```js title=\"eslint.config.js\"\nimport pluginRouter from '@tanstack/eslint-plugin-router'\n\nexport default [\n ...pluginRouter.configs['flat/recommended'],\n // Any other config...\n]\n```\n\n\n\n### Custom Flat Config setup\n\nAlternatively, you can load the plugin and configure only the rules you want to use:\n\n\n\n```js title=\"eslint.config.js\"\nimport pluginRouter from '@tanstack/eslint-plugin-router'\n\nexport default [\n {\n plugins: {\n '@tanstack/router': pluginRouter,\n },\n rules: {\n '@tanstack/router/create-route-property-order': 'error',\n },\n },\n // Any other config...\n]\n```\n\n\n\n## Legacy Config (`.eslintrc`)\n\nPrior to the ESLint 9.0 release, the most common way of configuring EsLint was using a `.eslintrc` file. The TanStack Router ESLint Plugin still supports this configuration method.\n\n### Recommended Legacy Config setup\n\nTo enable all of the recommended rules for our plugin, add `plugin:@tanstack/eslint-plugin-router/recommended` in extends:\n\n```json\n{\n \"extends\": [\"plugin:@tanstack/eslint-plugin-router/recommended\"]\n}\n```\n\n### Custom Legacy Config setup\n\nAlternatively, add `@tanstack/eslint-plugin-router` to the plugins section, and configure the rules you want to use:\n\n```json\n{\n \"plugins\": [\"@tanstack/eslint-plugin-router\"],\n \"rules\": {\n \"@tanstack/router/create-route-property-order\": \"error\"\n }\n}\n```\n\n## Rules\n\nThe following rules are available in the TanStack Router ESLint Plugin:\n\n- [@tanstack/router/create-route-property-order](./create-route-property-order.md)\n\n## Conflicts with other ESLint plugins\n\nIf you have other ESLint plugins installed, they may rules that conflict with this plugin. If so, you'll need to make some tweaks to allow these plugins to work together.\n\n### `typescript-eslint`\n\nThe `@typescript-eslint/only-throw-error` rule, enabled by default in the `recommended-type-checked` and `strict-type-checked` rulesets, disallows the throwing of non-Error values as exceptions, which is considered a good practice.\n\nTo ensure it does not conflict with TanStack Router, you should allow `redirect` and `notFound` as throwable objects.\n\n```json\n{\n \"rules\": {\n \"@typescript-eslint/only-throw-error\": [\n \"error\",\n {\n \"allow\": [\n {\n \"from\": \"package\",\n \"package\": \"@tanstack/router-core\",\n \"name\": \"Redirect\"\n },\n {\n \"from\": \"package\",\n \"package\": \"@tanstack/router-core\",\n \"name\": \"NotFoundError\"\n }\n ]\n }\n ]\n }\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3428,"content_sha256":"0ec4cca9e4ff345bb726005cccf12c87215b5ed2b8e4b2f8e404229c267617cd"},{"filename":"references/docs/router/faq.md","content":"---\ntitle: Frequently Asked Questions\n---\n\nWelcome 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.\n\n## Why should you choose TanStack Router over another router?\n\nTo answer this question, it's important to view the other options in the space. There are many alternatives to choose from, but only a couple that are widely adopted and actively maintained:\n\n- **Next.js** - Widely regarded as the leading framework for starting new React projects. Its design focuses on performance, development workflows, and cutting-edge technology. The framework's APIs and abstractions, while powerful, can sometimes present as non-standard. Rapid growth and industry adoption have resulted in a feature-rich experience, sometimes leading to a steeper learning curve and increased overhead.\n- **Remix / React Router** - Based on the historically successful React Router, Remix delivers a powerful developer and user experience. Its API and architectural vision are firmly rooted in web standards such as Request/Response, with an emphasis on adaptability across various JavaScript environments. Many of its APIs and abstractions are well-designed and have influenced more than a few of TanStack Router's APIs. However, its rigid design, the integration of type safety as an add-on, and sometimes strict adherence to platform APIs can present limitations for some developers.\n\nThese frameworks and routers have their strengths, but they also come with trade-offs that may not align with every project's needs. TanStack Router aims to strike a balance by offering routing APIs designed to improve the developer experience without sacrificing flexibility or performance.\n\n## Is TanStack Router a framework?\n\nTanStack Router itself is not a \"framework\" in the traditional sense, since it doesn't address a few other common full-stack concerns. However, TanStack Router has been designed to be upgradable to a full-stack framework when used in conjunction with other tools that address bundling, deployments, and server-side-specific functionality. This is why we are currently developing TanStack Start, a full-stack framework that is built on top of TanStack Router and Vite.\nFor a deeper dive on the history of TanStack Router, feel free to read [TanStack Router's History](./decisions-on-dx.md#tanstack-routers-origin-story).\n\n## Should I commit my `routeTree.gen.ts` file into git?\n\nYes! Although the route tree file (i.e., `routeTree.gen.ts`) is generated by TanStack Router, it is essentially part of your application’s runtime, not a build artifact. The route tree file is a critical part of your application’s source code, and it is used by TanStack Router to build your application’s routes at runtime.\n\nYou should commit this file into git so that other developers can use it to build your application.\n\n## Can I conditionally render the Root Route component?\n\nNo, the root route is always rendered as it is the entry point of your application.\nIf you need to conditionally render a route's component, this usually means that the page content needs to be different based on some condition (e.g. user authentication). For this use case, you should use a [Layout Route](./routing/routing-concepts.md#layout-routes) or a [Pathless Layout Route](./routing/routing-concepts.md#pathless-layout-routes) to conditionally render the content.\n\nYou can restrict access to these routes using a conditional check in the `beforeLoad` function of the route.\n\n\u003cdetails>\n\u003csummary>What does this look like?\u003c/summary>\n\n\n\n# React\n\n```tsx\n// src/routes/_pathless-layout.tsx\nimport { createFileRoute, Outlet } from '@tanstack/react-router'\nimport { isAuthenticated } from '../utils/auth'\n\nexport const Route = createFileRoute('/_pathless-layout', {\n beforeLoad: async () => {\n // Check if the user is authenticated\n const authed = await isAuthenticated()\n if (!authed) {\n // Redirect the user to the login page\n return '/login'\n }\n },\n component: PathlessLayoutRouteComponent,\n // ...\n})\n\nfunction PathlessLayoutRouteComponent() {\n return (\n \u003cdiv>\n \u003ch1>You are authed\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx\n// src/routes/_pathless-layout.tsx\nimport { createFileRoute, Outlet } from '@tanstack/solid-router'\nimport { isAuthenticated } from '../utils/auth'\n\nexport const Route = createFileRoute('/_pathless-layout', {\n beforeLoad: async () => {\n // Check if the user is authenticated\n const authed = await isAuthenticated()\n if (!authed) {\n // Redirect the user to the login page\n return '/login'\n }\n },\n component: PathlessLayoutRouteComponent,\n // ...\n})\n\nfunction PathlessLayoutRouteComponent() {\n return (\n \u003cdiv>\n \u003ch1>You are authed\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n\n\n\u003c/details>\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4907,"content_sha256":"f5b5bea9cbe34ef432e62db74d2dd8c3aa434536732b8f0090b4451f59a1da19"},{"filename":"references/docs/router/guide/authenticated-routes.md","content":"---\nid: authenticated-routes\ntitle: Authenticated Routes\n---\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\n### Handling Auth Check Failures\n\nIf your authentication check can throw errors (network failures, token validation, etc.), wrap it in try/catch:\n\n\n\n# React\n\n```tsx\nimport { createFileRoute, redirect, isRedirect } from '@tanstack/react-router'\n\n// src/routes/_authenticated.tsx\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: async ({ location }) => {\n try {\n const user = await verifySession() // might throw on network error\n if (!user) {\n throw redirect({\n to: '/login',\n search: { redirect: location.href },\n })\n }\n return { user }\n } catch (error) {\n // Re-throw redirects (they're intentional, not errors)\n if (isRedirect(error)) throw error\n\n // Auth check failed (network error, etc.) - redirect to login\n throw redirect({\n to: '/login',\n search: { redirect: location.href },\n })\n }\n },\n})\n```\n\n# Solid\n\n```tsx\nimport { createFileRoute, redirect, isRedirect } from '@tanstack/solid-router'\n\n// src/routes/_authenticated.tsx\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: async ({ location }) => {\n try {\n const user = await verifySession() // might throw on network error\n if (!user) {\n throw redirect({\n to: '/login',\n search: { redirect: location.href },\n })\n }\n return { user }\n } catch (error) {\n // Re-throw redirects (they're intentional, not errors)\n if (isRedirect(error)) throw error\n\n // Auth check failed (network error, etc.) - redirect to login\n throw redirect({\n to: '/login',\n search: { redirect: location.href },\n })\n }\n },\n})\n```\n\n\n\nThe [`isRedirect()`](../api/router/isRedirectFunction.md) helper distinguishes between actual errors and intentional redirects.\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 `\u003cOutlet />` 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 \u003cLogin />\n }\n\n return \u003cOutlet />\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 `\u003cOutlet />` 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 `\u003cRouterProvider />` 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.\n\n\n\n# React\n\n\n\n```tsx title=\"src/routes/__root.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\u003cMyRouterContext>()({\n component: () => \u003cOutlet />,\n})\n```\n\n```tsx title=\"src/router.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```tsx title=\"src/App.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 \u003cRouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n \u003cAuthProvider>\n \u003cInnerApp />\n \u003c/AuthProvider>\n )\n}\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRouteWithContext } from '@tanstack/solid-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\u003cMyRouterContext>()({\n component: () => \u003cOutlet />,\n})\n```\n\n```tsx title=\"src/router.tsx\"\nimport { createRouter } from '@tanstack/solid-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```tsx title=\"src/App.tsx\"\nimport { RouterProvider } from '@tanstack/solid-router'\n\nimport { AuthProvider, useAuth } from './auth'\n\nimport { router } from './router'\n\nfunction InnerApp() {\n const auth = useAuth()\n return \u003cRouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n \u003cAuthProvider>\n \u003cInnerApp />\n \u003c/AuthProvider>\n )\n}\n```\n\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\n\n# React\n\n```tsx title=\"src/routes/dashboard.route.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\n# Solid\n\n```tsx title=\"src/routes/dashboard.route.tsx\"\nimport { createFileRoute, redirect } from '@tanstack/solid-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\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 - Simple authentication with context\n- Firebase Authentication - Firebase Auth integration\n- TanStack Start Auth Examples - Various auth implementations with TanStack Start\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10115,"content_sha256":"b5bf60fd7e26f2aad0582daabfd44f1c98489844db1a6bcb0569e15698a93dd5"},{"filename":"references/docs/router/guide/automatic-code-splitting.md","content":"---\ntitle: Automatic Code Splitting\n---\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\nexport const Route = createRoute('/posts')({\n // ...\n notFoundComponent: PostsNotFoundComponent,\n})\n\n// ❌ 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 // ❌\n // ...\n}\n\nfunction PostsNotFoundComponent() {\n // ✅\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 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 title=\"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 title=\"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 title=\"src/routes/posts.route.tsx\"\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","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10559,"content_sha256":"f3661e96c9330beeb027aa83388618cf8a8ac28c57ae1ad23c1ddd957e780c15"},{"filename":"references/docs/router/guide/code-splitting.md","content":"---\ntitle: Code Splitting\n---\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> **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 ](#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\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` supports:\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\n\n# React\n\n```tsx title=\"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# Solid\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/solid-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\n\n**After (Split into two files)**\n\nThis file would contain the critical route configuration:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\nimport { fetchPosts } from './api'\n\nexport const Route = createFileRoute('/posts')({\n loader: fetchPosts,\n})\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\nimport { fetchPosts } from './api'\n\nexport const Route = createFileRoute('/posts')({\n loader: fetchPosts,\n})\n```\n\n\n\nWith the non-critical route configuration going into the file with the `.lazy.tsx` suffix:\n\n\n\n# React\n\n```tsx title=\"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# Solid\n\n```tsx title=\"src/routes/posts.lazy.tsx\"\nimport { createLazyFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createLazyFileRoute('/posts')({\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\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\n\n# React\n\n\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts')({\n // Hello?\n})\n```\n\n```tsx title=\"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\n\n# Solid\n\n\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/posts')({\n // Hello?\n})\n```\n\n```tsx title=\"src/routes/posts.lazy.tsx\"\nimport { createLazyFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createLazyFileRoute('/posts')({\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\n```\n\n\n\n\n\n**After (Virtual Routes)**\n\n\n\n# React\n\n```tsx title=\"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# Solid\n\n```tsx title=\"src/routes/posts.lazy.tsx\"\nimport { createLazyFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createLazyFileRoute('/posts')({\n component: Posts,\n})\n\nfunction Posts() {\n // ...\n}\n```\n\n\n\nTada! \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 title=\"src/posts.lazy.tsx\"\nexport const Route = createLazyRoute('/posts')({\n component: MyComponent,\n})\n\nfunction MyComponent() {\n return \u003cdiv>My Component\u003c/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 title=\"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\n\n# React\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\n# Solid\n\n```tsx\nimport { lazyFn } from '@tanstack/solid-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\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\n\n# React\n\n\n\n```tsx title=\"src/my-route.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```tsx title=\"src/MyComponent.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 \u003cdiv>...\u003c/div>\n}\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/my-route.tsx\"\nimport { createRoute } from '@tanstack/solid-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```tsx title=\"src/MyComponent.tsx\"\nimport { getRouteApi } from '@tanstack/solid-router'\n\nconst route = getRouteApi('/my-route')\n\nexport function MyComponent() {\n const loaderData = route.useLoaderData()\n // ^? { foo: string }\n\n return \u003cdiv>...\u003c/div>\n}\n```\n\n\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","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12671,"content_sha256":"74d52331fc5c2d419d83a2d9787ea8d70547d4af58fb76c9829f2b61ea78121c"},{"filename":"references/docs/router/guide/creating-a-router.md","content":"---\ntitle: Creating a Router\n---\n\n## The `createRouter` function\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\n\n# React\n\n```tsx title=\"src/router.tsx\"\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n})\n```\n\n# Solid\n\n```tsx title=\"src/router.tsx\"\nimport { createRouter } from '@tanstack/solid-router'\n\nconst router = createRouter({\n // ...\n})\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! \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 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\n\n# React\n\n```tsx title=\"src/router.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\n# Solid\n\n```tsx title=\"src/router.tsx\"\ndeclare module '@tanstack/solid-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\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: () => \u003cdiv>404 Not Found\u003c/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","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3407,"content_sha256":"0b20d0b8bdcc6de0269fd760ffaeecd81fbe0929bd24e5b7774fb8a55e1f7608"},{"filename":"references/docs/router/guide/custom-link.md","content":"---\ntitle: Custom Link\n---\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\n\n# React\n\n```tsx\nimport * as React from 'react'\nimport { createLink, LinkComponent } from '@tanstack/react-router'\n\ninterface BasicLinkProps extends React.AnchorHTMLAttributes\u003cHTMLAnchorElement> {\n // Add any additional props you want to pass to the anchor element\n}\n\nconst BasicLinkComponent = React.forwardRef\u003cHTMLAnchorElement, BasicLinkProps>(\n (props, ref) => {\n return (\n \u003ca 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\u003ctypeof BasicLinkComponent> = (props) => {\n return \u003cCreatedLinkComponent preload={'intent'} {...props} />\n}\n```\n\n# Solid\n\n```tsx\nimport * as Solid from 'solid-js'\nimport { createLink, LinkComponent } from '@tanstack/solid-router'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\ntype BasicLinkProps = Solid.JSX.IntrinsicElements['a'] & {\n // Add any additional props you want to pass to the anchor element\n}\n\nconst BasicLinkComponent: Solid.Component\u003cBasicLinkProps> = (props) => (\n \u003ca {...props} class=\"block px-3 py-2 text-red-700\">\n {props.children}\n \u003c/a>\n)\n\nconst CreatedLinkComponent = createLink(BasicLinkComponent)\n\nexport const CustomLink: LinkComponent\u003ctypeof BasicLinkComponent> = (props) => {\n return \u003cCreatedLinkComponent preload={'intent'} {...props} />\n}\n```\n\n\n\nYou can then use your newly created `Link` component as any other `Link`\n\n```tsx\n\u003cCustomLink to={'/dashboard/invoices/$invoiceId'} params={{ invoiceId: 0 }} />\n```\n\n## `createLink` with third party libraries\n\nHere are some examples of how you can use `createLink` with third-party libraries.\n\n\n\n# React\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\n\n```tsx title=\"RACLink.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\n```tsx title=\"CustomRACLink.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 \u003cRACLink\n {...props}\n style={({ isHovered }) => ({\n color: isHovered ? 'red' : 'blue',\n })}\n />\n )\n}\n\nexport const Link = createLink(MyLink)\n```\n\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\n\n\n\n# React\n\n### Chakra UI example\n\n```tsx title=\"ChakraLinkComponent.tsx\"\nimport * as React from 'react'\nimport { createLink, LinkComponent } from '@tanstack/react-router'\nimport { Link } from '@chakra-ui/react'\n\ninterface ChakraLinkProps extends Omit\u003c\n React.ComponentPropsWithoutRef\u003ctypeof Link>,\n 'href'\n> {\n // Add any additional props you want to pass to the link\n}\n\nconst ChakraLinkComponent = React.forwardRef\u003c\n HTMLAnchorElement,\n ChakraLinkProps\n>((props, ref) => {\n return \u003cLink ref={ref} {...props} />\n})\n\nconst CreatedLinkComponent = createLink(ChakraLinkComponent)\n\nexport const CustomLink: LinkComponent\u003ctypeof ChakraLinkComponent> = (\n props,\n) => {\n return (\n \u003cCreatedLinkComponent\n textDecoration={'underline'}\n _hover={{ textDecoration: 'none' }}\n _focus={{ textDecoration: 'none' }}\n preload={'intent'}\n {...props}\n />\n )\n}\n```\n\n\n\n\n\n# React\n\n### MUI example\n\nThere is an example 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\n\n```tsx title=\"CustomLink.tsx\"\nimport { createLink } from '@tanstack/react-router'\nimport { Link } from '@mui/material'\n\nexport const CustomLink = createLink(Link)\n```\n\n\n\nIf the `Link` should be customized this approach can be used:\n\n\n\n```tsx title=\"CustomLink.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\u003cHTMLAnchorElement, MUILinkProps>(\n (props, ref) => \u003cLink ref={ref} {...props} />,\n)\n\nconst CreatedLinkComponent = createLink(MUILinkComponent)\n\nexport const CustomLink: LinkComponent\u003ctypeof MUILinkComponent> = (props) => {\n return \u003cCreatedLinkComponent preload={'intent'} {...props} />\n}\n\n// Can also be styled\n```\n\n\n\n#### `Button`\n\nIf a `Button` should be used as a router `Link`, the `component` should be set as `a`:\n\n\n\n```tsx title=\"CustomButtonLink.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\u003c'a'> {\n // Add any additional props you want to pass to the Button\n}\n\nconst MUIButtonLinkComponent = React.forwardRef\u003c\n HTMLAnchorElement,\n MUIButtonLinkProps\n>((props, ref) => \u003cButton ref={ref} component=\"a\" {...props} />)\n\nconst CreatedButtonLinkComponent = createLink(MUIButtonLinkComponent)\n\nexport const CustomButtonLink: LinkComponent\u003ctypeof MUIButtonLinkComponent> = (\n props,\n) => {\n return \u003cCreatedButtonLinkComponent preload={'intent'} {...props} />\n}\n```\n\n\n\n#### Usage with `styled`\n\nAny of these MUI approaches can then be used with `styled`:\n\n\n\n```tsx title=\"StyledCustomLink.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\n\n\n\n\n\n# React\n\n### Mantine example\n\n\n\n```tsx title=\"CustomLink.tsx\"\nimport * as React from 'react'\nimport { createLink, LinkComponent } from '@tanstack/react-router'\nimport { Anchor, AnchorProps } from '@mantine/core'\n\ninterface MantineAnchorProps extends Omit\u003cAnchorProps, 'href'> {\n // Add any additional props you want to pass to the anchor\n}\n\nconst MantineLinkComponent = React.forwardRef\u003c\n HTMLAnchorElement,\n MantineAnchorProps\n>((props, ref) => {\n return \u003cAnchor ref={ref} {...props} />\n})\n\nconst CreatedLinkComponent = createLink(MantineLinkComponent)\n\nexport const CustomLink: LinkComponent\u003ctypeof MantineLinkComponent> = (\n props,\n) => {\n return \u003cCreatedLinkComponent preload=\"intent\" {...props} />\n}\n```\n\n\n\n\n\n\n\n# Solid\n\n### Some Library example\n\n\n\n```tsx title=\"UntitledLink.tsx\"\n// TODO: Add this example.\n```\n\n\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7530,"content_sha256":"382ff1ff7c2c5858af91a4fe1d79789fecb7916a2ad6fc030d3e66769197de70"},{"filename":"references/docs/router/guide/custom-search-param-serialization.md","content":"---\ntitle: Custom Search Param Serialization\n---\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\n\n# React\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\n# Solid\n\n```tsx\nimport {\n createRouter,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/solid-router'\n\nconst router = createRouter({\n // ...\n parseSearch: parseSearchWith(JSON.parse),\n stringifySearch: stringifySearchWith(JSON.stringify),\n})\n```\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, JSURL2, or 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\n\n# React\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# Solid\n\n```tsx\nimport {\n Router,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/solid-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\n\n> [ 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 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\n\n# React\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\n# Solid\n\n```tsx\nimport { createRouter } from '@tanstack/solid-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\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\nJSURL2 is a non-standard library that can compress URLs while still maintaining readability. This can be done with the following code:\n\n\n\n# React\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\n# Solid\n\n```tsx\nimport {\n Router,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/solid-router'\nimport { parse, stringify } from 'jsurl2'\n\nconst router = createRouter({\n // ...\n parseSearch: parseSearchWith(parse),\n stringifySearch: stringifySearchWith(stringify),\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~tanner~min*_words~800)~\n```\n\n## Using the Zipson library\n\nZipson 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\n\n# React\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# Solid\n\n```tsx\nimport {\n Router,\n parseSearchWith,\n stringifySearchWith,\n} from '@tanstack/solid-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\n\n> [ 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\u003chr>\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```ts\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```ts\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","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10022,"content_sha256":"e2d78435df2be05becbb0f84a4d409d44d42e6f7d7fc9441b43a8afd278b28f3"},{"filename":"references/docs/router/guide/data-loading.md","content":"---\nid: data-loading\ntitle: Data Loading\n---\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 Query, skip to the [External Data Loading](./external-data-loading.md) guide.\n\n## Using the Router Cache\n\nThe router cache is built-in and is as easy as returning data from any route's `loader` function. Let's learn how!\n\n## Route `loader`s\n\nRoute `loader` functions are called when a route match is loaded. They are called with a single parameter which is an object containing many helpful properties. We'll go over those in a bit, but first, let's look at the two supported `loader` forms:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n})\n```\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: {\n handler: () => fetchPosts(),\n },\n})\n```\n\nUse the object form when you want to configure loader-specific behavior such as `staleReloadMode`.\n\n## `loader` Parameters\n\nThe `loader` function receives a single object with the following properties:\n\n- `abortController` - The route's abortController. Its signal is cancelled when the route is unloaded or when the Route is no longer relevant and the current invocation of the `loader` function becomes outdated.\n- `cause` - The cause of the current route match. Can be either one of the following:\n - `enter` - When the route is matched and loaded after not being matched in the previous location.\n - `preload` - When the route is being preloaded.\n - `stay` - When the route is matched and loaded after being matched in the previous location.\n- `context` - The route's context object, which is a merged union of:\n - Parent route context\n - This route's context as provided by the `beforeLoad` option\n- `deps` - The object value returned from the `Route.loaderDeps` function. If `Route.loaderDeps` is not defined, an empty object will be provided instead.\n- `location` - The current location\n- `params` - The route's path params\n- `parentMatchPromise` - `Promise\u003cRouteMatch>` (`undefined` for the root route)\n- `preload` - Boolean which is `true` when the route is being preloaded instead of loaded\n- `route` - The route itself\n\nUsing these parameters, we can do a lot of cool things, but first, let's take a look at how we can control it and when the `loader` function is called.\n\n## Consuming data from `loader`s\n\nTo consume data from a `loader`, use the `useLoaderData` hook defined on your Route object.\n\n```tsx\nconst posts = Route.useLoaderData()\n```\n\nIf you don't have ready access to your route object (i.e. you're deep in the component tree for the current route), you can use `getRouteApi` to access the same hook (as well as the other hooks on the Route object). This should be preferred over importing the Route object, which is likely to create circular dependencies.\n\n\n\n# React\n\n```tsx\nimport { getRouteApi } from '@tanstack/react-router'\n\n// in your component\n\nconst routeApi = getRouteApi('/posts')\nconst data = routeApi.useLoaderData()\n```\n\n# Solid\n\n```tsx\nimport { getRouteApi } from '@tanstack/solid-router'\n\n// in your component\n\nconst routeApi = getRouteApi('/posts')\nconst data = routeApi.useLoaderData()\n```\n\n\n\n## Dependency-based Stale-While-Revalidate Caching\n\nTanStack Router provides a built-in Stale-While-Revalidate caching layer for route loaders that is keyed on the dependencies of a route:\n\n- The route's fully parsed pathname\n - e.g. `/posts/1` vs `/posts/2`\n- Any additional dependencies provided by the `loaderDeps` option\n - e.g. `loaderDeps: ({ search: { pageIndex, pageSize } }) => ({ pageIndex, pageSize })`\n\nUsing these dependencies as keys, TanStack Router will cache the data returned from a route's `loader` function and use it to fulfill subsequent requests for the same route match. This means that if a route's data is already in the cache, it will be returned immediately, then **potentially** be refetched in the background depending on the \"freshness\" of the data.\n\n### Key options\n\nTo control router dependencies and \"freshness\", TanStack Router provides a plethora of options to control the keying and caching behavior of your route loaders. Let's take a look at them in the order that you are most likely to use them:\n\n- `routeOptions.loaderDeps`\n - A function that supplies you the search params for a router and returns an object of dependencies for use in your `loader` function. When these deps changed from navigation to navigation, it will cause the route to reload regardless of `staleTime`s. The deps are compared using a deep equality check.\n- `routeOptions.staleTime`\n- `routerOptions.defaultStaleTime`\n - The number of milliseconds that a route's data should be considered fresh when attempting to load.\n- `routeOptions.preloadStaleTime`\n- `routerOptions.defaultPreloadStaleTime`\n - The number of milliseconds that a route's data should be considered fresh attempting to preload.\n- `routeOptions.gcTime`\n- `routerOptions.defaultGcTime`\n - The number of milliseconds that a route's data should be kept in the cache before being garbage collected.\n- `routeOptions.shouldReload`\n - A function that receives the same `beforeLoad` and `loaderContext` parameters and returns a boolean indicating if the route should reload. This offers one more level of control over when a route should reload beyond `staleTime` and `loaderDeps` and can be used to implement patterns similar to Remix's `shouldLoad` option.\n- `routeOptions.loader.staleReloadMode`\n- `routerOptions.defaultStaleReloadMode`\n - Controls what happens when a matched route already has stale successful data. Use `'background'` for stale-while-revalidate, or `'blocking'` to wait for the stale loader reload to finish before continuing.\n\n### Some Important Defaults\n\n- By default, the `staleTime` is set to `0`, meaning that the route's data is immediately considered stale. Stale matches are reloaded in the background when the route is entered again, when its loader key changes (path params used by the route or `loaderDeps`), or when `router.load()` is called explicitly.\n- By default, a previously preloaded route is considered fresh for **30 seconds**. This means if a route is preloaded, then preloaded again within 30 seconds, the second preload will be ignored. This prevents unnecessary preloads from happening too frequently. **When a route is loaded normally, the standard `staleTime` is used.**\n- By default, the `gcTime` is set to **30 minutes**, meaning that any route data that has not been accessed in 30 minutes will be garbage collected and removed from the cache.\n- By default, `staleReloadMode` is `'background'`, so stale successful matches keep rendering with their existing `loaderData` while the loader revalidates in the background.\n- `router.invalidate()` will force all active routes to reload their loaders immediately and mark every cached route's data as stale.\n\n### Using `loaderDeps` to access search params\n\nImagine a `/posts` route supports some pagination via search params `offset` and `limit`. For the cache to uniquely store this data, we need to access these search params via the `loaderDeps` function. By explicitly identifying them, each route match for `/posts` with different `offset` and `limit` won't get mixed up!\n\nOnce we have these deps in place, the route will always reload when the deps change.\n\n```tsx\n// /routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),\n loader: ({ deps: { offset, limit } }) =>\n fetchPosts({\n offset,\n limit,\n }),\n})\n```\n\n> [!WARNING]\n> **Only include dependencies you actually use in the loader.**\n>\n> A common mistake is returning the entire `search` object:\n>\n> ```tsx\n> // ❌ Don't do this - causes unnecessary cache invalidation\n> loaderDeps: ({ search }) => search,\n> loader: ({ deps }) => fetchPosts({ page: deps.page }), // only uses page!\n> ```\n>\n> This causes the route to reload whenever ANY search param changes, even params not used in the loader (like `viewMode` or `sortDirection`). Instead, extract only what you need:\n>\n> ```tsx\n> // ✅ Do this - only reload when used params change\n> loaderDeps: ({ search }) => ({\n> page: search.page,\n> limit: search.limit,\n> }),\n> loader: ({ deps }) => fetchPosts(deps),\n> ```\n\n### Using `staleTime` to control how long data is considered fresh\n\nBy default, `staleTime` for navigations is set to `0`ms (and 30 seconds for preloads) which means that the route's data will always be considered stale. When a stale route is entered again, its loader key changes, or `router.load()` is called explicitly, the route will reload in the background.\n\n**This is a good default for most use cases, but you may find that some route data is more static or potentially expensive to load.** In these cases, you can use the `staleTime` option to control how long the route's data is considered fresh for navigations. Let's take a look at an example:\n\n```tsx\n// /routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n // Consider the route's data fresh for 10 seconds\n staleTime: 10_000,\n})\n```\n\nBy passing `10_000` to the `staleTime` option, we are telling the router to consider the route's data fresh for 10 seconds. This means that if the user navigates to `/posts` from `/about` within 10 seconds of the last loader result, the route's data will not be reloaded. If the user then navigates to `/posts` from `/about` after 10 seconds, the route's data will be reloaded **in the background**.\n\n## Choosing background vs blocking stale reloads\n\nBy default, stale successful matches use stale-while-revalidate behavior. That means the router can render with the existing `loaderData` immediately and then refresh it in the background.\n\nIf you want a specific loader to wait for a stale reload to finish before continuing, use the object form and set `staleReloadMode: 'blocking'`:\n\n```tsx\n// /routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: {\n handler: () => fetchPosts(),\n staleReloadMode: 'blocking',\n },\n})\n```\n\nYou can also change the default for the entire router:\n\n```tsx\nconst router = createRouter({\n routeTree,\n defaultStaleReloadMode: 'blocking',\n})\n```\n\nUse `'background'` when showing stale data during revalidation is acceptable. Use `'blocking'` when you want stale matches to behave more like a fresh load and wait for the new loader result.\n\n## Turning off automatic stale reloads\n\nTo disable automatic stale reloads for a route, set the `staleTime` option to `Infinity`:\n\n```tsx\n// /routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n staleTime: Infinity,\n})\n```\n\nYou can even turn this off for all routes by setting the `defaultStaleTime` option on the router:\n\n```tsx\nconst router = createRouter({\n routeTree,\n defaultStaleTime: Infinity,\n})\n```\n\nThis differs from `staleReloadMode: 'blocking'`:\n\n- `staleTime: Infinity` prevents the route from becoming stale in the first place\n- `staleReloadMode: 'blocking'` still allows stale reloads, but waits for them instead of doing them in the background\n\n## Using `shouldReload` and `gcTime` to opt-out of caching\n\nSimilar to Remix's default functionality, you may want to configure a route to only load on entry or when critical loader deps change. You can do this by using the `gcTime` option combined with the `shouldReload` option, which accepts either a `boolean` or a function that receives the same `beforeLoad` and `loaderContext` parameters and returns a boolean indicating if the route should reload.\n\n```tsx\n// /routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),\n loader: ({ deps }) => fetchPosts(deps),\n // Do not cache this route's data after it's unloaded\n gcTime: 0,\n // Only reload the route when the user navigates to it or when deps change\n shouldReload: false,\n})\n```\n\n### Opting out of caching while still preloading\n\nEven though you may opt-out of short-term caching for your route data, you can still get the benefits of preloading! With the above configuration, preloading will still \"just work\" with the default `preloadGcTime`. This means that if a route is preloaded, then navigated to, the route's data will be considered fresh and will not be reloaded.\n\nTo opt out of preloading, don't turn it on via the `routerOptions.defaultPreload` or `routeOptions.preload` options.\n\n## Passing all loader events to an external cache\n\nWe break down this use case in the [External Data Loading](./external-data-loading.md) page, but if you'd like to use an external cache like TanStack Query, you can do so by passing all loader events to your external cache. As long as you are using the defaults, the only change you'll need to make is to set the `defaultPreloadStaleTime` option on the router to `0`:\n\n```tsx\nconst router = createRouter({\n routeTree,\n defaultPreloadStaleTime: 0,\n})\n```\n\nThis will ensure that every preload, load, and reload event will trigger your `loader` functions, which can then be handled and deduped by your external cache.\n\n## Using Router Context\n\nThe `context` argument passed to the `loader` function is an object containing a merged union of:\n\n- Parent route context\n- This route's context as provided by the `beforeLoad` option\n\nStarting at the very top of the router, you can pass an initial context to the router via the `context` option. This context will be available to all routes in the router and get copied and extended by each route as they are matched. This happens by passing a context to a route via the `beforeLoad` option. This context will be available to all the route's child routes. The resulting context will be available to the route's `loader` function.\n\nIn this example, we'll create a function in our route context to fetch posts, then use it in our `loader` function.\n\n> Context is a powerful tool for dependency injection. You can use it to inject services, hooks, and other objects into your router and routes. You can also additively pass data down the route tree at every route using a route's `beforeLoad` option.\n\n- `/utils/fetchPosts.tsx`\n\n```tsx\nexport const fetchPosts = async () => {\n const res = await fetch(`/api/posts?page=${pageIndex}`)\n if (!res.ok) throw new Error('Failed to fetch posts')\n return res.json()\n}\n```\n\n- `/routes/__root.tsx`\n\n\n\n# React\n\n```tsx\nimport { createRootRouteWithContext } from '@tanstack/react-router'\n\n// Create a root route using the createRootRouteWithContext\u003c{...}>() function and pass it whatever types you would like to be available in your router context.\nexport const Route = createRootRouteWithContext\u003c{\n fetchPosts: typeof fetchPosts\n}>()() // NOTE: the double call is on purpose, since createRootRouteWithContext is a factory ;)\n```\n\n# Solid\n\n```tsx\nimport { createRootRouteWithContext } from '@tanstack/solid-router'\n\n// Create a root route using the createRootRouteWithContext\u003c{...}>() function and pass it whatever types you would like to be available in your router context.\nexport const Route = createRootRouteWithContext\u003c{\n fetchPosts: typeof fetchPosts\n}>()() // NOTE: the double call is on purpose, since createRootRouteWithContext is a factory ;)\n```\n\n\n\n- `/routes/posts.tsx`\n\n```tsx\n// Notice how our postsRoute references context to get our fetchPosts function\n// This can be a powerful tool for dependency injection across your router\n// and routes.\nexport const Route = createFileRoute('/posts')({\n loader: ({ context: { fetchPosts } }) => fetchPosts(),\n})\n```\n\n- `/router.tsx`\n\n```tsx\nimport { routeTree } from './routeTree.gen'\n\n// Use your routerContext to create a new router\n// This will require that you fullfil the type requirements of the routerContext\nconst router = createRouter({\n routeTree,\n context: {\n // Supply the fetchPosts function to the router context\n fetchPosts,\n },\n})\n```\n\n## Using Path Params\n\nTo use path params in your `loader` function, access them via the `params` property on the function's parameters. Here's an example:\n\n```tsx\n// src/routes/posts.$postId.tsx\nexport const Route = createFileRoute('/posts/$postId')({\n loader: ({ params: { postId } }) => fetchPostById(postId),\n})\n```\n\n## Using Route Context\n\nPassing down global context to your router is great, but what if you want to provide context that is specific to a route? This is where the `beforeLoad` option comes in. The `beforeLoad` option is a function that runs right before attempting to load a route and receives the same parameters as `loader`. Beyond its ability to redirect potential matches, block loader requests, etc, it can also return an object that will be merged into the route's context. Let's take a look at an example where we inject some data into our route context via the `beforeLoad` option:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n // Pass the fetchPosts function to the route context\n beforeLoad: () => ({\n fetchPosts: () => console.info('foo'),\n }),\n loader: ({ context: { fetchPosts } }) => {\n fetchPosts() // 'foo'\n\n // ...\n },\n})\n```\n\n## Using Search Params in Loaders\n\n> But wait Tanner... where the heck are my search params?!\n\nYou might be here wondering why `search` isn't directly available in the `loader` function's parameters. We've purposefully designed it this way to help you succeed. Let's take a look at why:\n\n- Search Parameters being used in a loader function are a very good indicator that those search params should also be used to uniquely identify the data being loaded. For example, you may have a route that uses a search param like `pageIndex` that uniquely identifies the data held inside of the route match. Or, imagine a `/users/user` route that uses the search param `userId` to identify a specific user in your application, you might model your url like this: `/users/user?userId=123`. This means that your `user` route would need some extra help to identify a specific user.\n- Directly accessing search params in a loader function can lead to bugs in caching and preloading where the data being loaded is not unique to the current URL pathname and search params. For example, you might ask your `/posts` route to preload page 2's results, but without the distinction of pages in your route configuration, you will end up fetching, storing and displaying page 2's data on your `/posts` or `?page=1` screen instead of it preloading in the background!\n- Placing a threshold between search parameters and the loader function allows the router to understand your dependencies and reactivity.\n\n```tsx\n// /routes/users.user.tsx\nexport const Route = createFileRoute('/users/user')({\n validateSearch: (search) =>\n search as {\n userId: string\n },\n loaderDeps: ({ search: { userId } }) => ({\n userId,\n }),\n loader: async ({ deps: { userId } }) => getUser(userId),\n})\n```\n\n### Accessing Search Params via `routeOptions.loaderDeps`\n\n```tsx\n// /routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n // Use zod to validate and parse the search params\n validateSearch: z.object({\n offset: z.number().int().nonnegative().catch(0),\n }),\n // Pass the offset to your loader deps via the loaderDeps function\n loaderDeps: ({ search: { offset } }) => ({ offset }),\n // Use the offset from context in the loader function\n loader: async ({ deps: { offset } }) =>\n fetchPosts({\n offset,\n }),\n})\n```\n\n## Using the Abort Signal\n\nThe `abortController` property of the `loader` function is an AbortController. Its signal is cancelled when the route is unloaded or when the `loader` call becomes outdated. This is useful for cancelling network requests when the route is unloaded or when the route's params change. Here is an example using it with a fetch call:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: ({ abortController }) =>\n fetchPosts({\n // Pass this to an underlying fetch call or anything that supports signals\n signal: abortController.signal,\n }),\n})\n```\n\n## Using the `preload` flag\n\nThe `preload` property of the `loader` function is a boolean which is `true` when the route is being preloaded instead of loaded. Some data loading libraries may handle preloading differently than a standard fetch, so you may want to pass `preload` to your data loading library, or use it to execute the appropriate data loading logic:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: async ({ preload }) =>\n fetchPosts({\n maxAge: preload ? 10_000 : 0, // Preloads should hang around a bit longer\n }),\n})\n```\n\n## Handling Slow Loaders\n\nIdeally most route loaders can resolve their data within a short moment, removing the need to render a placeholder spinner and simply rely on suspense to render the next route when it's completely ready. When critical data that is required to render a route's component is slow though, you have 2 options:\n\n- Split up your fast and slow data into separate promises and `defer` the slow data until after the fast data is loaded (see the [Deferred Data Loading](./deferred-data-loading.md) guide).\n- Show a pending component after an optimistic suspense threshold until all of the data is ready (See below).\n\n## Showing a pending component\n\n**By default, TanStack Router will show a pending component for loaders that take longer than 1 second to resolve.** This is an optimistic threshold that can be configured via:\n\n- `routeOptions.pendingMs` or\n- `routerOptions.defaultPendingMs`\n\nWhen the pending time threshold is exceeded, the router will render the `pendingComponent` option of the route, if configured.\n\n## Avoiding Pending Component Flash\n\nIf you're using a pending component, the last thing you want is for your pending time threshold to be met, then have your data resolve immediately after, resulting in a jarring flash of your pending component. To avoid this, **TanStack Router by default will show your pending component for at least 500ms**. This is an optimistic threshold that can be configured via:\n\n- `routeOptions.pendingMinMs` or\n- `routerOptions.defaultPendingMinMs`\n\n## Handling Errors\n\nTanStack Router provides a few ways to handle errors that occur during the route loading lifecycle. Let's take a look at them.\n\n### Handling Errors with `routeOptions.onError`\n\nThe `routeOptions.onError` option is a function that is called when an error occurs during the route loading.\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n onError: ({ error }) => {\n // Log the error\n console.error(error)\n },\n})\n```\n\n### Handling Errors with `routeOptions.onCatch`\n\nThe `routeOptions.onCatch` option is a function that is called whenever an error was caught by the router's CatchBoundary.\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n onCatch: ({ error, errorInfo }) => {\n // Log the error\n console.error(error)\n },\n})\n```\n\n### Handling Errors with `routeOptions.errorComponent`\n\nThe `routeOptions.errorComponent` option is a component that is rendered when an error occurs during the route loading or rendering lifecycle. It is rendered with the following props:\n\n- `error` - The error that occurred\n- `reset` - A function to reset the internal `CatchBoundary`\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n errorComponent: ({ error }) => {\n // Render an error message\n return \u003cdiv>{error.message}\u003c/div>\n },\n})\n```\n\nThe `reset` function can be used to allow the user to retry rendering the error boundaries normal children:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n errorComponent: ({ error, reset }) => {\n return (\n \u003cdiv>\n {error.message}\n \u003cbutton\n onClick={() => {\n // Reset the router error boundary\n reset()\n }}\n >\n retry\n \u003c/button>\n \u003c/div>\n )\n },\n})\n```\n\nIf the error was the result of a route load, you should instead call `router.invalidate()`, which will coordinate both a router reload and an error boundary reset:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n errorComponent: ({ error, reset }) => {\n const router = useRouter()\n\n return (\n \u003cdiv>\n {error.message}\n \u003cbutton\n onClick={() => {\n // Invalidate the route to reload the loader, which will also reset the error boundary\n router.invalidate()\n }}\n >\n retry\n \u003c/button>\n \u003c/div>\n )\n },\n})\n```\n\n### Using the default `ErrorComponent`\n\nTanStack Router provides a default `ErrorComponent` that is rendered when an error occurs during the route loading or rendering lifecycle. If you choose to override your routes' error components, it's still wise to always fall back to rendering any uncaught errors with the default `ErrorComponent`:\n\n```tsx\n// src/routes/posts.tsx\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n errorComponent: ({ error }) => {\n if (error instanceof MyCustomError) {\n // Render a custom error message\n return \u003cdiv>{error.message}\u003c/div>\n }\n\n // Fallback to the default ErrorComponent\n return \u003cErrorComponent error={error} />\n },\n})\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":28274,"content_sha256":"4fbade1a14262cbfec27077455b5b079886ce44037d6a15f7763ebd0873f389e"},{"filename":"references/docs/router/guide/data-mutations.md","content":"---\ntitle: Data Mutations\n---\n\nSince TanStack router does not store or cache data, it's role in data mutation is slim to none outside of reacting to potential URL side-effects from external mutation events. That said, we've compiled a list of mutation-related features you might find useful and libraries that implement them.\n\nLook for and use mutation utilities that support:\n\n- Handling and caching submission state\n- Providing both local and global optimistic UI support\n- Built-in hooks to wire up invalidation (or automatically support it)\n- Handling multiple in-flight mutations at once\n- Organizing mutation state as a globally accessible resource\n- Submission state history and garbage collection\n\nSome suggested libraries:\n\n- TanStack Query\n- SWR\n- RTK Query\n- urql\n- Relay\n- Apollo\n\nOr, even...\n\n- Zustand\n- Jotai\n- Recoil\n- Redux\n\nSimilar to data fetching, mutation state isn't a one-size-fits-all solution, so you'll need to pick a solution that fits your needs and your team's needs. We recommend trying out a few different solutions and seeing what works best for you.\n\n> Still here? Submission state is an interesting topic when it comes to persistence. Do you keep every mutation around forever? How do you know when to get rid of it? What if the user navigates away from the screen and then back? Let's dig in!\n\n## Invalidating TanStack Router after a mutation\n\nTanStack Router comes with short-term caching built-in. So even though we're not storing any data after a route match is unmounted, there is a high probability that if any mutations are made related to the data stored in the Router, the current route matches' data could become stale.\n\nWhen mutations related to loader data are made, we can use `router.invalidate` to force the router to reload all of the current route matches:\n\n```tsx\nconst router = useRouter()\n\nconst addTodo = async (todo: Todo) => {\n try {\n await api.addTodo()\n router.invalidate()\n } catch {\n //\n }\n}\n```\n\nInvalidating all of the current route matches happens in the background, so existing data will continue to be served until the new data is ready, just as if you were navigating to a new route.\n\nIf you want to await the invalidation until all loaders have finished, pass `{sync: true}` into `router.invalidate`:\n\n```tsx\nconst router = useRouter()\n\nconst addTodo = async (todo: Todo) => {\n try {\n await api.addTodo()\n await router.invalidate({ sync: true })\n } catch {\n //\n }\n}\n```\n\n## Long-term mutation State\n\nRegardless of the mutation library used, mutations often create state related to their submission. While most mutations are set-and-forget, some mutation states are more long-lived, either to support optimistic UI or to provide feedback to the user about the status of their submissions. Most state managers will correctly keep this submission state around and expose it to make it possible to show UI elements like loading spinners, success messages, error messages, etc.\n\nLet's consider the following interactions:\n\n- User navigates to the `/posts/123/edit` screen to edit a post\n- User edits the `123` post and upon success, sees a success message below the editor that the post was updated\n- User navigates to the `/posts` screen\n- User navigates back to the `/posts/123/edit` screen again\n\nWithout notifying your mutation management library about the route change, it's possible that your submission state could still be around and your user would still see the **\"Post updated successfully\"** message when they return to the previous screen. This is not ideal. Obviously, our intent wasn't to keep this mutation state around forever, right?!\n\n## Using mutation keys\n\nHopefully and hypothetically, the easiest way is for your mutation library to support a keying mechanism that will allow your mutations's state to be reset when the key changes:\n\n```tsx\nconst routeApi = getRouteApi('/room/$roomId/chat')\n\nfunction ChatRoom() {\n const { roomId } = routeApi.useParams()\n\n const sendMessageMutation = useCoolMutation({\n fn: sendMessage,\n // Clear the mutation state when the roomId changes\n // including any submission state\n key: ['sendMessage', roomId],\n })\n\n // Fire off a bunch of messages\n const test = () => {\n sendMessageMutation.mutate({ roomId, message: 'Hello!' })\n sendMessageMutation.mutate({ roomId, message: 'How are you?' })\n sendMessageMutation.mutate({ roomId, message: 'Goodbye!' })\n }\n\n return (\n \u003c>\n {sendMessageMutation.submissions.map((submission) => {\n return (\n \u003cdiv>\n \u003cdiv>{submission.status}\u003c/div>\n \u003cdiv>{submission.message}\u003c/div>\n \u003c/div>\n )\n })}\n \u003c/>\n )\n}\n```\n\n## Using the `router.subscribe` method\n\nFor libraries that don't have a keying mechanism, we'll likely need to manually reset the mutation state when the user navigates away from the screen. To solve this, we can use TanStack Router's `invalidate` and `subscribe` method to clear mutation states when the user is no longer in need of them.\n\nThe `router.subscribe` method is a function that subscribes a callback to various router events. The event in particular that we'll use here is the `onResolved` event. It's important to understand that this event is fired when the location path is _changed (not just reloaded) and has finally resolved_.\n\nThis is a great place to reset your old mutation states. Here's an example:\n\n```tsx\nconst router = createRouter()\nconst coolMutationCache = createCoolMutationCache()\n\nconst unsubscribeFn = router.subscribe('onResolved', () => {\n // Reset mutation states when the route changes\n coolMutationCache.clear()\n})\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5642,"content_sha256":"52924f67151e53e69548eee47d280ec5f9a54ed459fc8b1d9b6891b42adff2cb"},{"filename":"references/docs/router/guide/deferred-data-loading.md","content":"---\nid: deferred-data-loading\ntitle: Deferred Data Loading\n---\n\nTanStack Router is designed to run loaders in parallel and wait for all of them to resolve before rendering the next route. This is great most of the time, but occasionally, you may want to show the user something sooner while the rest of the data loads in the background.\n\nDeferred data loading is a pattern that allows the router to render the next location's critical data/markup while slower, non-critical route data is resolved in the background. This process works on both the client and server (via streaming) and is a great way to improve the perceived performance of your application.\n\nIf you are using a library like TanStack Query or any other data fetching library, then deferred data loading works a bit differently. Skip ahead to the [Deferred Data Loading with External Libraries](#deferred-data-loading-with-external-libraries) section for more information.\n\n## Deferred Data Loading with `Await`\n\nTo defer slow or non-critical data, return an **unawaited/unresolved** promise anywhere in your loader response:\n\n```tsx\n// src/routes/posts.$postId.tsx\nimport { createFileRoute, defer } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async () => {\n // Fetch some slower data, but do not await it\n const slowDataPromise = fetchSlowData()\n\n // Fetch and await some data that resolves quickly\n const fastData = await fetchFastData()\n\n return {\n fastData,\n deferredSlowData: slowDataPromise,\n }\n },\n})\n```\n\nAs soon as any awaited promises are resolved, the next route will begin rendering while the deferred promises continue to resolve.\n\nIn the component, deferred promises can be resolved and utilized using the `Await` component:\n\n```tsx\n// src/routes/posts.$postId.tsx\nimport { createFileRoute, Await } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n // ...\n component: PostIdComponent,\n})\n\nfunction PostIdComponent() {\n const { deferredSlowData, fastData } = Route.useLoaderData()\n\n // do something with fastData\n\n return (\n \u003cAwait promise={deferredSlowData} fallback={\u003cdiv>Loading...\u003c/div>}>\n {(data) => {\n return \u003cdiv>{data}\u003c/div>\n }}\n \u003c/Await>\n )\n}\n```\n\n> [!TIP]\n> If your component is code-split, you can use the [getRouteApi function](./code-splitting.md#manually-accessing-route-apis-in-other-files-with-the-getrouteapi-helper) to avoid having to import the `Route` configuration to get access to the typed `useLoaderData()` hook.\n\nThe `Await` component resolves the promise by triggering the nearest suspense boundary until it is resolved, after which it renders the component's `children` as a function with the resolved data.\n\nIf the promise is rejected, the `Await` component will throw the serialized error, which can be caught by the nearest error boundary.\n\n\n\n# React\n\n> [!TIP]\n> In React 19, you can use the `use()` hook instead of `Await`\n\n\n\n## Deferred Data Loading with External libraries\n\nWhen your strategy for fetching information for the route relies on [External Data Loading](./external-data-loading.md) with an external library like TanStack Query, deferred data loading works a bit differently, as the library handles the data fetching and caching for you outside of TanStack Router.\n\nSo, instead of using `defer` and `Await`, you'll instead want to use the Route's `loader` to kick off the data fetching and then use the library's hooks to access the data in your components.\n\n\n\n# React\n\n```tsx\n// src/routes/posts.$postId.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { slowDataOptions, fastDataOptions } from '~/api/query-options'\n\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ context: { queryClient } }) => {\n // Kick off the fetching of some slower data, but do not await it\n queryClient.prefetchQuery(slowDataOptions())\n\n // Fetch and await some data that resolves quickly\n await queryClient.ensureQueryData(fastDataOptions())\n },\n})\n```\n\n# Solid\n\n```tsx\n// src/routes/posts.$postId.tsx\nimport { createFileRoute } from '@tanstack/solid-router'\nimport { slowDataOptions, fastDataOptions } from '~/api/query-options'\n\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ context: { queryClient } }) => {\n // Kick off the fetching of some slower data, but do not await it\n queryClient.prefetchQuery(slowDataOptions())\n\n // Fetch and await some data that resolves quickly\n await queryClient.ensureQueryData(fastDataOptions())\n },\n})\n```\n\n\n\nThen in your component, you can use the library's hooks to access the data:\n\n\n\n# React\n\n```tsx\n// src/routes/posts.$postId.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { useSuspenseQuery } from '@tanstack/react-query'\nimport { slowDataOptions, fastDataOptions } from '~/api/query-options'\n\nexport const Route = createFileRoute('/posts/$postId')({\n // ...\n component: PostIdComponent,\n})\n\nfunction PostIdComponent() {\n const fastData = useSuspenseQuery(fastDataOptions())\n\n // do something with fastData\n\n return (\n \u003cSuspense fallback={\u003cdiv>Loading...\u003c/div>}>\n \u003cSlowDataComponent />\n \u003c/Suspense>\n )\n}\n\nfunction SlowDataComponent() {\n const data = useSuspenseQuery(slowDataOptions())\n\n return \u003cdiv>{data}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx\n// src/routes/posts.$postId.tsx\nimport { createFileRoute } from '@tanstack/solid-router'\nimport { useSuspenseQuery } from '@tanstack/solid-query'\nimport { slowDataOptions, fastDataOptions } from '~/api/query-options'\n\nexport const Route = createFileRoute('/posts/$postId')({\n // ...\n component: PostIdComponent,\n})\n\nfunction PostIdComponent() {\n const fastData = useSuspenseQuery(fastDataOptions())\n\n // do something with fastData\n\n return (\n \u003cSuspense fallback={\u003cdiv>Loading...\u003c/div>}>\n \u003cSlowDataComponent />\n \u003c/Suspense>\n )\n}\n\nfunction SlowDataComponent() {\n const data = useSuspenseQuery(slowDataOptions())\n\n return \u003cdiv>{data()}\u003c/div>\n}\n```\n\n\n\n## Caching and Invalidation\n\nStreamed promises follow the same lifecycle as the loader data they are associated with. They can even be preloaded!\n\n\n\n# React\n\n## SSR & Streaming Deferred Data\n\n**Streaming requires a server that supports it and for TanStack Router to be configured to use it properly.**\n\nPlease read the entire [Streaming SSR Guide](./ssr.md#streaming-ssr) for step by step instructions on how to set up your server for streaming.\n\n## SSR Streaming Lifecycle\n\nThe following is a high-level overview of how deferred data streaming works with TanStack Router:\n\n- Server\n - Promises are marked and tracked as they are returned from route loaders\n - All loaders resolve and any deferred promises are serialized and embedded into the html\n - The route begins to render\n - Deferred promises rendered with the `\u003cAwait>` component trigger suspense boundaries, allowing the server to stream html up to that point\n- Client\n - The client receives the initial html from the server\n - `\u003cAwait>` components suspend with placeholder promises while they wait for their data to resolve on the server\n- Server\n - As deferred promises resolve, their results (or errors) are serialized and streamed to the client via an inline script tag\n - The resolved `\u003cAwait>` components and their suspense boundaries are resolved and their resulting HTML is streamed to the client along with their dehydrated data\n- Client\n - The suspended placeholder promises within `\u003cAwait>` are resolved with the streamed data/error responses and either render the result or throw the error to the nearest error boundary\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7641,"content_sha256":"47614b8d0afe1b830dd5324142815a1a258bccb3e653ff580eb4106ae0bab82b"},{"filename":"references/docs/router/guide/document-head-management.md","content":"---\ntitle: Document Head Management\n---\n\nDocument head management is the process of managing the head, title, meta, link, and script tags of a document and TanStack Router provides a robust way to manage the document head for full-stack applications that use Start and for single-page applications that use TanStack Router. It provides:\n\n- Automatic deduping of `title` and `meta` tags\n- Automatic loading/unloading of tags based on route visibility\n- A composable way to merge `title` and `meta` tags from nested routes\n\nFor full-stack applications that use Start, and even for single-page applications that use TanStack Router, managing the document head is a crucial part of any application for the following reasons:\n\n- SEO\n- Social media sharing\n- Analytics\n- CSS and JS loading/unloading\n\nTo manage the document head, it's required that you render both the `\u003cHeadContent />` and `\u003cScripts />` components and use the `routeOptions.head` property to manage the head of a route, which returns an object with `title`, `meta`, `links`, `styles`, and `scripts` properties.\n\n## Managing the Document Head\n\n```tsx\nexport const Route = createRootRoute({\n head: () => ({\n meta: [\n {\n name: 'description',\n content: 'My App is a web application',\n },\n {\n title: 'My App',\n },\n ],\n links: [\n {\n rel: 'icon',\n href: '/favicon.ico',\n },\n ],\n styles: [\n {\n media: 'all and (max-width: 500px)',\n children: `p {\n color: blue;\n background-color: yellow;\n }`,\n },\n ],\n scripts: [\n {\n src: 'https://www.google-analytics.com/analytics.js',\n },\n ],\n }),\n})\n```\n\n### Deduping\n\nOut of the box, TanStack Router will dedupe `title` and `meta` tags, preferring the **last** occurrence of each tag found in nested routes.\n\n- `title` tags defined in nested routes will override a `title` tag defined in a parent route (but you can compose them together, which is covered in a future section of this guide)\n- `meta` tags with the same `name` or `property` will be overridden by the last occurrence of that tag found in nested routes\n\n### `\u003cHeadContent />`\n\nThe `\u003cHeadContent />` component is **required** to render the head, title, meta, link, and head-related script tags of a document.\n\nIt should be **rendered either in the `\u003chead>` tag of your root layout or as high up in the component tree as possible** if your application doesn't or can't manage the `\u003chead>` tag.\n\n### Start/Full-Stack Applications\n\n\n\n# React\n\n```tsx\nimport { HeadContent } from '@tanstack/react-router'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003chtml>\n \u003chead>\n \u003cHeadContent />\n \u003c/head>\n \u003cbody>\n \u003cOutlet />\n \u003c/body>\n \u003c/html>\n ),\n})\n```\n\n# Solid\n\n```tsx\nimport { HeadContent } from '@tanstack/solid-router'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003chtml>\n \u003chead>\n \u003cHeadContent />\n \u003c/head>\n \u003cbody>\n \u003cOutlet />\n \u003c/body>\n \u003c/html>\n ),\n})\n```\n\n\n\n### Single-Page Applications\n\nFirst, remove the `\u003ctitle>` tag from the index.html if you have set any.\n\n\n\n# React\n\n```tsx\nimport { HeadContent } from '@tanstack/react-router'\n\nconst rootRoute = createRootRoute({\n component: () => (\n \u003c>\n \u003cHeadContent />\n \u003cOutlet />\n \u003c/>\n ),\n})\n```\n\n# Solid\n\n```tsx\nimport { HeadContent } from '@tanstack/solid-router'\n\nconst rootRoute = createRootRoute({\n component: () => (\n \u003c>\n \u003cHeadContent />\n \u003cOutlet />\n \u003c/>\n ),\n})\n```\n\n\n\n## Managing Body Scripts\n\nIn addition to scripts that can be rendered in the `\u003chead>` tag, you can also render scripts in the `\u003cbody>` tag using the `routeOptions.scripts` property. This is useful for loading scripts (even inline scripts) that require the DOM to be loaded, but before the main entry point of your application (which includes hydration if you're using Start or a full-stack implementation of TanStack Router).\n\nTo do this, you must:\n\n- Use the `scripts` property of the `routeOptions` object\n- [Render the `\u003cScripts />` component](#scripts)\n\n```tsx\nexport const Route = createRootRoute({\n scripts: () => [\n {\n children: 'console.log(\"Hello, world!\")',\n },\n ],\n})\n```\n\n### `\u003cScripts />`\n\nThe `\u003cScripts />` component is **required** to render the body scripts of a document. It should be rendered either in the `\u003cbody>` tag of your root layout or as high up in the component tree as possible if your application doesn't or can't manage the `\u003cbody>` tag.\n\n### Example\n\n\n\n# React\n\n```tsx\nimport { createRootRoute, Scripts } from '@tanstack/react-router'\nexport const Route = createFileRoute('/')({\n component: () => (\n \u003chtml>\n \u003chead />\n \u003cbody>\n \u003cOutlet />\n \u003cScripts />\n \u003c/body>\n \u003c/html>\n ),\n})\n```\n\n# Solid\n\n```tsx\nimport { createFileRoute, Scripts } from '@tanstack/solid-router'\nexport const Route = createRootRoute('/')({\n component: () => (\n \u003chtml>\n \u003chead />\n \u003cbody>\n \u003cOutlet />\n \u003cScripts />\n \u003c/body>\n \u003c/html>\n ),\n})\n```\n\n\n\n## Inline Scripts with ScriptOnce\n\nFor scripts that must run before React hydrates (like theme detection), use `ScriptOnce`. This is particularly useful for avoiding flash of unstyled content (FOUC) or theme flicker.\n\n\n\n# React\n\n```tsx\nimport { ScriptOnce } from '@tanstack/react-router'\n\nconst themeScript = `(function() {\n try {\n const theme = localStorage.getItem('theme') || 'auto';\n const resolved = theme === 'auto'\n ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')\n : theme;\n document.documentElement.classList.add(resolved);\n } catch (e) {}\n})();`\n\nfunction ThemeProvider({ children }) {\n return (\n \u003c>\n \u003cScriptOnce children={themeScript} />\n {children}\n \u003c/>\n )\n}\n```\n\n# Solid\n\n```tsx\nimport { ScriptOnce } from '@tanstack/solid-router'\n\nconst themeScript = `(function() {\n try {\n const theme = localStorage.getItem('theme') || 'auto';\n const resolved = theme === 'auto'\n ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')\n : theme;\n document.documentElement.classList.add(resolved);\n } catch (e) {}\n})();`\n\nfunction ThemeProvider({ children }) {\n return (\n \u003c>\n \u003cScriptOnce children={themeScript} />\n {children}\n \u003c/>\n )\n}\n```\n\n\n\n### How ScriptOnce Works\n\n1. During SSR, renders a `\u003cscript>` tag with the provided code\n2. The script executes immediately when the browser parses the HTML (before React hydrates)\n3. After execution, the script removes itself from the DOM\n4. On client-side navigation, nothing is rendered (prevents duplicate execution)\n\n\n\n# React\n\n### Preventing Hydration Warnings\n\nIf your script modifies the DOM before hydration (like adding a class to `\u003chtml>`), use `suppressHydrationWarning` to prevent React warnings:\n\n```tsx\nexport const Route = createRootRoute({\n component: () => (\n \u003chtml lang=\"en\" suppressHydrationWarning>\n \u003chead>\n \u003cHeadContent />\n \u003c/head>\n \u003cbody>\n \u003cThemeProvider>\n \u003cOutlet />\n \u003c/ThemeProvider>\n \u003cScripts />\n \u003c/body>\n \u003c/html>\n ),\n})\n```\n\n\n\n### Common Use Cases\n\n- **Theme/dark mode detection** - Apply theme class before hydration to prevent flash\n- **Feature detection** - Check browser capabilities before rendering\n- **Analytics initialization** - Initialize tracking before user interaction\n- **Critical path setup** - Any JavaScript that must run before hydration\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7528,"content_sha256":"4eb1fc4814ae99e15095adb72f198c4e15ba05bf02dfd1444e42a9f2bc92acf6"},{"filename":"references/docs/router/guide/external-data-loading.md","content":"---\nid: external-data-loading\ntitle: External Data Loading\n---\n\n> [!IMPORTANT]\n> This guide is geared towards external state management libraries and their integration with TanStack Router for data fetching, ssr, hydration/dehydration and streaming. If you haven't read the standard [Data Loading](./data-loading.md) guide, please do so first.\n\n## To **Store** or to **Coordinate**?\n\nWhile Router is very capable of storing and managing most data needs out of the box, sometimes you just might want something more robust!\n\nRouter is designed to be a perfect **coordinator** for external data fetching and caching libraries. This means that you can use any data fetching/caching library you want, and the router will coordinate the loading of your data in a way that aligns with your users' navigation and expectations of freshness.\n\n## What data fetching libraries are supported?\n\nAny data fetching library that supports asynchronous promises can be used with TanStack Router. This includes:\n\n- TanStack Query\n- SWR\n- RTK Query\n- urql\n- Relay\n- Apollo\n\nOr, even...\n\n- Zustand\n- Jotai\n- Recoil\n- Redux\n\nLiterally any library that **can return a promise and read/write data** can be integrated.\n\n## Using Loaders to ensure data is loaded\n\nThe easiest way to integrate external caching/data library into Router is to use `route.loader`s to ensure that the data required inside of a route has been loaded and is ready to be displayed.\n\n> BUT WHY? It's very important to preload your critical render data in the loader for a few reasons:\n>\n> - No \"flash of loading\" states\n> - No waterfall data fetching, caused by component based fetching\n> - Better for SEO. If your data is available at render time, it will be indexed by search engines.\n\nHere is a naive illustration (don't do this) of using a Route's `loader` option to seed the cache for some data:\n\n```tsx\n// src/routes/posts.tsx\nlet postsCache = []\n\nexport const Route = createFileRoute('/posts')({\n loader: async () => {\n postsCache = await fetchPosts()\n },\n component: () => {\n return (\n \u003cdiv>\n {postsCache.map((post) => (\n \u003cPost key={post.id} post={post} />\n ))}\n \u003c/div>\n )\n },\n})\n```\n\nThis example is **obviously flawed**, but illustrates the point that you can use a route's `loader` option to seed your cache with data. Let's take a look at a more realistic example using TanStack Query.\n\n- Replace `fetchPosts` with your preferred data fetching library's prefetching API\n- Replace `postsCache` with your preferred data fetching library's read-or-fetch API or hook\n\n## A more realistic example using TanStack Query\n\nLet's take a look at a more realistic example using TanStack Query.\n\n```tsx\n// src/routes/posts.tsx\nconst postsQueryOptions = queryOptions({\n queryKey: ['posts'],\n queryFn: () => fetchPosts(),\n})\n\nexport const Route = createFileRoute('/posts')({\n // Use the `loader` option to ensure that the data is loaded\n loader: () => queryClient.ensureQueryData(postsQueryOptions),\n component: () => {\n // Read the data from the cache and subscribe to updates\n const {\n data: { posts },\n } = useSuspenseQuery(postsQueryOptions)\n\n return (\n \u003cdiv>\n {posts.map((post) => (\n \u003cPost key={post.id} post={post} />\n ))}\n \u003c/div>\n )\n },\n})\n```\n\n### Error handling with TanStack Query\n\nWhen an error occurs while using `suspense` with `TanStack Query`, you need to let queries know that you want to try again when re-rendering. This can be done by using the `reset` function provided by the `useQueryErrorResetBoundary` hook. You can invoke this function in an effect as soon as the error component mounts. This will make sure that the query is reset and will try to fetch data again when the route component is rendered again. This will also cover cases where users navigate away from the route instead of clicking the `retry` button.\n\n```tsx\nexport const Route = createFileRoute('/')({\n loader: () => queryClient.ensureQueryData(postsQueryOptions),\n errorComponent: ({ error, reset }) => {\n const router = useRouter()\n const queryErrorResetBoundary = useQueryErrorResetBoundary()\n\n useEffect(() => {\n // Reset the query error boundary\n queryErrorResetBoundary.reset()\n }, [queryErrorResetBoundary])\n\n return (\n \u003cdiv>\n {error.message}\n \u003cbutton\n onClick={() => {\n // Invalidate the route to reload the loader, and reset any router error boundaries\n router.invalidate()\n }}\n >\n retry\n \u003c/button>\n \u003c/div>\n )\n },\n})\n```\n\n## SSR Dehydration/Hydration\n\nTools that are able can integrate with TanStack Router's convenient Dehydration/Hydration APIs to shuttle dehydrated data between the server and client and rehydrate it where needed. Let's go over how to do this with both 3rd party critical data and 3rd party deferred data.\n\n## Critical Dehydration/Hydration\n\n**For critical data needed for the first render/paint**, TanStack Router supports **`dehydrate` and `hydrate`** options when configuring the `Router`. These callbacks are functions that are automatically called on the server and client when the router dehydrates and hydrates normally and allow you to augment the dehydrated data with your own data.\n\nThe `dehydrate` function can return any serializable JSON data which will get merged and injected into the dehydrated payload that is sent to the client.\n\nFor example, let's dehydrate and hydrate a TanStack Query `QueryClient` so that our data we fetched on the server will be available for hydration on the client.\n\n```tsx\n// src/router.tsx\n\nexport function createRouter() {\n // Make sure you create your loader client or similar data\n // stores inside of your `createRouter` function. This ensures\n // that your data stores are unique to each request and\n // always present on both server and client.\n const queryClient = new QueryClient()\n\n return createRouter({\n routeTree,\n // Optionally provide your loaderClient to the router context for\n // convenience (you can provide anything you want to the router\n // context!)\n context: {\n queryClient,\n },\n // On the server, dehydrate the loader client so the router\n // can serialize it and send it to the client for us\n dehydrate: () => {\n return {\n queryClientState: dehydrate(queryClient),\n }\n },\n // On the client, hydrate the loader client with the data\n // we dehydrated on the server\n hydrate: (dehydrated) => {\n hydrate(queryClient, dehydrated.queryClientState)\n },\n // Optionally, we can use `Wrap` to wrap our router in the loader client provider\n Wrap: ({ children }) => {\n return (\n \u003cQueryClientProvider client={queryClient}>\n {children}\n \u003c/QueryClientProvider>\n )\n },\n })\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6842,"content_sha256":"eeea0f91a75e858758bbbf8a2ea39be1a19828b4d6be61c6e2ea70a7e7f7dab5"},{"filename":"references/docs/router/guide/history-types.md","content":"---\ntitle: History Types\n---\n\nWhile it's not required to know the `@tanstack/history` API itself to use TanStack Router, it's a good idea to understand how it works. Under the hood, TanStack Router requires and uses a `history` abstraction to manage the routing history.\n\nIf you don't create a history instance, a browser-oriented instance of this API is created for you when the router is initialized. If you need a special history API type, You can use the `@tanstack/history` package to create your own:\n\n- `createBrowserHistory`: The default history type.\n- `createHashHistory`: A history type that uses a hash to track history.\n- `createMemoryHistory`: A history type that keeps the history in memory.\n\nOnce you have a history instance, you can pass it to the `Router` constructor:\n\n\n\n# React\n\n```ts\nimport { createMemoryHistory, createRouter } from '@tanstack/react-router'\n\nconst memoryHistory = createMemoryHistory({\n initialEntries: ['/'], // Pass your initial url\n})\n\nconst router = createRouter({ routeTree, history: memoryHistory })\n```\n\n# Solid\n\n```ts\nimport { createMemoryHistory, createRouter } from '@tanstack/solid-router'\n\nconst memoryHistory = createMemoryHistory({\n initialEntries: ['/'], // Pass your initial url\n})\n\nconst router = createRouter({ routeTree, history: memoryHistory })\n```\n\n\n\n## Browser Routing\n\nThe `createBrowserHistory` is the default history type. It uses the browser's history API to manage the browser history.\n\n## Hash Routing\n\nHash routing can be helpful if your server doesn't support rewrites to index.html for HTTP requests (among other environments that don't have a server).\n\n\n\n# React\n\n```ts\nimport { createHashHistory, createRouter } from '@tanstack/react-router'\n\nconst hashHistory = createHashHistory()\n\nconst router = createRouter({ routeTree, history: hashHistory })\n```\n\n# Solid\n\n```ts\nimport { createHashHistory, createRouter } from '@tanstack/solid-router'\n\nconst hashHistory = createHashHistory()\n\nconst router = createRouter({ routeTree, history: hashHistory })\n```\n\n\n\n## Memory Routing\n\nMemory routing is useful in environments that are not a browser or when you do not want components to interact with the URL.\n\n\n\n# React\n\n```ts\nimport { createMemoryHistory, createRouter } from '@tanstack/react-router'\n\nconst memoryHistory = createMemoryHistory({\n initialEntries: ['/'], // Pass your initial url\n})\n\nconst router = createRouter({ routeTree, history: memoryHistory })\n```\n\n# Solid\n\n```ts\nimport { createMemoryHistory, createRouter } from '@tanstack/solid-router'\n\nconst memoryHistory = createMemoryHistory({\n initialEntries: ['/'], // Pass your initial url\n})\n\nconst router = createRouter({ routeTree, history: memoryHistory })\n```\n\n\n\nRefer to the [SSR Guide](./ssr.md#automatic-server-history) for usage on the server for server-side rendering.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2813,"content_sha256":"bda3ab2993db49047d055dc5fc1df103af0e87f4046cde76fe220664e6440cea"},{"filename":"references/docs/router/guide/internationalization-i18n.md","content":"---\ntitle: Internationalization (i18n)\n---\n\nTanStack Router provides flexible and highly customizable primitives that can be composed to support common internationalization (i18n) routing patterns, such as **optional path parameters**, **route rewriting**, and **type-safe params**. This enables clean, SEO-friendly URLs, flexible locale handling, and seamless integration with i18n libraries.\n\nThis guide covers:\n\n- Prefix-based and optional-locale routing\n- Advanced routing patterns for i18n\n- Language navigation and switching\n- SEO considerations\n- Type safety\n- Integration patterns with i18n libraries (Paraglide)\n\n---\n\n## i18n with Optional Path Parameters\n\nThis pattern relies exclusively on TanStack Router features. It is suitable when:\n\n- You want full control over translations\n- You already manage translations manually\n- You do not need automatic locale detection\n\nOptional path parameters are ideal for implementing locale-aware routing without duplicating routes.\n\n```\n/{-$locale}/about\n```\n\nThis single route matches:\n\n- `/about` (default locale)\n- `/en/about`\n- `/fr/about`\n- `/es/about`\n\n### Prefix-based i18n\n\n```tsx\n// Route: /{-$locale}/about\nexport const Route = createFileRoute('/{-$locale}/about')({\n component: AboutComponent,\n})\n\nfunction AboutComponent() {\n const { locale } = Route.useParams()\n const currentLocale = locale || 'en'\n\n const content = {\n en: { title: 'About Us' },\n fr: { title: 'À Propos' },\n es: { title: 'Acerca de' },\n }\n\n return \u003ch1>{content[currentLocale].title}\u003c/h1>\n}\n```\n\n### Complex Routing Patterns\n\n```tsx\n// Route: /{-$locale}/blog/{-$category}/$slug\nexport const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({\n beforeLoad: ({ params }) => {\n const locale = params.locale || 'en'\n const validLocales = ['en', 'fr', 'es', 'de']\n\n if (params.locale && !validLocales.includes(params.locale)) {\n throw new Error('Invalid locale')\n }\n\n return { locale }\n },\n})\n```\n\n### Language Switching\n\n```tsx\n\u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={(prev) => ({\n ...prev,\n locale: prev.locale === 'en' ? undefined : 'fr',\n })}\n>\n Français\n\u003c/Link>\n```\n\n### Type-safe Locales\n\n```ts\ntype Locale = 'en' | 'fr' | 'es' | 'de'\n\nfunction isLocale(value?: string): value is Locale {\n return ['en', 'fr', 'es', 'de'].includes(value as Locale)\n}\n```\n\n---\n\n## i18n Library Integration Patterns\n\nTanStack Router is **library-agnostic**. You can integrate any i18n solution by mapping locale state to routing behavior.\n\nBelow is a recommended pattern using **Paraglide**.\n\n---\n\n## Client-side i18n with a Library (TanStack Router)\n\nThis pattern combines TanStack Router with a client-side i18n library. It is suitable when:\n\n- You want type-safe translations\n- You want localized URLs\n- You do not need server-side rendering\n\n### TanStack Router + Paraglide (Client-only)\n\nParaglide provides type-safe translations, locale detection, and URL localization that pair naturally with TanStack Router.\n\n**GitHub example:**\nhttps://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide\n\n### Project Setup\n\n```bash\nnpx @inlang/paraglide-js@latest init\n```\n\n```ts\nimport { paraglideVitePlugin } from '@inlang/paraglide-js'\n\nparaglideVitePlugin({\n project: './project.inlang',\n outdir: './app/paraglide',\n})\n```\n\n### URL Localization via Router Rewrite\n\nThe router's `rewrite` option enables bidirectional URL transformation, perfect for locale prefixes. For comprehensive documentation on URL rewrites including advanced patterns, see the [URL Rewrites guide](./url-rewrites.md).\n\n```ts\nimport { deLocalizeUrl, localizeUrl } from './paraglide/runtime'\n\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => deLocalizeUrl(url),\n output: ({ url }) => localizeUrl(url),\n },\n})\n```\n\n---\n\n## Server-side i18n (TanStack Start)\n\nThis pattern integrates i18n at the routing and server layers. It is suitable when:\n\n- You use TanStack Start\n- You need SSR or streaming\n- You want locale-aware redirects and metadata\n\n### TanStack Start + Paraglide\n\n**GitHub example:**\nhttps://github.com/TanStack/router/tree/main/examples/react/start-i18n-paraglide\n\n### Server Middleware (SSR)\n\n```ts\nimport { paraglideMiddleware } from './paraglide/server'\n\nexport default {\n fetch(req: Request) {\n return paraglideMiddleware(req, () => handler.fetch(req))\n },\n}\n```\n\n### HTML Language Attribute\n\n```tsx\nimport { getLocale } from '../paraglide/runtime'\n;\u003chtml lang={getLocale()} />\n```\n\n---\n\n## Offline-safe Redirects\n\nFor offline or client-only environments:\n\n```ts\nimport { shouldRedirect } from '../paraglide/runtime'\n\nbeforeLoad: async () => {\n const decision = await shouldRedirect({ url: window.location.href })\n if (decision.redirectUrl) {\n throw redirect({ href: decision.redirectUrl.href })\n }\n}\n```\n\n---\n\n## Type-safe Translated Pathnames\n\nTo ensure every route has translations, you can derive translated pathnames directly from the TanStack Router route tree.\n\n```ts\nimport { FileRoutesByTo } from '../routeTree.gen'\nimport { Locale } from '@/paraglide/runtime'\n```\n\nThis guarantees:\n\n- No missing translations\n- Full type safety\n- Compiler feedback for routing mistakes\n\n---\n\n## Prerendering Localized Routes\n\n```ts\nimport { localizeHref } from './paraglide/runtime'\n\nexport const prerenderRoutes = ['/', '/about'].map((path) => ({\n path: localizeHref(path),\n prerender: { enabled: true },\n}))\n```\n\n---\n\n## Additional i18n Integration Patterns\n\n### Intlayer (TanStack Start integration)\n\nhttps://intlayer.org/doc/environment/tanstack-start\n\n### use-intl (TanStack Start integration)\n\nhttps://nikuscs.com/blog/13-tanstackstart-i18n/\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5685,"content_sha256":"206a7ca82fe17b91495d25f89a54d85db69ecfc725279af1e0387e7b0c1a316c"},{"filename":"references/docs/router/guide/link-options.md","content":"---\ntitle: Link Options\n---\n\nYou may want to reuse options that are intended to be passed to `Link`, `redirect` or `navigate`. In which case you may decide an object literal is a good way to represent options passed to `Link`.\n\n```tsx\nconst dashboardLinkOptions = {\n to: '/dashboard',\n search: { search: '' },\n}\n\nfunction DashboardComponent() {\n return \u003cLink {...dashboardLinkOptions} />\n}\n```\n\nThere are a few problems here. `dashboardLinkOptions.to` is inferred as `string` which by default will resolve to every route when passed to `Link`, `navigate` or `redirect` (this particular issue could be fixed by `as const`). The other issue here is we do not know `dashboardLinkOptions` even passes the type checker until it is spread into `Link`. We could very easily create incorrect navigation options and only when the options are spread into `Link` do we know there is a type error.\n\n### Using `linkOptions` function to create re-usable options\n\n`linkOptions` is a function which type checks an object literal and returns the inferred input as is. This provides type safety on options exactly like `Link` before it is used allowing for easier maintenance and re-usability. Our above example using `linkOptions` looks like this:\n\n```tsx\nconst dashboardLinkOptions = linkOptions({\n to: '/dashboard',\n search: { search: '' },\n})\n\nfunction DashboardComponent() {\n return \u003cLink {...dashboardLinkOptions} />\n}\n```\n\nThis allows eager type checking of `dashboardLinkOptions` which can then be re-used anywhere\n\n```tsx\nconst dashboardLinkOptions = linkOptions({\n to: '/dashboard',\n search: { search: '' },\n})\n\nexport const Route = createFileRoute('/dashboard')({\n component: DashboardComponent,\n validateSearch: (input) => ({ search: input.search }),\n beforeLoad: () => {\n // can used in redirect\n throw redirect(dashboardLinkOptions)\n },\n})\n\nfunction DashboardComponent() {\n const navigate = useNavigate()\n\n return (\n \u003cdiv>\n {/** can be used in navigate */}\n \u003cbutton onClick={() => navigate(dashboardLinkOptions)} />\n\n {/** can be used in Link */}\n \u003cLink {...dashboardLinkOptions} />\n \u003c/div>\n )\n}\n```\n\n### An array of `linkOptions`\n\nWhen creating navigation you might loop over an array to construct a navigation bar. In which case `linkOptions` can be used to type check an array of object literals which are intended for `Link` props\n\n```tsx\nconst options = linkOptions([\n {\n to: '/dashboard',\n label: 'Summary',\n activeOptions: { exact: true },\n },\n {\n to: '/dashboard/invoices',\n label: 'Invoices',\n },\n {\n to: '/dashboard/users',\n label: 'Users',\n },\n])\n\nfunction DashboardComponent() {\n return (\n \u003c>\n \u003cdiv className=\"flex items-center border-b\">\n \u003ch2 className=\"text-xl p-2\">Dashboard\u003c/h2>\n \u003c/div>\n\n \u003cdiv className=\"flex flex-wrap divide-x\">\n {options.map((option) => {\n return (\n \u003cLink\n {...option}\n key={option.to}\n activeProps={{ className: `font-bold` }}\n className=\"p-2\"\n >\n {option.label}\n \u003c/Link>\n )\n })}\n \u003c/div>\n \u003chr />\n\n \u003cOutlet />\n \u003c/>\n )\n}\n```\n\nThe input of `linkOptions` is inferred and returned, as shown with the use of `label` as this does not exist on `Link` props\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3331,"content_sha256":"88e1cea274ea28093bc6ff2a170e9c583cc0a2c1f2a9d3ec8c0225c74c88becb"},{"filename":"references/docs/router/guide/navigation-blocking.md","content":"---\ntitle: Navigation Blocking\n---\n\nNavigation blocking is a way to prevent navigation from happening. This is typical if a user attempts to navigate while they:\n\n- Have unsaved changes\n- Are in the middle of a form\n- Are in the middle of a payment\n\nIn these situations, a prompt or custom UI should be shown to the user to confirm they want to navigate away.\n\n- If the user confirms, navigation will continue as normal\n- If the user cancels, all pending navigations will be blocked\n\n## How does navigation blocking work?\n\nNavigation blocking adds one or more layers of \"blockers\" to the entire underlying history API. If any blockers are present, navigation will be paused via one of the following ways:\n\n- Custom UI\n - If the navigation is triggered by something we control at the router level, we can allow you to perform any task or show any UI you'd like to the user to confirm the action. Each blocker's `blocker` function will be asynchronously and sequentially executed. If any blocker function resolves or returns `true`, the navigation will be allowed and all other blockers will continue to do the same until all blockers have been allowed to proceed. If any single blocker resolves or returns `false`, the navigation will be canceled and the rest of the `blocker` functions will be ignored.\n- The `onbeforeunload` event\n - For page events that we cannot control directly, we rely on the browser's `onbeforeunload` event. If the user attempts to close the tab or window, refresh, or \"unload\" the page assets in any way, the browser's generic \"Are you sure you want to leave?\" dialog will be shown. If the user confirms, all blockers will be bypassed and the page will unload. If the user cancels, the unload will be cancelled, and the page will remain as is.\n\n## How do I use navigation blocking?\n\nThere are 2 ways to use navigation blocking:\n\n- Hook/logical-based blocking\n- Component-based blocking\n\n## Hook/logical-based blocking\n\nLet's imagine we want to prevent navigation if a form is dirty. We can do this by using the `useBlocker` hook:\n\n\n\n# React\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n useBlocker({\n shouldBlockFn: () => {\n if (!formIsDirty) return false\n\n const shouldLeave = confirm('Are you sure you want to leave?')\n return !shouldLeave\n },\n })\n\n // ...\n}\n```\n\n# Solid\n\n```tsx\nimport { useBlocker } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = createSignal(false)\n\n useBlocker({\n shouldBlockFn: () => {\n if (!formIsDirty()) return false\n\n const shouldLeave = confirm('Are you sure you want to leave?')\n return !shouldLeave\n },\n })\n\n // ...\n}\n```\n\n\n\n`shouldBlockFn` gives you type safe access to the `current` and `next` location:\n\n\n\n# React\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n // always block going from /foo to /bar/123?hello=world\n const { proceed, reset, status } = useBlocker({\n shouldBlockFn: ({ current, next }) => {\n return (\n current.routeId === '/foo' &&\n next.fullPath === '/bar/$id' &&\n next.params.id === 123 &&\n next.search.hello === 'world'\n )\n },\n withResolver: true,\n })\n\n // ...\n}\n```\n\n# Solid\n\n```tsx\nimport { useBlocker } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n // always block going from /foo to /bar/123?hello=world\n const { proceed, reset, status } = useBlocker({\n shouldBlockFn: ({ current, next }) => {\n return (\n current.routeId === '/foo' &&\n next.fullPath === '/bar/$id' &&\n next.params.id === 123 &&\n next.search.hello === 'world'\n )\n },\n withResolver: true,\n })\n\n // ...\n}\n```\n\n\n\nNote that even if `shouldBlockFn` returns `false`, the browser's `beforeunload` event may still be triggered on page reloads or tab closing. To gain control over this, you can use the `enableBeforeUnload` option to conditionally register the `beforeunload` handler:\n\n\n\n# React\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n useBlocker({\n {/* ... */}\n enableBeforeUnload: formIsDirty, // or () => formIsDirty\n })\n\n // ...\n}\n```\n\n# Solid\n\n```tsx\nimport { useBlocker } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n useBlocker({\n {/* ... */}\n enableBeforeUnload: formIsDirty(),\n })\n\n // ...\n}\n```\n\n\n\nYou can find more information about the `useBlocker` hook in the [API reference](../api/router/useBlockerHook.md).\n\n## Component-based blocking\n\nIn addition to logical/hook based blocking, you can use the `Block` component to achieve similar results:\n\n\n\n# React\n\n```tsx\nimport { Block } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n return (\n \u003cBlock\n shouldBlockFn={() => {\n if (!formIsDirty) return false\n\n const shouldLeave = confirm('Are you sure you want to leave?')\n return !shouldLeave\n }}\n enableBeforeUnload={formIsDirty}\n />\n )\n\n // OR\n\n return (\n \u003cBlock\n shouldBlockFn={() => formIsDirty}\n enableBeforeUnload={formIsDirty}\n withResolver\n >\n {({ status, proceed, reset }) => \u003c>{/* ... */}\u003c/>}\n \u003c/Block>\n )\n}\n```\n\n# Solid\n\n```tsx\nimport { Block } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = createSignal(false)\n\n return (\n \u003cBlock\n shouldBlockFn={() => {\n if (!formIsDirty()) return false\n\n const shouldLeave = confirm('Are you sure you want to leave?')\n return !shouldLeave\n }}\n />\n )\n\n // OR\n\n return (\n \u003cBlock shouldBlockFn={() => !formIsDirty} withResolver>\n {({ status, proceed, reset }) => \u003c>{/* ... */}\u003c/>}\n \u003c/Block>\n )\n}\n```\n\n\n\n## How can I show a custom UI?\n\nIn most cases, using `window.confirm` in the `shouldBlockFn` function with `withResolver: false` in the hook is enough since it will clearly show the user that the navigation is being blocked and resolve the blocking based on their response.\n\nHowever, in some situations, you might want to show a custom UI that is intentionally less disruptive and more integrated with your app's design.\n\n**Note:** The return value of `shouldBlockFn` does not resolve the blocking if `withResolver` is `true`.\n\n### Hook/logical-based custom UI with resolver\n\n\n\n# React\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n const { proceed, reset, status } = useBlocker({\n shouldBlockFn: () => formIsDirty,\n withResolver: true,\n })\n\n // ...\n\n return (\n \u003c>\n {/* ... */}\n {status === 'blocked' && (\n \u003cdiv>\n \u003cp>Are you sure you want to leave?\u003c/p>\n \u003cbutton onClick={proceed}>Yes\u003c/button>\n \u003cbutton onClick={reset}>No\u003c/button>\n \u003c/div>\n )}\n \u003c/>\n}\n```\n\n# Solid\n\n```tsx\nimport { useBlocker } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = createSignal(false)\n\n const { proceed, reset, status } = useBlocker({\n shouldBlockFn: () => formIsDirty(),\n withResolver: true,\n })\n\n // ...\n\n return (\n \u003c>\n {/* ... */}\n {status === 'blocked' && (\n \u003cdiv>\n \u003cp>Are you sure you want to leave?\u003c/p>\n \u003cbutton onClick={proceed}>Yes\u003c/button>\n \u003cbutton onClick={reset}>No\u003c/button>\n \u003c/div>\n )}\n \u003c/>\n}\n```\n\n\n\n### Hook/logical-based custom UI without resolver\n\n\n\n# React\n\n```tsx\nimport { useBlocker } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n useBlocker({\n shouldBlockFn: () => {\n if (!formIsDirty) {\n return false\n }\n\n const shouldBlock = new Promise\u003cboolean>((resolve) => {\n // Using a modal manager of your choice\n modals.open({\n title: 'Are you sure you want to leave?',\n children: (\n \u003cSaveBlocker\n confirm={() => {\n modals.closeAll()\n resolve(false)\n }}\n reject={() => {\n modals.closeAll()\n resolve(true)\n }}\n />\n ),\n onClose: () => resolve(true),\n })\n })\n return shouldBlock\n },\n })\n\n // ...\n}\n```\n\n# Solid\n\n```tsx\nimport { useBlocker } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = createSignal(false)\n\n useBlocker({\n shouldBlockFn: () => {\n if (!formIsDirty()) {\n return false\n }\n\n const shouldBlock = new Promise\u003cboolean>((resolve) => {\n // Using a modal manager of your choice\n modals.open({\n title: 'Are you sure you want to leave?',\n children: (\n \u003cSaveBlocker\n confirm={() => {\n modals.closeAll()\n resolve(false)\n }}\n reject={() => {\n modals.closeAll()\n resolve(true)\n }}\n />\n ),\n onClose: () => resolve(true),\n })\n })\n return shouldBlock\n },\n })\n\n // ...\n}\n```\n\n\n\n### Component-based custom UI\n\nSimilarly to the hook, the `Block` component returns the same state and functions as render props:\n\n\n\n# React\n\n```tsx\nimport { Block } from '@tanstack/react-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = useState(false)\n\n return (\n \u003cBlock shouldBlockFn={() => formIsDirty} withResolver>\n {({ status, proceed, reset }) => (\n \u003c>\n {/* ... */}\n {status === 'blocked' && (\n \u003cdiv>\n \u003cp>Are you sure you want to leave?\u003c/p>\n \u003cbutton onClick={proceed}>Yes\u003c/button>\n \u003cbutton onClick={reset}>No\u003c/button>\n \u003c/div>\n )}\n \u003c/>\n )}\n \u003c/Block>\n )\n}\n```\n\n# Solid\n\n```tsx\nimport { Block } from '@tanstack/solid-router'\n\nfunction MyComponent() {\n const [formIsDirty, setFormIsDirty] = createSignal(false)\n\n return (\n \u003cBlock shouldBlockFn={() => formIsDirty()} withResolver>\n {({ status, proceed, reset }) => (\n \u003c>\n {/* ... */}\n {status === 'blocked' && (\n \u003cdiv>\n \u003cp>Are you sure you want to leave?\u003c/p>\n \u003cbutton onClick={proceed}>Yes\u003c/button>\n \u003cbutton onClick={reset}>No\u003c/button>\n \u003c/div>\n )}\n \u003c/>\n )}\n \u003c/Block>\n )\n}\n```\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10705,"content_sha256":"437ef93d6ca8731b5811f89d1ad7cd37b8e61488d592ba01e9fcb5a060e725e4"},{"filename":"references/docs/router/guide/navigation.md","content":"---\ntitle: Navigation\n---\n\n## Everything is Relative\n\nBelieve it or not, every navigation within an app is **relative**, even if you aren't using explicit relative path syntax (`../../somewhere`). Any time a link is clicked or an imperative navigation call is made, you will always have an **origin** path and a **destination** path which means you are navigating **from** one route **to** another route.\n\nTanStack Router keeps this constant concept of relative navigation in mind for every navigation, so you'll constantly see two properties in the API:\n\n- `from` - The origin route path\n- `to` - The destination route path\n\n> If a `from` route path isn't provided the router will assume you are navigating from the root `/` route and only auto-complete absolute paths. After all, you need to know where you are from in order to know where you're going .\n\n## Shared Navigation API\n\nEvery navigation and route matching API in TanStack Router uses the same core interface with minor differences depending on the API. This means that you can learn navigation and route matching once and use the same syntax and concepts across the library.\n\n### `ToOptions` Interface\n\nThis is the core `ToOptions` interface that is used in every navigation and route matching API:\n\n```ts\ntype ToOptions\u003c\n TRouteTree extends AnyRoute = AnyRoute,\n TFrom extends RoutePaths\u003cTRouteTree> | string = string,\n TTo extends string = '',\n> = {\n // `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.\n from?: string\n // `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.\n to: string\n // `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.\n params:\n | Record\u003cstring, unknown>\n | ((prevParams: Record\u003cstring, unknown>) => Record\u003cstring, unknown>)\n // `search` is either an object of query params or a function that supplies the previous search and allows you to return new ones. Depending on the `from` and `to` route, you may need to supply none, some or all of the query params. TypeScript will notify you of the required search params if there are any.\n search:\n | Record\u003cstring, unknown>\n | ((prevSearch: Record\u003cstring, unknown>) => Record\u003cstring, unknown>)\n // `hash` is either a string or a function that supplies the previous hash and allows you to return a new one.\n hash?: string | ((prevHash: string) => string)\n // `state` is either an object of state or a function that supplies the previous state and allows you to return a new one. State is stored in the history API and can be useful for passing data between routes that you do not want to permanently store in URL search params.\n state?:\n | Record\u003cstring, any>\n | ((prevState: Record\u003cstring, unknown>) => Record\u003cstring, unknown>)\n // `mask` is another navigation object used to mask the URL shown in the browser for this navigation.\n mask?: ToMaskOptions\u003cTRouteTree>\n}\n\ntype ToMaskOptions\u003cTRouteTree extends AnyRoute = AnyRoute> = {\n // `from`, `to`, `params`, `search`, `hash`, and `state` behave the same as in `ToOptions`.\n // `mask` itself is not allowed inside `ToMaskOptions`.\n from?: string\n to: string\n params:\n | Record\u003cstring, unknown>\n | ((prevParams: Record\u003cstring, unknown>) => Record\u003cstring, unknown>)\n search:\n | Record\u003cstring, unknown>\n | ((prevSearch: Record\u003cstring, unknown>) => Record\u003cstring, unknown>)\n hash?: string | ((prevHash: string) => string)\n state?:\n | Record\u003cstring, any>\n | ((prevState: Record\u003cstring, unknown>) => Record\u003cstring, unknown>)\n // If true, the URL will unmask on page reload.\n unmaskOnReload?: boolean\n}\n```\n\n> Every route object has a `to` property, which can be used as the `to` for any navigation or route matching API. Where possible, this will allow you to avoid plain strings and use type-safe route references instead:\n\n```tsx\nimport { Route as aboutRoute } from './routes/about.tsx'\n\nfunction Comp() {\n return \u003cLink to={aboutRoute.to}>About\u003c/Link>\n}\n```\n\n### `NavigateOptions` Interface\n\nThis is the core `NavigateOptions` interface that extends `ToOptions`. Any API that is actually performing a navigation will use this interface:\n\n```ts\nexport type NavigateOptions\u003c\n TRouteTree extends AnyRoute = AnyRoute,\n TFrom extends RoutePaths\u003cTRouteTree> | string = string,\n TTo extends string = '',\n> = ToOptions\u003cTRouteTree, TFrom, TTo> & {\n // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.\n replace?: boolean\n // `resetScroll` is a boolean that determines whether scroll position will be reset to 0,0 after the location is committed to browser history.\n resetScroll?: boolean\n // `hashScrollIntoView` is a boolean or object that determines whether an id matching the hash will be scrolled into view after the location is committed to history.\n hashScrollIntoView?: boolean | ScrollIntoViewOptions\n // `viewTransition` is either a boolean or function that determines if and how the browser will call document.startViewTransition() when navigating.\n viewTransition?: boolean | ViewTransitionOptions\n // `ignoreBlocker` is a boolean that determines if navigation should ignore any blockers that might prevent it.\n ignoreBlocker?: boolean\n // `reloadDocument` is a boolean that determines if navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation.\n reloadDocument?: boolean\n // `href` is a string that can be used in place of `to` to navigate to a full built href, e.g. pointing to an external target.\n href?: string\n}\n```\n\n`NavigateOptions` includes all `ToOptions` fields, including `mask`.\n\n### `LinkOptions` Interface\n\nAnywhere an actual `\u003ca>` tag the `LinkOptions` interface which extends `NavigateOptions` will be available:\n\n```tsx\nexport type LinkOptions\u003c\n TRouteTree extends AnyRoute = AnyRoute,\n TFrom extends RoutePaths\u003cTRouteTree> | string = string,\n TTo extends string = '',\n> = NavigateOptions\u003cTRouteTree, TFrom, TTo> & {\n // The standard anchor tag target attribute\n target?: HTMLAnchorElement['target']\n // Defaults to `{ exact: false, includeHash: false }`\n activeOptions?: {\n exact?: boolean\n includeHash?: boolean\n includeSearch?: boolean\n explicitUndefined?: boolean\n }\n // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.\n preload?: false | 'intent'\n // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.\n preloadDelay?: number\n // If true, will render the link without the href attribute\n disabled?: boolean\n}\n```\n\nSince `LinkOptions` extends `NavigateOptions`, it also supports `mask`.\n\n## Navigation API\n\nWith relative navigation and all of the interfaces in mind now, let's talk about the different flavors of navigation API at your disposal:\n\n- The `\u003cLink>` component\n - Generates an actual `\u003ca>` tag with a valid `href` which can be click or even cmd/ctrl + clicked to open in a new tab\n- The `useNavigate()` hook\n - When possible, `Link` component should be used for navigation, but sometimes you need to navigate imperatively as a result of a side-effect. `useNavigate` returns a function that can be called to perform an immediate client-side navigation.\n- The `\u003cNavigate>` component\n - Renders nothing and performs an immediate client-side navigation.\n- The `Router.navigate()` method\n - This is the most powerful navigation API in TanStack Router. Similar to `useNavigate`, it imperatively navigates, but is available everywhere you have access to your router.\n\n None of these APIs are a replacement for server-side redirects. If you need to redirect a user immediately from one route to another before mounting your application, use a server-side redirect instead of a client-side navigation.\n\n## `\u003cLink>` Component\n\nThe `Link` component is the most common way to navigate within an app. It renders an actual `\u003ca>` tag with a valid `href` attribute which can be clicked or even cmd/ctrl + clicked to open in a new tab. It also supports any normal `\u003ca>` attributes including `target` to open links in new windows, etc.\n\nIn addition to the [`LinkOptions`](#linkoptions-interface) interface, the `Link` component also supports the following props:\n\n```tsx\nexport type LinkProps\u003c\n TFrom extends RoutePaths\u003cRegisteredRouter['routeTree']> | string = string,\n TTo extends string = '',\n> = LinkOptions\u003cRegisteredRouter['routeTree'], TFrom, TTo> & {\n // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n activeProps?:\n | FrameworkHTMLAnchorTagAttributes\n | (() => FrameworkHTMLAnchorAttributes)\n // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n inactiveProps?:\n | FrameworkHTMLAnchorAttributes\n | (() => FrameworkHTMLAnchorAttributes)\n}\n```\n\n### Absolute Links\n\nLet's make a simple static link!\n\n\n\n# React\n\n```tsx\nimport { Link } from '@tanstack/react-router'\n\nconst link = \u003cLink to=\"/about\">About\u003c/Link>\n```\n\n# Solid\n\n```tsx\nimport { Link } from '@tanstack/solid-router'\n\nconst link = \u003cLink to=\"/about\">About\u003c/Link>\n```\n\n\n\n### Dynamic Links\n\nDynamic links are links that have dynamic segments in them. For example, a link to a blog post might look like this:\n\n```tsx\nconst link = (\n \u003cLink\n to=\"/blog/post/$postId\"\n params={{\n postId: 'my-first-blog-post',\n }}\n >\n Blog Post\n \u003c/Link>\n)\n```\n\nKeep in mind that normally dynamic segment params are `string` values, but they can also be any other type that you parse them to in your route options. Either way, the type will be checked at compile time to ensure that you are passing the correct type.\n\n### Relative Links\n\nBy default, all links are absolute unless a `from` route path is provided. This means that the above link will always navigate to the `/about` route regardless of what route you are currently on.\n\nRelative links can be combined with a `from` route path. If a from route path isn't provided, relative paths default to the current active location.\n\n> [!NOTE]\n> Keep in mind that when calling useNavigate as a method on the route, for example `Route.useNavigate`, then the `from` location is predefined to be the route it's called on.\n>\n> Another common pitfall is when using this in a pathless layout route, since the pathless layout route does not have an actual path, the `from` location is regarded as the parent of the pathless layout route. Hence relative routing will be resolved from this parent.\n\n```tsx\nconst postIdRoute = createRoute({\n path: '/blog/post/$postId',\n})\n\nconst link = (\n \u003cLink from={postIdRoute.fullPath} to=\"../categories\">\n Categories\n \u003c/Link>\n)\n```\n\nAs seen above, it's common to provide the `route.fullPath` as the `from` route path. This is because the `route.fullPath` is a reference that will update if you refactor your application. However, sometimes it's not possible to import the route directly, in which case it's fine to provide the route path directly as a string. It will still get type-checked as per usual!\n\n### Special relative paths: `\".\"` and `\"..\"`\n\nQuite often you might want to reload the current location or another `from` path, for example, to rerun the loaders on the current and/or parent routes, or maybe navigate back to a parent route. This can be achieved by specifying a `to` route path of `\".\"` which will reload the current location or provided `from` path.\n\nAnother common need is to navigate one route back relative to the current location or another path. By specifying a `to` route path of `\"..\"` navigation will be resolved to the first parent route preceding the current location.\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n component: PostComponent,\n})\n\nfunction PostComponent() {\n return (\n \u003cdiv>\n \u003cLink to=\".\">Reload the current route of /posts/$postId\u003c/Link>\n \u003cLink to=\"..\">Navigate back to /posts\u003c/Link>\n // the below are all equivalent\n \u003cLink to=\"/posts\">Navigate back to /posts\u003c/Link>\n \u003cLink from=\"/posts\" to=\".\">\n Navigate back to /posts\n \u003c/Link>\n // the below are all equivalent\n \u003cLink to=\"/\">Navigate to root\u003c/Link>\n \u003cLink from=\"/posts\" to=\"..\">\n Navigate to root\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\n### Search Param Links\n\nSearch params are a great way to provide additional context to a route. For example, you might want to provide a search query to a search page:\n\n```tsx\nconst link = (\n \u003cLink\n to=\"/search\"\n search={{\n query: 'tanstack',\n }}\n >\n Search\n \u003c/Link>\n)\n```\n\nIt's also common to want to update a single search param without supplying any other information about the existing route. For example, you might want to update the page number of a search result:\n\n```tsx\nconst link = (\n \u003cLink\n to=\".\"\n search={(prev) => ({\n ...prev,\n page: prev.page + 1,\n })}\n >\n Next Page\n \u003c/Link>\n)\n```\n\n### Search Param Type Safety\n\nSearch params are a highly dynamic state management mechanism, so it's important to ensure that you are passing the correct types to your search params. We'll see in a later section in detail how to validate and ensure search params typesafety, among other great features!\n\n### Hash Links\n\nHash links are a great way to link to a specific section of a page. For example, you might want to link to a specific section of a blog post:\n\n```tsx\nconst link = (\n \u003cLink\n to=\"/blog/post/$postId\"\n params={{\n postId: 'my-first-blog-post',\n }}\n hash=\"section-1\"\n >\n Section 1\n \u003c/Link>\n)\n```\n\n> When directly navigating to a URL with a hash fragment, the fragment is only available on the client; the browser does not send the fragment to the server as part of the request URL.\n>\n> This means that if you are using a server-side rendering approach, the hash fragment will not be available on the server-side, and hydration mismatches can occur when using the hash for rendering markup.\n>\n> Examples of this would be:\n>\n> - returning the hash value in the markup,\n> - conditional rendering based on the hash value, or\n> - setting the Link as active based on the hash value.\n\n### Navigating with Optional Parameters\n\nOptional path parameters provide flexible navigation patterns where you can include or omit parameters as needed. Optional parameters use the `{-$paramName}` syntax and offer fine-grained control over URL structure.\n\n#### Parameter Inheritance vs Removal\n\nWhen navigating with optional parameters, you have two main strategies:\n\n**Inheriting Current Parameters**\nUse `params: {}` to inherit all current route parameters:\n\n```tsx\n// Inherits current route parameters\n\u003cLink to=\"/posts/{-$category}\" params={{}}>\n All Posts\n\u003c/Link>\n```\n\n**Removing Parameters** \nSet parameters to `undefined` to explicitly remove them:\n\n```tsx\n// Removes the category parameter\n\u003cLink to=\"/posts/{-$category}\" params={{ category: undefined }}>\n All Posts\n\u003c/Link>\n```\n\n#### Basic Optional Parameter Navigation\n\n```tsx\n// Navigate with optional parameter\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: 'tech' }}\n>\n Tech Posts\n\u003c/Link>\n\n// Navigate without optional parameter\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: undefined }}\n>\n All Posts\n\u003c/Link>\n\n// Navigate using parameter inheritance\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{}}\n>\n Current Category\n\u003c/Link>\n```\n\n#### Function-Style Parameter Updates\n\nFunction-style parameter updates are particularly useful with optional parameters:\n\n```tsx\n// Remove a parameter using function syntax\n\u003cLink\n to=\"/posts/{-$category}\"\n params={(prev) => ({ ...prev, category: undefined })}\n>\n Clear Category\n\u003c/Link>\n\n// Update a parameter while keeping others\n\u003cLink\n to=\"/articles/{-$category}/{-$slug}\"\n params={(prev) => ({ ...prev, category: 'news' })}\n>\n News Articles\n\u003c/Link>\n\n// Conditionally set parameters\n\u003cLink\n to=\"/posts/{-$category}\"\n params={(prev) => ({\n ...prev,\n category: someCondition ? 'tech' : undefined\n })}\n>\n Conditional Category\n\u003c/Link>\n```\n\n#### Multiple Optional Parameters\n\nWhen working with multiple optional parameters, you can mix and match which ones to include:\n\n```tsx\n// Navigate with some optional parameters\n\u003cLink\n to=\"/posts/{-$category}/{-$slug}\"\n params={{ category: 'tech', slug: undefined }}\n>\n Tech Posts\n\u003c/Link>\n\n// Remove all optional parameters\n\u003cLink\n to=\"/posts/{-$category}/{-$slug}\"\n params={{ category: undefined, slug: undefined }}\n>\n All Posts\n\u003c/Link>\n\n// Set multiple parameters\n\u003cLink\n to=\"/posts/{-$category}/{-$slug}\"\n params={{ category: 'tech', slug: 'react-tips' }}\n>\n Specific Post\n\u003c/Link>\n```\n\n#### Mixed Required and Optional Parameters\n\nOptional parameters work seamlessly with required parameters:\n\n```tsx\n// Required 'id', optional 'tab'\n\u003cLink\n to=\"/users/$id/{-$tab}\"\n params={{ id: '123', tab: 'settings' }}\n>\n User Settings\n\u003c/Link>\n\n// Remove optional parameter while keeping required\n\u003cLink\n to=\"/users/$id/{-$tab}\"\n params={{ id: '123', tab: undefined }}\n>\n User Profile\n\u003c/Link>\n\n// Use function style with mixed parameters\n\u003cLink\n to=\"/users/$id/{-$tab}\"\n params={(prev) => ({ ...prev, tab: 'notifications' })}\n>\n User Notifications\n\u003c/Link>\n```\n\n#### Advanced Optional Parameter Patterns\n\n**Prefix and Suffix Parameters**\nOptional parameters with prefix/suffix work with navigation:\n\n```tsx\n// Navigate to file with optional name\n\u003cLink\n to=\"/files/prefix{-$name}.txt\"\n params={{ name: 'document' }}\n>\n Document File\n\u003c/Link>\n\n// Navigate to file without optional name\n\u003cLink\n to=\"/files/prefix{-$name}.txt\"\n params={{ name: undefined }}\n>\n Default File\n\u003c/Link>\n```\n\n**All Optional Parameters**\nRoutes where all parameters are optional:\n\n```tsx\n// Navigate to specific date\n\u003cLink\n to=\"/{-$year}/{-$month}/{-$day}\"\n params={{ year: '2023', month: '12', day: '25' }}\n>\n Christmas 2023\n\u003c/Link>\n\n// Navigate to partial date\n\u003cLink\n to=\"/{-$year}/{-$month}/{-$day}\"\n params={{ year: '2023', month: '12', day: undefined }}\n>\n December 2023\n\u003c/Link>\n\n// Navigate to root with all parameters removed\n\u003cLink\n to=\"/{-$year}/{-$month}/{-$day}\"\n params={{ year: undefined, month: undefined, day: undefined }}\n>\n Home\n\u003c/Link>\n```\n\n#### Navigation with Search Params and Optional Parameters\n\nOptional parameters work great in combination with search params:\n\n```tsx\n// Combine optional path params with search params\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: 'tech' }}\n search={{ page: 1, sort: 'newest' }}\n>\n Tech Posts - Page 1\n\u003c/Link>\n\n// Remove path param but keep search params\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: undefined }}\n search={(prev) => prev}\n>\n All Posts - Same Filters\n\u003c/Link>\n```\n\n#### Imperative Navigation with Optional Parameters\n\nAll the same patterns work with imperative navigation:\n\n```tsx\nfunction Component() {\n const navigate = useNavigate()\n\n const clearFilters = () => {\n navigate({\n to: '/posts/{-$category}/{-$tag}',\n params: { category: undefined, tag: undefined },\n })\n }\n\n const setCategory = (category: string) => {\n navigate({\n to: '/posts/{-$category}/{-$tag}',\n params: (prev) => ({ ...prev, category }),\n })\n }\n\n const applyFilters = (category?: string, tag?: string) => {\n navigate({\n to: '/posts/{-$category}/{-$tag}',\n params: { category, tag },\n })\n }\n}\n```\n\n### Active & Inactive Props\n\nThe `Link` component supports two additional props: `activeProps` and `inactiveProps`. These props are functions that return additional props for the `active` and `inactive` states of the link. All props other than styles and classes passed here will override the original props passed to `Link`. Any styles or classes passed are merged together.\n\nHere's an example:\n\n```tsx\nconst link = (\n \u003cLink\n to=\"/blog/post/$postId\"\n params={{\n postId: 'my-first-blog-post',\n }}\n activeProps={{\n style: {\n fontWeight: 'bold',\n },\n }}\n >\n Section 1\n \u003c/Link>\n)\n```\n\n### The `data-status` attribute\n\nIn addition to the `activeProps` and `inactiveProps` props, the `Link` component also adds a `data-status` attribute to the rendered element when it is in an active state. This attribute will be `active` or `undefined` depending on the current state of the link. This can come in handy if you prefer to use data-attributes to style your links instead of props.\n\n### Active Options\n\nThe `Link` component comes with an `activeOptions` property that offers a few options of determining if a link is active or not. The following interface describes those options:\n\n```tsx\nexport interface ActiveOptions {\n // If true, the link will be active if the current route matches the `to` route path exactly (no children routes)\n // Defaults to `false`\n exact?: boolean\n // If true, the link will only be active if the current URL hash matches the `hash` prop\n // Defaults to `false`\n includeHash?: boolean // Defaults to false\n // If true, the link will only be active if the current URL search params inclusively match the `search` prop\n // Defaults to `true`\n includeSearch?: boolean\n // This modifies the `includeSearch` behavior.\n // If true, properties in `search` that are explicitly `undefined` must NOT be present in the current URL search params for the link to be active.\n // defaults to `false`\n explicitUndefined?: boolean\n}\n```\n\nBy default, it will check if the resulting **pathname** is a prefix of the current route. If any search params are provided, it will check that they _inclusively_ match those in the current location. Hashes are not checked by default.\n\nFor example, if you are on the `/blog/post/my-first-blog-post` route, the following links will be active:\n\n```tsx\nconst link1 = (\n \u003cLink to=\"/blog/post/$postId\" params={{ postId: 'my-first-blog-post' }}>\n Blog Post\n \u003c/Link>\n)\nconst link2 = \u003cLink to=\"/blog/post\">Blog Post\u003c/Link>\nconst link3 = \u003cLink to=\"/blog\">Blog Post\u003c/Link>\n```\n\nHowever, the following links will not be active:\n\n```tsx\nconst link4 = (\n \u003cLink to=\"/blog/post/$postId\" params={{ postId: 'my-second-blog-post' }}>\n Blog Post\n \u003c/Link>\n)\n```\n\nIt's common for some links to only be active if they are an exact match. A good example of this would be a link to the home page. In scenarios like these, you can pass the `exact: true` option:\n\n```tsx\nconst link = (\n \u003cLink to=\"/\" activeOptions={{ exact: true }}>\n Home\n \u003c/Link>\n)\n```\n\nThis will ensure that the link is not active when you are a child route.\n\nA few more options to be aware of:\n\n- If you want to include the hash in your matching, you can pass the `includeHash: true` option\n- If you do **not** want to include the search params in your matching, you can pass the `includeSearch: false` option\n\n### Passing `isActive` to children\n\nThe `Link` component accepts a function for its children, allowing you to propagate its `isActive` property to children. For example, you could style a child component based on whether the parent link is active:\n\n```tsx\nconst link = (\n \u003cLink to=\"/blog/post\">\n {({ isActive }) => {\n return (\n \u003c>\n \u003cspan>My Blog Post\u003c/span>\n \u003cicon className={isActive ? 'active' : 'inactive'} />\n \u003c/>\n )\n }}\n \u003c/Link>\n)\n```\n\n### Link Preloading\n\nThe `Link` component supports automatically preloading routes on intent (hovering or touchstart for now). This can be configured as a default in the router options (which we'll talk more about soon) or by passing a `preload='intent'` prop to the `Link` component. Here's an example:\n\n```tsx\nconst link = (\n \u003cLink to=\"/blog/post/$postId\" preload=\"intent\">\n Blog Post\n \u003c/Link>\n)\n```\n\nWith preloading enabled and relatively quick asynchronous route dependencies (if any), this simple trick can increase the perceived performance of your application with very little effort.\n\nWhat's even better is that by using a cache-first library like `@tanstack/query`, preloaded routes will stick around and be ready for a stale-while-revalidate experience if the user decides to navigate to the route later on.\n\n### Link Preloading Delay\n\nAlong with preloading is a configurable delay which determines how long a user must hover over a link to trigger the intent-based preloading. The default delay is 50 milliseconds, but you can change this by passing a `preloadDelay` prop to the `Link` component with the number of milliseconds you'd like to wait:\n\n```tsx\nconst link = (\n \u003cLink to=\"/blog/post/$postId\" preload=\"intent\" preloadDelay={100}>\n Blog Post\n \u003c/Link>\n)\n```\n\n## `useNavigate`\n\n> Because of the `Link` component's built-in affordances around `href`, cmd/ctrl + click-ability, and active/inactive capabilities, it's recommended to use the `Link` component instead of `useNavigate` for anything the user can interact with (e.g. links, buttons). However, there are some cases where `useNavigate` is necessary to handle side-effect navigations (e.g. a successful async action that results in a navigation).\n\nThe `useNavigate` hook returns a `navigate` function that can be called to imperatively navigate. It's a great way to navigate to a route from a side-effect (e.g. a successful async action). Here's an example:\n\n```tsx\nfunction Component() {\n const navigate = useNavigate({ from: '/posts/$postId' })\n\n const handleSubmit = async (e: FrameworkFormEvent) => {\n e.preventDefault()\n\n const response = await fetch('/posts', {\n method: 'POST',\n body: JSON.stringify({ title: 'My First Post' }),\n })\n\n const { id: postId } = await response.json()\n\n if (response.ok) {\n navigate({ to: '/posts/$postId', params: { postId } })\n }\n }\n}\n```\n\n> As shown above, you can pass the `from` option to specify the route to navigate from in the hook call. While this is also possible to pass in the resulting `navigate` function each time you call it, it's recommended to pass it here to reduce on potential error and also not type as much!\n\n### `navigate` Options\n\nThe `navigate` function returned by `useNavigate` accepts the [`NavigateOptions` interface](#navigateoptions-interface)\n\n## `Navigate` Component\n\nOccasionally, you may find yourself needing to navigate immediately when a component mounts. Your first instinct might be to reach for `useNavigate` and an immediate side-effect (e.g. useEffect), but this is unnecessary. Instead, you can render the `Navigate` component to achieve the same result:\n\n```tsx\nfunction Component() {\n return \u003cNavigate to=\"/posts/$postId\" params={{ postId: 'my-first-post' }} />\n}\n```\n\nThink of the `Navigate` component as a way to navigate to a route immediately when a component mounts. It's a great way to handle client-only redirects. It is _definitely not_ a substitute for handling server-aware redirects responsibly on the server.\n\n## `router.navigate`\n\nThe `router.navigate` method is the same as the `navigate` function returned by `useNavigate` and accepts the same [`NavigateOptions` interface](#navigateoptions-interface). Unlike the `useNavigate` hook, it is available anywhere your `router` instance is available and is thus a great way to navigate imperatively from anywhere in your application, including outside of your framework.\n\n## `useMatchRoute` and `\u003cMatchRoute>`\n\nThe `useMatchRoute` hook and `\u003cMatchRoute>` component are the same thing, but the hook is a bit more flexible. They both accept the standard navigation `ToOptions` interface either as options or props and return `true/false` if that route is currently matched. It also has a handy `pending` option that will return `true` if the route is currently pending (e.g. a route is currently transitioning to that route). This can be extremely useful for showing optimistic UI around where a user is navigating:\n\n```tsx\nfunction Component() {\n return (\n \u003cdiv>\n \u003cLink to=\"/users\">\n Users\n \u003cMatchRoute to=\"/users\" pending>\n \u003cSpinner />\n \u003c/MatchRoute>\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\nThe component version `\u003cMatchRoute>` can also be used with a function as children to render something when the route is matched:\n\n```tsx\nfunction Component() {\n return (\n \u003cdiv>\n \u003cLink to=\"/users\">\n Users\n \u003cMatchRoute to=\"/users\" pending>\n {(match) => {\n return \u003cSpinner show={match} />\n }}\n \u003c/MatchRoute>\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\nThe hook version `useMatchRoute` returns a function that can be called programmatically to check if a route is matched:\n\n```tsx\nfunction Component() {\n const matchRoute = useMatchRoute()\n\n useEffect(() => {\n if (matchRoute({ to: '/users', pending: true })) {\n console.info('The /users route is matched and pending')\n }\n })\n\n return (\n \u003cdiv>\n \u003cLink to=\"/users\">Users\u003c/Link>\n \u003c/div>\n )\n}\n```\n\n---\n\nPhew! That's a lot of navigating! That said, hopefully you're feeling pretty good about getting around your application now. Let's move on!\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":29831,"content_sha256":"ad929b80abc3b06b24c4580bad1dc0738a5c3ad6a89d7e7d3e04f84e4c337aba"},{"filename":"references/docs/router/guide/not-found-errors.md","content":"---\ntitle: Not Found Errors\n---\n\n> This page covers the newer `notFound` function and `notFoundComponent` API for handling not found errors. The `NotFoundRoute` route is deprecated and will be removed in a future release. See [Migrating from `NotFoundRoute`](#migrating-from-notfoundroute) for more information.\n\n## Overview\n\nThere are 2 uses for not-found errors in TanStack Router:\n\n- **Non-matching route paths**: When a path does not match any known route matching pattern **OR** when it partially matches a route, but with extra path segments\n - The **router** will automatically throw a not-found error when a path does not match any known route matching pattern\n - If the router's `notFoundMode` is set to `fuzzy`, the nearest parent route with a `notFoundComponent` will handle the error. If the router's `notFoundMode` is set to `root`, the root route will handle the error.\n - Examples:\n - Attempting to access `/users` when there is no `/users` route\n - Attempting to access `/posts/1/edit` when the route tree only handles `/posts/$postId`\n- **Missing resources**: When a resource cannot be found, such as a post with a given ID or any asynchronous data that is not available or does not exist\n - **You, the developer** must throw a not-found error when a resource cannot be found. This can be done in the `beforeLoad` or `loader` functions using the `notFound` utility.\n - Will be handled by the nearest parent route with a `notFoundComponent` (when `notFound` is called within `loader`) or the root route.\n - Examples:\n - Attempting to access `/posts/1` when the post with ID 1 does not exist\n - Attempting to access `/docs/path/to/document` when the document does not exist\n\nUnder the hood, both of these cases are implemented using the same `notFound` function and `notFoundComponent` API.\n\n## The `notFoundMode` option\n\nWhen TanStack Router encounters a **pathname** that doesn't match any known route pattern **OR** partially matches a route pattern but with extra trailing pathname segments, it will automatically throw a not-found error.\n\nDepending on the `notFoundMode` option, the router will handle these automatic errors differently::\n\n- [\"fuzzy\" mode](#notfoundmode-fuzzy) (default): The router will intelligently find the closest matching suitable route and display the `notFoundComponent`.\n- [\"root\" mode](#notfoundmode-root): All not-found errors will be handled by the root route's `notFoundComponent`, regardless of the nearest matching route.\n\n### `notFoundMode: 'fuzzy'`\n\nBy default, the router's `notFoundMode` is set to `fuzzy`, which indicates that if a pathname doesn't match any known route, the router will attempt to use the closest matching route with children/(an outlet) and a configured not found component.\n\n> ** Why is this the default?** Fuzzy matching to preserve as much parent layout as possible for the user gives them more context to navigate to a useful location based on where they thought they would arrive.\n\nThe nearest suitable route is found using the following criteria:\n\n- The route must have children and therefore an `Outlet` to render the `notFoundComponent`\n- The route must have a `notFoundComponent` configured or the router must have a `defaultNotFoundComponent` configured\n\nFor example, consider the following route tree:\n\n- `__root__` (has a `notFoundComponent` configured)\n - `posts` (has a `notFoundComponent` configured)\n - `$postId` (has a `notFoundComponent` configured)\n\nIf provided the path of `/posts/1/edit`, the following component structure will be rendered:\n\n- `\u003cRoot>`\n - `\u003cPosts>`\n - `\u003cPosts.notFoundComponent>`\n\nThe `notFoundComponent` of the `posts` route will be rendered because it is the **nearest suitable parent route with children (and therefore an outlet) and a `notFoundComponent` configured**.\n\n### `notFoundMode: 'root'`\n\nWhen `notFoundMode` is set to `root`, all not-found errors will be handled by the root route's `notFoundComponent` instead of bubbling up from the nearest fuzzy-matched route.\n\nFor example, consider the following route tree:\n\n- `__root__` (has a `notFoundComponent` configured)\n - `posts` (has a `notFoundComponent` configured)\n - `$postId` (has a `notFoundComponent` configured)\n\nIf provided the path of `/posts/1/edit`, the following component structure will be rendered:\n\n- `\u003cRoot>`\n - `\u003cRoot.notFoundComponent>`\n\nThe `notFoundComponent` of the `__root__` route will be rendered because the `notFoundMode` is set to `root`.\n\n## Configuring a route's `notFoundComponent`\n\nTo handle both types of not-found errors, you can attach a `notFoundComponent` to a route. This component will be rendered when a not-found error is thrown.\n\nFor example, configuring a `notFoundComponent` for a `/settings` route to handle non-existing settings pages:\n\n```tsx\nexport const Route = createFileRoute('/settings')({\n component: () => {\n return (\n \u003cdiv>\n \u003cp>Settings page\u003c/p>\n \u003cOutlet />\n \u003c/div>\n )\n },\n notFoundComponent: () => {\n return \u003cp>This setting page doesn't exist!\u003c/p>\n },\n})\n```\n\nOr configuring a `notFoundComponent` for a `/posts/$postId` route to handle posts that don't exist:\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params: { postId } }) => {\n const post = await getPost(postId)\n if (!post) throw notFound()\n return { post }\n },\n component: ({ post }) => {\n return (\n \u003cdiv>\n \u003ch1>{post.title}\u003c/h1>\n \u003cp>{post.body}\u003c/p>\n \u003c/div>\n )\n },\n notFoundComponent: () => {\n return \u003cp>Post not found!\u003c/p>\n },\n})\n```\n\n## Default Router-Wide Not Found Handling\n\nYou may want to provide a default not-found component for every route in your app with child routes.\n\n> Why only routes with children? **Leaf-node routes (routes without children) will never render an `Outlet` and therefore are not able to handle not-found errors.**\n\nTo do this, pass a `defaultNotFoundComponent` to the `createRouter` function:\n\n```tsx\nconst router = createRouter({\n defaultNotFoundComponent: () => {\n return (\n \u003cdiv>\n \u003cp>Not found!\u003c/p>\n \u003cLink to=\"/\">Go home\u003c/Link>\n \u003c/div>\n )\n },\n})\n```\n\n## Throwing your own `notFound` errors\n\nYou can manually throw not-found errors in loader methods and components using the `notFound` function. This is useful when you need to signal that a resource cannot be found.\n\nThe `notFound` function works in a similar fashion to the `redirect` function. To cause a not-found error, you can **throw a `notFound()`**.\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params: { postId } }) => {\n // Returns `null` if the post doesn't exist\n const post = await getPost(postId)\n if (!post) {\n throw notFound()\n // Alternatively, you can make the notFound function throw:\n // notFound({ throw: true })\n }\n // Post is guaranteed to be defined here because we threw an error\n return { post }\n },\n})\n```\n\nThe not-found error above will be handled by the same route or nearest parent route that has either a `notFoundComponent` route option or the `defaultNotFoundComponent` router option configured.\n\nIf neither the route nor any suitable parent route is found to handle the error, the root route will handle it using TanStack Router's **extremely basic (and purposefully undesirable)** default not-found component that simply renders `\u003cp>Not Found\u003c/p>`. It's highly recommended to either attach at least one `notFoundComponent` to the root route or configure a router-wide `defaultNotFoundComponent` to handle not-found errors.\n\n> When you throw `notFound()` in `beforeLoad`, TanStack Router resolves it the same way as other not-found errors:\n>\n> - If you pass `routeId`, that route (or the nearest valid ancestor boundary) handles it.\n> - If you don't pass `routeId`, the nearest route/ancestor with a `notFoundComponent` handles it (based on the router's mode and matching rules).\n> - If no suitable boundary is found, handling falls back to the root/default not-found behavior.\n>\n> For `beforeLoad`-thrown not-found errors, TanStack Router still runs required parent loaders so the selected not-found boundary can render with the loader data it depends on.\n\n## Specifying Which Routes Handle Not Found Errors\n\nSometimes you may want to trigger a not-found on a specific parent route and bypass the normal not-found component propagation. To do this, pass in a route id to the `route` option in the `notFound` function.\n\n```tsx\n// _pathlessLayout.tsx\nexport const Route = createFileRoute('/_pathlessLayout')({\n // This will render\n notFoundComponent: () => {\n return \u003cp>Not found (in _pathlessLayout)\u003c/p>\n },\n component: () => {\n return (\n \u003cdiv>\n \u003cp>This is a pathless layout route!\u003c/p>\n \u003cOutlet />\n \u003c/div>\n )\n },\n})\n\n// _pathlessLayout/route-a.tsx\nexport const Route = createFileRoute('/_pathless/route-a')({\n loader: async () => {\n // This will make LayoutRoute handle the not-found error\n throw notFound({ routeId: '/_pathlessLayout' })\n // ^^^^^^^^^ This will autocomplete from the registered router\n },\n // This WILL NOT render\n notFoundComponent: () => {\n return \u003cp>Not found (in _pathlessLayout/route-a)\u003c/p>\n },\n})\n```\n\n### Manually targeting the root route\n\nYou can also target the root route by passing the exported `rootRouteId` variable to the `notFound` function's `route` property:\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params: { postId } }) => {\n const post = await getPost(postId)\n if (!post) throw notFound({ routeId: rootRouteId })\n return { post }\n },\n})\n```\n\n### Throwing Not Found Errors in Components\n\nYou can also throw not-found errors in components. However, **it is recommended to throw not-found errors in loader methods instead of components in order to correctly type loader data and prevent flickering.**\n\nTanStack Router exposes a `CatchNotFound` component similar to `CatchBoundary` that can be used to catch not-found errors in components and display UI accordingly.\n\n### Data Loading Inside `notFoundComponent`\n\n`notFoundComponent` is a special case when it comes to data loading. **`SomeRoute.useLoaderData` may not be defined depending on which route you are trying to access and where the not-found error gets thrown**. However, `Route.useParams`, `Route.useSearch`, `Route.useRouteContext`, etc. will return a defined value.\n\n**If you need to pass incomplete loader data to `notFoundComponent`,** pass the data via the `data` option in the `notFound` function and validate it in `notFoundComponent`.\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params: { postId } }) => {\n const post = await getPost(postId)\n if (!post)\n throw notFound({\n // Forward some data to the notFoundComponent\n // data: someIncompleteLoaderData\n })\n return { post }\n },\n // `data: unknown` is passed to the component via the `data` option when calling `notFound`\n notFoundComponent: ({ data }) => {\n // ❌ useLoaderData is not valid here: const { post } = Route.useLoaderData()\n\n // ✅:\n const { postId } = Route.useParams()\n const search = Route.useSearch()\n const context = Route.useRouteContext()\n\n return \u003cp>Post with id {postId} not found!\u003c/p>\n },\n})\n```\n\n## Usage With SSR\n\nSee [SSR guide](./ssr.md) for more information.\n\n## Migrating from `NotFoundRoute`\n\nThe `NotFoundRoute` API is deprecated in favor of `notFoundComponent`. The `NotFoundRoute` API will be removed in a future release.\n\n**The `notFound` function and `notFoundComponent` will not work when using `NotFoundRoute`.**\n\nThe main differences are:\n\n- `NotFoundRoute` is a route that requires an `\u003cOutlet>` on its parent route to render. `notFoundComponent` is a component that can be attached to any route.\n- When using `NotFoundRoute`, you can't use layouts. `notFoundComponent` can be used with layouts.\n- When using `notFoundComponent`, path matching is strict. This means that if you have a route at `/post/$postId`, a not-found error will be thrown if you try to access `/post/1/2/3`. With `NotFoundRoute`, `/post/1/2/3` would match the `NotFoundRoute` and only render it if there is an `\u003cOutlet>`.\n\nTo migrate from `NotFoundRoute` to `notFoundComponent`, you'll just need to make a few changes:\n\n```tsx title='src/router.tsx'\nimport { createRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen.'\n- import { notFoundRoute } from './notFoundRoute' // [!code --]\n\nexport const router = createRouter({\n routeTree,\n- notFoundRoute // [!code --]\n})\n\n// routes/__root.tsx\nimport { createRootRoute } from '@tanstack/react-router'\n\nexport const Route = createRootRoute({\n // ...\n+ notFoundComponent: () => { // [!code ++]\n+ return \u003cp>Not found!\u003c/p> // [!code ++]\n+ } // [!code ++]\n})\n```\n\nImportant changes:\n\n- A `notFoundComponent` is added to the root route for global not-found handling.\n - You can also add a `notFoundComponent` to any other route in your route tree to handle not-found errors for that specific route.\n- The `notFoundComponent` does not support rendering an `\u003cOutlet>`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13203,"content_sha256":"66bf2169f2284386322e5025103e58873f0fcf7c57bc5536073188b7828364dc"},{"filename":"references/docs/router/guide/outlets.md","content":"---\ntitle: Outlets\n---\n\nNested routing means that routes can be nested within other routes, including the way they render. So how do we tell our routes where to render this nested content?\n\n## The `Outlet` Component\n\nThe `Outlet` component is used to render the next potentially matching child route. `\u003cOutlet />` doesn't take any props and can be rendered anywhere within a route's component tree. If there is no matching child route, `\u003cOutlet />` will render `null`.\n\n> [!TIP]\n> If a route's `component` is left undefined, it will render an `\u003cOutlet />` automatically.\n\nA great example is configuring the root route of your application. Let's give our root route a component that renders a title, then an `\u003cOutlet />` for our top-level routes to render.\n\n\n\n# React\n\n```tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \u003cdiv>\n \u003ch1>My App\u003c/h1>\n \u003cOutlet /> {/* This is where child routes will render */}\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx\nimport { createRootRoute, Outlet } from '@tanstack/solid-router'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \u003cdiv>\n \u003ch1>My App\u003c/h1>\n \u003cOutlet /> {/* This is where child routes will render */}\n \u003c/div>\n )\n}\n```\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1378,"content_sha256":"a1bcc43372e6654799e7f86181fcfe40b3a0e1a9d12852a3d8ca1bc46876b26b"},{"filename":"references/docs/router/guide/parallel-routes.md","content":"---\ntitle: Parallel Routes\n---\n\nWe haven't covered this yet. Stay tuned!\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":73,"content_sha256":"6562e421b0f3cfa9f58fe9d6422f38a1368fdd9f31f88ef2a5d136bab6b49e97"},{"filename":"references/docs/router/guide/path-params.md","content":"---\ntitle: Path Params\n---\n\nPath params are used to match a single segment (the text until the next `/`) and provide its value back to you as a **named** variable. They are defined by using the ` tanstack-vue-router-skilld — Skillopedia character prefix in the path, followed by the key variable to assign it to. The following are valid path param paths:\n\n- `$postId`\n- `$name`\n- `$teamId`\n- `about/$name`\n- `team/$teamId`\n- `blog/$postId`\n\nBecause path param routes only match to the next `/`, child routes can be created to continue expressing hierarchy:\n\nLet's create a post route file that uses a path param to match the post ID:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.$postId.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params }) => {\n return fetchPost(params.postId)\n },\n})\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.$postId.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params }) => {\n return fetchPost(params.postId)\n },\n})\n```\n\n\n\n## Path Params can be used by child routes\n\nOnce a path param has been parsed, it is available to all child routes. This means that if we define a child route to our `postRoute`, we can use the `postId` variable from the URL in the child route's path!\n\n## Path Params in Loaders\n\nPath params are passed to the loader as a `params` object. The keys of this object are the names of the path params, and the values are the values that were parsed out of the actual URL path. For example, if we were to visit the `/blog/123` URL, the `params` object would be `{ postId: '123' }`:\n\n```tsx title=\"src/routes/posts.$postId.tsx\"\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params }) => {\n return fetchPost(params.postId)\n },\n})\n```\n\nThe `params` object is also passed to the `beforeLoad` option:\n\n```tsx title=\"src/routes/posts.$postId.tsx\"\nexport const Route = createFileRoute('/posts/$postId')({\n beforeLoad: async ({ params }) => {\n // do something with params.postId\n },\n})\n```\n\n## Path Params in Components\n\nIf we add a component to our `postRoute`, we can access the `postId` variable from the URL by using the route's `useParams` hook:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.$postId.tsx\"\nexport const Route = createFileRoute('/posts/$postId')({\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const { postId } = Route.useParams()\n return \u003cdiv>Post {postId}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.$postId.tsx\"\nexport const Route = createFileRoute('/posts/$postId')({\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const params = Route.useParams()\n return \u003cdiv>Post {params().postId}\u003c/div>\n}\n```\n\n\n\n> Quick tip: If your component is code-split, you can use the [getRouteApi function](./code-splitting.md#manually-accessing-route-apis-in-other-files-with-the-getrouteapi-helper) to avoid having to import the `Route` configuration to get access to the typed `useParams()` hook.\n\n## Path Params outside of Routes\n\nYou can also use the globally exported `useParams` hook to access any parsed path params from any component in your app. You'll need to pass the `strict: false` option to `useParams`, denoting that you want to access the params from an ambiguous location:\n\n\n\n# React\n\n```tsx title=\"src/components/PostComponent.tsx\"\nfunction PostComponent() {\n const { postId } = useParams({ strict: false })\n return \u003cdiv>Post {postId}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/components/PostComponent.tsx\"\nfunction PostComponent() {\n const params = useParams({ strict: false })\n return \u003cdiv>Post {params().postId}\u003c/div>\n}\n```\n\n\n\n## Navigating with Path Params\n\nWhen navigating to a route with path params, TypeScript will require you to pass the params either as an object or as a function that returns an object of params.\n\nLet's see what an object style looks like:\n\n```tsx\nfunction Component() {\n return (\n \u003cLink to=\"/blog/$postId\" params={{ postId: '123' }}>\n Post 123\n \u003c/Link>\n )\n}\n```\n\nAnd here's what a function style looks like:\n\n```tsx\nfunction Component() {\n return (\n \u003cLink to=\"/blog/$postId\" params={(prev) => ({ ...prev, postId: '123' })}>\n Post 123\n \u003c/Link>\n )\n}\n```\n\nNotice that the function style is useful when you need to persist params that are already in the URL for other routes. This is because the function style will receive the current params as an argument, allowing you to modify them as needed and return the final params object.\n\n## Prefixes and Suffixes for Path Params\n\nYou can also use **prefixes** and **suffixes** with path params to create more complex routing patterns. This allows you to match specific URL structures while still capturing the dynamic segments.\n\nWhen using either prefixes or suffixes, you can define them by wrapping the path param in curly braces `{}` and placing the prefix or suffix before or after the variable name.\n\n### Defining Prefixes\n\nPrefixes are defined by placing the prefix text outside the curly braces before the variable name. For example, if you want to match a URL that starts with `post-` followed by a post ID, you can define it like this:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts/post-{$postId}.tsx\"\nexport const Route = createFileRoute('/posts/post-{$postId}')({\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const { postId } = Route.useParams()\n // postId will be the value after 'post-'\n return \u003cdiv>Post ID: {postId}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts/post-{$postId}.tsx\"\nexport const Route = createFileRoute('/posts/post-{$postId}')({\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const params = Route.useParams()\n // postId will be the value after 'post-'\n return \u003cdiv>Post ID: {params().postId}\u003c/div>\n}\n```\n\n\n\nYou can even combines prefixes with wildcard routes to create more complex patterns:\n\n\n\n# React\n\n```tsx title=\"src/routes/on-disk/storage-{$postId}/$.tsx\"\nexport const Route = createFileRoute('/on-disk/storage-{$postId}/

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

)({\n component: StorageComponent,\n})\n\nfunction StorageComponent() {\n const { _splat } = Route.useParams()\n // _splat, will be value after 'storage-'\n // i.e. my-drive/documents/foo.txt\n return \u003cdiv>Storage Location: /{_splat}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/on-disk/storage-{$postId}/$.tsx\"\nexport const Route = createFileRoute('/on-disk/storage-{$postId}/

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

)({\n component: StorageComponent,\n})\n\nfunction StorageComponent() {\n const params = Route.useParams()\n // _splat, will be value after 'storage-'\n // i.e. my-drive/documents/foo.txt\n return \u003cdiv>Storage Location: /{params()._splat}\u003c/div>\n}\n```\n\n\n\n### Defining Suffixes\n\nSuffixes are defined by placing the suffix text outside the curly braces after the variable name. For example, if you want to match a URL a filename that ends with `txt`, you can define it like this:\n\n\n\n# React\n\n```tsx title=\"src/routes/files/{$fileName}[.]txt.tsx\"\nexport const Route = createFileRoute('/files/{$fileName}.txt')({\n component: FileComponent,\n})\n\nfunction FileComponent() {\n const { fileName } = Route.useParams()\n // fileName will be the value before 'txt'\n return \u003cdiv>File Name: {fileName}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/files/{$fileName}[.]txt.tsx\"\nexport const Route = createFileRoute('/files/{$fileName}.txt')({\n component: FileComponent,\n})\n\nfunction FileComponent() {\n const params = Route.useParams()\n // fileName will be the value before 'txt'\n return \u003cdiv>File Name: {params().fileName}\u003c/div>\n}\n```\n\n\n\nYou can also combine suffixes with wildcards for more complex routing patterns:\n\n\n\n# React\n\n```tsx title=\"src/routes/files/{$}[.]txt.tsx\"\nexport const Route = createFileRoute('/files/{$}.txt')({\n component: FileComponent,\n})\n\nfunction FileComponent() {\n const { _splat } = Route.useParams()\n // _splat will be the value before '.txt'\n return \u003cdiv>File Splat: {_splat}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/files/{$}[.]txt.tsx\"\nexport const Route = createFileRoute('/files/{$}.txt')({\n component: FileComponent,\n})\n\nfunction FileComponent() {\n const params = Route.useParams()\n // _splat will be the value before '.txt'\n return \u003cdiv>File Splat: {params()._splat}\u003c/div>\n}\n```\n\n\n\n### Combining Prefixes and Suffixes\n\nYou can combine both prefixes and suffixes to create very specific routing patterns. For example, if you want to match a URL that starts with `user-` and ends with `.json`, you can define it like this:\n\n\n\n# React\n\n```tsx title=\"src/routes/users/user-{$userId}.json\"\nexport const Route = createFileRoute('/users/user-{$userId}.json')({\n component: UserComponent,\n})\n\nfunction UserComponent() {\n const { userId } = Route.useParams()\n // userId will be the value between 'user-' and '.json'\n return \u003cdiv>User ID: {userId}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/users/user-{$userId}.json\"\nexport const Route = createFileRoute('/users/user-{$userId}.json')({\n component: UserComponent,\n})\n\nfunction UserComponent() {\n const params = Route.useParams()\n // userId will be the value between 'user-' and '.json'\n return \u003cdiv>User ID: {params().userId}\u003c/div>\n}\n```\n\n\n\nSimilar to the previous examples, you can also use wildcards with prefixes and suffixes. Go wild!\n\n## Optional Path Parameters\n\nOptional path parameters allow you to define route segments that may or may not be present in the URL. They use the `{-$paramName}` syntax and provide flexible routing patterns where certain parameters are optional.\n\n### Defining Optional Parameters\n\nOptional path parameters are defined using curly braces with a dash prefix: `{-$paramName}`\n\n```tsx\n// Single optional parameter\n// src/routes/posts/{-$category}.tsx\nexport const Route = createFileRoute('/posts/{-$category}')({\n component: PostsComponent,\n})\n\n// Multiple optional parameters\n// src/routes/posts/{-$category}/{-$slug}.tsx\nexport const Route = createFileRoute('/posts/{-$category}/{-$slug}')({\n component: PostComponent,\n})\n\n// Mixed required and optional parameters\n// src/routes/users/$id/{-$tab}.tsx\nexport const Route = createFileRoute('/users/$id/{-$tab}')({\n component: UserComponent,\n})\n```\n\n### How Optional Parameters Work\n\nOptional parameters create flexible URL patterns:\n\n- `/posts/{-$category}` matches both `/posts` and `/posts/tech`\n- `/posts/{-$category}/{-$slug}` matches `/posts`, `/posts/tech`, and `/posts/tech/hello-world`\n- `/users/$id/{-$tab}` matches `/users/123` and `/users/123/settings`\n\nWhen an optional parameter is not present in the URL, its value will be `undefined` in your route handlers and components.\n\n### Accessing Optional Parameters\n\nOptional parameters work exactly like regular parameters in your components, but their values may be `undefined`:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts/{-$category}.tsx\"\nfunction PostsComponent() {\n const { category } = Route.useParams()\n\n return \u003cdiv>{category ? `Posts in ${category}` : 'All Posts'}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts/{-$category}.tsx\"\nfunction PostsComponent() {\n const params = Route.useParams()\n\n return (\n \u003cdiv>\n {params().category ? `Posts in ${params().category}` : 'All Posts'}\n \u003c/div>\n )\n}\n```\n\n\n\n### Optional Parameters in Loaders\n\nOptional parameters are available in loaders and may be `undefined`:\n\n```tsx\nexport const Route = createFileRoute('/posts/{-$category}')({\n loader: async ({ params }) => {\n // params.category might be undefined\n return fetchPosts({ category: params.category })\n },\n})\n```\n\n### Optional Parameters in beforeLoad\n\nOptional parameters work in `beforeLoad` handlers as well:\n\n```tsx\nexport const Route = createFileRoute('/posts/{-$category}')({\n beforeLoad: async ({ params }) => {\n if (params.category) {\n // Validate category exists\n await validateCategory(params.category)\n }\n },\n})\n```\n\n### Advanced Optional Parameter Patterns\n\n#### With Prefix and Suffix\n\nOptional parameters support prefix and suffix patterns:\n\n\n\n# React\n\n```tsx title=\"src/routes/files/prefix{-$name}.txt\"\n// Route: /files/prefix{-$name}.txt\n// Matches: /files/prefix.txt and /files/prefixdocument.txt\nexport const Route = createFileRoute('/files/prefix{-$name}.txt')({\n component: FileComponent,\n})\n\nfunction FileComponent() {\n const { name } = Route.useParams()\n return \u003cdiv>File: {name || 'default'}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/files/prefix{-$name}.txt\"\n// Route: /files/prefix{-$name}.txt\n// Matches: /files/prefix.txt and /files/prefixdocument.txt\nexport const Route = createFileRoute('/files/prefix{-$name}.txt')({\n component: FileComponent,\n})\n\nfunction FileComponent() {\n const params = Route.useParams()\n return \u003cdiv>File: {params().name || 'default'}\u003c/div>\n}\n```\n\n\n\n#### All Optional Parameters\n\nYou can create routes where all parameters are optional:\n\n\n\n# React\n\n```tsx title=\"src/routes/{-$year}/{-$month}/{-$day}.tsx\"\n// Route: /{-$year}/{-$month}/{-$day}\n// Matches: /, /2023, /2023/12, /2023/12/25\nexport const Route = createFileRoute('/{-$year}/{-$month}/{-$day}')({\n component: DateComponent,\n})\n\nfunction DateComponent() {\n const { year, month, day } = Route.useParams()\n\n if (!year) return \u003cdiv>Select a year\u003c/div>\n if (!month) return \u003cdiv>Year: {year}\u003c/div>\n if (!day)\n return (\n \u003cdiv>\n Month: {year}/{month}\n \u003c/div>\n )\n\n return (\n \u003cdiv>\n Date: {year}/{month}/{day}\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/{-$year}/{-$month}/{-$day}.tsx\"\n// Route: /{-$year}/{-$month}/{-$day}\n// Matches: /, /2023, /2023/12, /2023/12/25\nexport const Route = createFileRoute('/{-$year}/{-$month}/{-$day}')({\n component: DateComponent,\n})\n\nfunction DateComponent() {\n const params = Route.useParams()\n\n if (!params().year) return \u003cdiv>Select a year\u003c/div>\n if (!params().month) return \u003cdiv>Year: {params().year}\u003c/div>\n if (!params().day)\n return (\n \u003cdiv>\n Month: {params().year}/{params().month}\n \u003c/div>\n )\n\n return (\n \u003cdiv>\n Date: {params().year}/{params().month}/{params().day}\n \u003c/div>\n )\n}\n```\n\n\n\n#### Optional Parameters with Wildcards\n\nOptional parameters can be combined with wildcards for complex routing patterns:\n\n\n\n# React\n\n```tsx title=\"src/routes/docs/{-$version}/$.tsx\"\n// Route: /docs/{-$version}/$\n// Matches: /docs/extra/path, /docs/v2/extra/path\nexport const Route = createFileRoute('/docs/{-$version}/

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

)({\n component: DocsComponent,\n})\n\nfunction DocsComponent() {\n const { version } = Route.useParams()\n const { _splat } = Route.useParams()\n\n return (\n \u003cdiv>\n Version: {version || 'latest'}\n Path: {_splat}\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/docs/{-$version}/$.tsx\"\n// Route: /docs/{-$version}/$\n// Matches: /docs/extra/path, /docs/v2/extra/path\nexport const Route = createFileRoute('/docs/{-$version}/

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

)({\n component: DocsComponent,\n})\n\nfunction DocsComponent() {\n const params = Route.useParams()\n\n return (\n \u003cdiv>\n Version: {params().version || 'latest'}\n Path: {params()._splat}\n \u003c/div>\n )\n}\n```\n\n\n\n### Navigating with Optional Parameters\n\nWhen navigating to routes with optional parameters, you have fine-grained control over which parameters to include:\n\n```tsx\nfunction Navigation() {\n return (\n \u003cdiv>\n {/* Navigate with optional parameter */}\n \u003cLink to=\"/posts/{-$category}\" params={{ category: 'tech' }}>\n Tech Posts\n \u003c/Link>\n\n {/* Navigate without optional parameter */}\n \u003cLink to=\"/posts/{-$category}\" params={{ category: undefined }}>\n All Posts\n \u003c/Link>\n\n {/* Navigate with multiple optional parameters */}\n \u003cLink\n to=\"/posts/{-$category}/{-$slug}\"\n params={{ category: 'tech', slug: 'react-tips' }}\n >\n Specific Post\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\n### Type Safety with Optional Parameters\n\nTypeScript provides full type safety for optional parameters:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts/{-$category}.tsx\"\nfunction PostsComponent() {\n // TypeScript knows category might be undefined\n const { category } = Route.useParams() // category: string | undefined\n\n // Safe navigation\n const categoryUpper = category?.toUpperCase()\n\n return \u003cdiv>{categoryUpper || 'All Categories'}\u003c/div>\n}\n\n// Navigation is type-safe and flexible\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: 'tech' }} // ✅ Valid - string\n>\n Tech Posts\n\u003c/Link>\n\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: 123 }} // ✅ Valid - number (auto-stringified)\n>\n Category 123\n\u003c/Link>\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts/{-$category}.tsx\"\nfunction PostsComponent() {\n // TypeScript knows category might be undefined\n const params = Route.useParams() // category: string | undefined\n\n // Safe navigation\n const categoryUpper = params().category?.toUpperCase()\n\n return \u003cdiv>{categoryUpper || 'All Categories'}\u003c/div>\n}\n\n// Navigation is type-safe and flexible\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: 'tech' }} // ✅ Valid - string\n>\n Tech Posts\n\u003c/Link>\n\n\u003cLink\n to=\"/posts/{-$category}\"\n params={{ category: 123 }} // ✅ Valid - number (auto-stringified)\n>\n Category 123\n\u003c/Link>\n```\n\n\n\n## Internationalization (i18n) with Optional Path Parameters\n\nOptional path parameters are excellent for implementing internationalization (i18n) routing patterns. You can use prefix patterns to handle multiple languages while maintaining clean, SEO-friendly URLs.\n\n### Prefix-based i18n\n\nUse optional language prefixes to support URLs like `/en/about`, `/fr/about`, or just `/about` (default language):\n\n\n\n# React\n\n```tsx title=\"src/routes/{-$locale}/about.tsx\"\n// Route: /{-$locale}/about\nexport const Route = createFileRoute('/{-$locale}/about')({\n component: AboutComponent,\n})\n\nfunction AboutComponent() {\n const { locale } = Route.useParams()\n const currentLocale = locale || 'en' // Default to English\n\n const content = {\n en: { title: 'About Us', description: 'Learn more about our company.' },\n fr: {\n title: 'À Propos',\n description: 'En savoir plus sur notre entreprise.',\n },\n es: {\n title: 'Acerca de',\n description: 'Conoce más sobre nuestra empresa.',\n },\n }\n\n return (\n \u003cdiv>\n \u003ch1>{content[currentLocale]?.title}\u003c/h1>\n \u003cp>{content[currentLocale]?.description}\u003c/p>\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/{-$locale}/about.tsx\"\n// Route: /{-$locale}/about\nexport const Route = createFileRoute('/{-$locale}/about')({\n component: AboutComponent,\n})\n\nfunction AboutComponent() {\n const params = Route.useParams()\n const currentLocale = params().locale || 'en' // Default to English\n\n const content = {\n en: { title: 'About Us', description: 'Learn more about our company.' },\n fr: {\n title: 'À Propos',\n description: 'En savoir plus sur notre entreprise.',\n },\n es: {\n title: 'Acerca de',\n description: 'Conoce más sobre nuestra empresa.',\n },\n }\n\n return (\n \u003cdiv>\n \u003ch1>{content[currentLocale]?.title}\u003c/h1>\n \u003cp>{content[currentLocale]?.description}\u003c/p>\n \u003c/div>\n )\n}\n```\n\n\n\nThis pattern matches:\n\n- `/about` (default locale)\n- `/en/about` (explicit English)\n- `/fr/about` (French)\n- `/es/about` (Spanish)\n\n### Complex i18n Patterns\n\nCombine optional parameters for more sophisticated i18n routing:\n\n\n\n# React\n\n```tsx title=\"src/routes/{-$locale}/blog/{-$category}/$slug.tsx\"\n// Route: /{-$locale}/blog/{-$category}/$slug\nexport const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({\n beforeLoad: async ({ params }) => {\n const locale = params.locale || 'en'\n const category = params.category\n\n // Validate locale and category\n const validLocales = ['en', 'fr', 'es', 'de']\n if (locale && !validLocales.includes(locale)) {\n throw new Error('Invalid locale')\n }\n\n return { locale, category }\n },\n loader: async ({ params, context }) => {\n const { locale } = context\n const { slug, category } = params\n\n return fetchBlogPost({ slug, category, locale })\n },\n component: BlogPostComponent,\n})\n\nfunction BlogPostComponent() {\n const { locale, category, slug } = Route.useParams()\n const data = Route.useLoaderData()\n\n return (\n \u003carticle>\n \u003ch1>{data.title}\u003c/h1>\n \u003cp>\n Category: {category || 'All'} | Language: {locale || 'en'}\n \u003c/p>\n \u003cdiv>{data.content}\u003c/div>\n \u003c/article>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/{-$locale}/blog/{-$category}/$slug.tsx\"\n// Route: /{-$locale}/blog/{-$category}/$slug\nexport const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({\n beforeLoad: async ({ params }) => {\n const locale = params.locale || 'en'\n const category = params.category\n\n // Validate locale and category\n const validLocales = ['en', 'fr', 'es', 'de']\n if (locale && !validLocales.includes(locale)) {\n throw new Error('Invalid locale')\n }\n\n return { locale, category }\n },\n loader: async ({ params, context }) => {\n const { locale } = context\n const { slug, category } = params\n\n return fetchBlogPost({ slug, category, locale })\n },\n component: BlogPostComponent,\n})\n\nfunction BlogPostComponent() {\n const params = Route.useParams()\n const data = Route.useLoaderData()\n\n return (\n \u003carticle>\n \u003ch1>{data.title}\u003c/h1>\n \u003cp>\n Category: {params().category || 'All'} | Language:{' '}\n {params().locale || 'en'}\n \u003c/p>\n \u003cdiv>{data.content}\u003c/div>\n \u003c/article>\n )\n}\n```\n\n\n\nThis supports URLs like:\n\n- `/blog/tech/my-post` (default locale, tech category)\n- `/fr/blog/my-post` (French, no category)\n- `/en/blog/tech/my-post` (explicit English, tech category)\n- `/es/blog/tecnologia/mi-post` (Spanish, Spanish category)\n\n### Language Navigation\n\nCreate language switchers using optional i18n parameters with function-style params:\n\n\n\n# React\n\n```tsx title=\"src/components/LanguageSwitcher.tsx\"\nfunction LanguageSwitcher() {\n const currentParams = useParams({ strict: false })\n\n const languages = [\n { code: 'en', name: 'English' },\n { code: 'fr', name: 'Français' },\n { code: 'es', name: 'Español' },\n ]\n\n return (\n \u003cdiv className=\"language-switcher\">\n {languages.map(({ code, name }) => (\n \u003cLink\n key={code}\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={(prev) => ({\n ...prev,\n locale: code === 'en' ? undefined : code, // Remove 'en' for clean URLs\n })}\n className={currentParams.locale === code ? 'active' : ''}\n >\n {name}\n \u003c/Link>\n ))}\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/components/LanguageSwitcher.tsx\"\nfunction LanguageSwitcher() {\n const currentParams = useParams({ strict: false })\n\n const languages = [\n { code: 'en', name: 'English' },\n { code: 'fr', name: 'Français' },\n { code: 'es', name: 'Español' },\n ]\n\n return (\n \u003cdiv class=\"language-switcher\">\n {languages.map(({ code, name }) => (\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={(prev) => ({\n ...prev,\n locale: code === 'en' ? undefined : code, // Remove 'en' for clean URLs\n })}\n class={currentParams().locale === code ? 'active' : ''}\n >\n {name}\n \u003c/Link>\n ))}\n \u003c/div>\n )\n}\n```\n\n\n\nYou can also create more sophisticated language switching logic:\n\n\n\n# React\n\n```tsx\nfunction AdvancedLanguageSwitcher() {\n const currentParams = useParams({ strict: false })\n\n const handleLanguageChange = (newLocale: string) => {\n return (prev: any) => {\n // Preserve all existing params but update locale\n const updatedParams = { ...prev }\n\n if (newLocale === 'en') {\n // Remove locale for clean English URLs\n delete updatedParams.locale\n } else {\n updatedParams.locale = newLocale\n }\n\n return updatedParams\n }\n }\n\n return (\n \u003cdiv className=\"language-switcher\">\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={handleLanguageChange('fr')}\n >\n Français\n \u003c/Link>\n\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={handleLanguageChange('es')}\n >\n Español\n \u003c/Link>\n\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={handleLanguageChange('en')}\n >\n English\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx\nfunction AdvancedLanguageSwitcher() {\n const currentParams = useParams({ strict: false })\n\n const handleLanguageChange = (newLocale: string) => {\n return (prev: any) => {\n // Preserve all existing params but update locale\n const updatedParams = { ...prev }\n\n if (newLocale === 'en') {\n // Remove locale for clean English URLs\n delete updatedParams.locale\n } else {\n updatedParams.locale = newLocale\n }\n\n return updatedParams\n }\n }\n\n return (\n \u003cdiv class=\"language-switcher\">\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={handleLanguageChange('fr')}\n >\n Français\n \u003c/Link>\n\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={handleLanguageChange('es')}\n >\n Español\n \u003c/Link>\n\n \u003cLink\n to=\"/{-$locale}/blog/{-$category}/$slug\"\n params={handleLanguageChange('en')}\n >\n English\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\n\n\n### Advanced i18n with Optional Parameters\n\nOrganize i18n routes using optional parameters for flexible locale handling:\n\n\n\n# React\n\n```tsx\n// Route structure:\n// routes/\n// {-$locale}/\n// index.tsx // /, /en, /fr\n// about.tsx // /about, /en/about, /fr/about\n// blog/\n// index.tsx // /blog, /en/blog, /fr/blog\n// $slug.tsx // /blog/post, /en/blog/post, /fr/blog/post\n\n// routes/{-$locale}/index.tsx\nexport const Route = createFileRoute('/{-$locale}/')({\n component: HomeComponent,\n})\n\nfunction HomeComponent() {\n const { locale } = Route.useParams()\n const isRTL = ['ar', 'he', 'fa'].includes(locale || '')\n\n return (\n \u003cdiv dir={isRTL ? 'rtl' : 'ltr'}>\n \u003ch1>Welcome ({locale || 'en'})\u003c/h1>\n {/* Localized content */}\n \u003c/div>\n )\n}\n\n// routes/{-$locale}/about.tsx\nexport const Route = createFileRoute('/{-$locale}/about')({\n component: AboutComponent,\n})\n```\n\n# Solid\n\n```tsx\n// Route structure:\n// routes/\n// {-$locale}/\n// index.tsx // /, /en, /fr\n// about.tsx // /about, /en/about, /fr/about\n// blog/\n// index.tsx // /blog, /en/blog, /fr/blog\n// $slug.tsx // /blog/post, /en/blog/post, /fr/blog/post\n\n// routes/{-$locale}/index.tsx\nexport const Route = createFileRoute('/{-$locale}/')({\n component: HomeComponent,\n})\n\nfunction HomeComponent() {\n const params = Route.useParams()\n const isRTL = ['ar', 'he', 'fa'].includes(params().locale || '')\n\n return (\n \u003cdiv dir={isRTL ? 'rtl' : 'ltr'}>\n \u003ch1>Welcome ({params().locale || 'en'})\u003c/h1>\n {/* Localized content */}\n \u003c/div>\n )\n}\n\n// routes/{-$locale}/about.tsx\nexport const Route = createFileRoute('/{-$locale}/about')({\n component: AboutComponent,\n})\n```\n\n\n\n### SEO and Canonical URLs\n\nHandle SEO for i18n routes properly:\n\n\n\n# React\n\n```tsx title=\"src/routes/{-$locale}/products/$id.tsx\"\nexport const Route = createFileRoute('/{-$locale}/products/$id')({\n component: ProductComponent,\n head: ({ params, loaderData }) => {\n const locale = params.locale || 'en'\n const product = loaderData\n\n return {\n title: product.title[locale] || product.title.en,\n meta: [\n {\n name: 'description',\n content: product.description[locale] || product.description.en,\n },\n {\n property: 'og:locale',\n content: locale,\n },\n ],\n links: [\n // Canonical URL (always use default locale format)\n {\n rel: 'canonical',\n href: `https://example.com/products/${params.id}`,\n },\n // Alternate language versions\n {\n rel: 'alternate',\n hreflang: 'en',\n href: `https://example.com/products/${params.id}`,\n },\n {\n rel: 'alternate',\n hreflang: 'fr',\n href: `https://example.com/fr/products/${params.id}`,\n },\n {\n rel: 'alternate',\n hreflang: 'es',\n href: `https://example.com/es/products/${params.id}`,\n },\n ],\n }\n },\n})\n```\n\n# Solid\n\n```tsx title=\"src/routes/{-$locale}/products/$id.tsx\"\nexport const Route = createFileRoute('/{-$locale}/products/$id')({\n component: ProductComponent,\n head: ({ params, loaderData }) => {\n const locale = params.locale || 'en'\n const product = loaderData\n\n return {\n title: product.title[locale] || product.title.en,\n meta: [\n {\n name: 'description',\n content: product.description[locale] || product.description.en,\n },\n {\n property: 'og:locale',\n content: locale,\n },\n ],\n links: [\n // Canonical URL (always use default locale format)\n {\n rel: 'canonical',\n href: `https://example.com/products/${params.id}`,\n },\n // Alternate language versions\n {\n rel: 'alternate',\n hreflang: 'en',\n href: `https://example.com/products/${params.id}`,\n },\n {\n rel: 'alternate',\n hreflang: 'fr',\n href: `https://example.com/fr/products/${params.id}`,\n },\n {\n rel: 'alternate',\n hreflang: 'es',\n href: `https://example.com/es/products/${params.id}`,\n },\n ],\n }\n },\n})\n```\n\n\n\n### Type Safety for i18n\n\nEnsure type safety for your i18n implementations:\n\n\n\n# React\n\n```tsx title=\"src/routes/{-$locale}/shop/{-$category}.tsx\"\n// Define supported locales\ntype Locale = 'en' | 'fr' | 'es' | 'de'\n\n// Type-safe locale validation\nfunction validateLocale(locale: string | undefined): locale is Locale {\n return ['en', 'fr', 'es', 'de'].includes(locale as Locale)\n}\n\nexport const Route = createFileRoute('/{-$locale}/shop/{-$category}')({\n beforeLoad: async ({ params }) => {\n const { locale } = params\n\n // Type-safe locale validation\n if (locale && !validateLocale(locale)) {\n throw redirect({\n to: '/shop/{-$category}',\n params: { category: params.category },\n })\n }\n\n return {\n locale: (locale as Locale) || 'en',\n isDefaultLocale: !locale || locale === 'en',\n }\n },\n component: ShopComponent,\n})\n\nfunction ShopComponent() {\n const { locale, category } = Route.useParams()\n const { isDefaultLocale } = Route.useRouteContext()\n\n // TypeScript knows locale is Locale | undefined\n // and we have validated it in beforeLoad\n\n return (\n \u003cdiv>\n \u003ch1>Shop {category ? `- ${category}` : ''}\u003c/h1>\n \u003cp>Language: {locale || 'en'}\u003c/p>\n {!isDefaultLocale && (\n \u003cLink to=\"/shop/{-$category}\" params={{ category }}>\n View in English\n \u003c/Link>\n )}\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/{-$locale}/shop/{-$category}.tsx\"\n// Define supported locales\ntype Locale = 'en' | 'fr' | 'es' | 'de'\n\n// Type-safe locale validation\nfunction validateLocale(locale: string | undefined): locale is Locale {\n return ['en', 'fr', 'es', 'de'].includes(locale as Locale)\n}\n\nexport const Route = createFileRoute('/{-$locale}/shop/{-$category}')({\n beforeLoad: async ({ params }) => {\n const { locale } = params\n\n // Type-safe locale validation\n if (locale && !validateLocale(locale)) {\n throw redirect({\n to: '/shop/{-$category}',\n params: { category: params.category },\n })\n }\n\n return {\n locale: (locale as Locale) || 'en',\n isDefaultLocale: !locale || locale === 'en',\n }\n },\n component: ShopComponent,\n})\n\nfunction ShopComponent() {\n const params = Route.useParams()\n const routeContext = Route.useRouteContext()\n\n // TypeScript knows locale is Locale | undefined\n // and we have validated it in beforeLoad\n\n return (\n \u003cdiv>\n \u003ch1>Shop {params().category ? `- ${params().category}` : ''}\u003c/h1>\n \u003cp>Language: {params().locale || 'en'}\u003c/p>\n {!routeContext().isDefaultLocale && (\n \u003cLink to=\"/shop/{-$category}\" params={{ category: params().category }}>\n View in English\n \u003c/Link>\n )}\n \u003c/div>\n )\n}\n```\n\n\n\nOptional path parameters provide a powerful and flexible foundation for implementing internationalization in your TanStack Router applications. Whether you prefer prefix-based or combined approaches, you can create clean, SEO-friendly URLs while maintaining excellent developer experience and type safety.\n\n## Allowed Characters\n\nBy default, path params are escaped with `encodeURIComponent`. If you want to allow other valid URI characters (e.g. `@` or `+`), you can specify that in your [RouterOptions](../api/router/RouterOptionsType.md#pathparamsallowedcharacters-property).\n\nExample usage:\n\n```tsx\nconst router = createRouter({\n // ...\n pathParamsAllowedCharacters: ['@'],\n})\n```\n\nThe following is the list of accepted allowed characters:\n\n- `;`\n- `:`\n- `@`\n- `&`\n- `=`\n- `+`\n- ` tanstack-vue-router-skilld — Skillopedia \n- `,`\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":33211,"content_sha256":"e4c0d6560e140b5c7de692affa95fb8c86430e7faf6e7a21ee2338c3976ac8d9"},{"filename":"references/docs/router/guide/preloading.md","content":"---\ntitle: Preloading\n---\n\nPreloading in TanStack Router is a way to load a route before the user actually navigates to it. This is useful for routes that are likely to be visited by the user next. For example, if you have a list of posts and the user is likely to click on one of them, you can preload the post route so that it's ready to go when the user clicks on it.\n\n## Supported Preloading Strategies\n\n- Intent\n - Preloading by **\"intent\"** works by using hover and touch start events on `\u003cLink>` components to preload the dependencies for the destination route.\n - This strategy is useful for preloading routes that the user is likely to visit next.\n- Viewport Visibility\n - Preloading by **\"viewport**\" works by using the Intersection Observer API to preload the dependencies for the destination route when the `\u003cLink>` component is in the viewport.\n - This strategy is useful for preloading routes that are below the fold or off-screen.\n- Render\n - Preloading by **\"render\"** works by preloading the dependencies for the destination route as soon as the `\u003cLink>` component is rendered in the DOM.\n - This strategy is useful for preloading routes that are always needed.\n\n## How long does preloaded data stay in memory?\n\nPreloaded route matches are temporarily cached in memory with a few important caveats:\n\n- **Unused preloaded data is removed after 30 seconds by default.** This can be configured by setting the `defaultPreloadMaxAge` option on your router.\n- **Obviously, when a route is loaded, its preloaded version is promoted to the router's normal pending matches state.**\n\nIf you need more control over preloading, caching and/or garbage collection of preloaded data, you should use an external caching library like TanStack Query.\n\nThe simplest way to preload routes for your application is to set the `defaultPreload` option to `intent` for your entire router:\n\n\n\n# React\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n defaultPreload: 'intent',\n})\n```\n\n# Solid\n\n```tsx\nimport { createRouter } from '@tanstack/solid-router'\n\nconst router = createRouter({\n // ...\n defaultPreload: 'intent',\n})\n```\n\n\n\nThis will turn on `intent` preloading by default for all `\u003cLink>` components in your application. You can also set the `preload` prop on individual `\u003cLink>` components to override the default behavior.\n\n## Preload Delay\n\nBy default, preloading will start after **50ms** of the user hovering or touching a `\u003cLink>` component. You can change this delay by setting the `defaultPreloadDelay` option on your router:\n\n\n\n# React\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n defaultPreloadDelay: 100,\n})\n```\n\n# Solid\n\n```tsx\nimport { createRouter } from '@tanstack/solid-router'\n\nconst router = createRouter({\n // ...\n defaultPreloadDelay: 100,\n})\n```\n\n\n\nYou can also set the `preloadDelay` prop on individual `\u003cLink>` components to override the default behavior on a per-link basis.\n\n## Built-in Preloading & `preloadStaleTime`\n\nIf you're using the built-in loaders, you can control how long preloaded data is considered fresh until another preload is triggered by setting either `routerOptions.defaultPreloadStaleTime` or `routeOptions.preloadStaleTime` to a number of milliseconds. **By default, preloaded data is considered fresh for 30 seconds.**.\n\nTo change this, you can set the `defaultPreloadStaleTime` option on your router:\n\n\n\n# React\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n defaultPreloadStaleTime: 10_000,\n})\n```\n\n# Solid\n\n```tsx\nimport { createRouter } from '@tanstack/solid-router'\n\nconst router = createRouter({\n // ...\n defaultPreloadStaleTime: 10_000,\n})\n```\n\n\n\nOr, you can use the `routeOptions.preloadStaleTime` option on individual routes:\n\n```tsx\n// src/routes/posts.$postId.tsx\nexport const Route = createFileRoute('/posts/$postId')({\n loader: async ({ params }) => fetchPost(params.postId),\n // Preload the route again if the preload cache is older than 10 seconds\n preloadStaleTime: 10_000,\n})\n```\n\n## Preloading with External Libraries\n\nWhen integrating external caching libraries like React Query, which have their own mechanisms for determining stale data, you may want to override the default preloading and stale-while-revalidate logic of TanStack Router. These libraries often use options like staleTime to control the freshness of data.\n\nTo customize the preloading behavior in TanStack Router and fully leverage your external library's caching strategy, you can bypass the built-in caching by setting routerOptions.defaultPreloadStaleTime or routeOptions.preloadStaleTime to 0. This ensures that all preloads are marked as stale internally, and loaders are always invoked, allowing your external library, such as React Query, to manage data loading and caching.\n\nFor example:\n\n\n\n# React\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n // ...\n defaultPreloadStaleTime: 0,\n})\n```\n\n# Solid\n\n```tsx\nimport { createRouter } from '@tanstack/solid-router'\n\nconst router = createRouter({\n // ...\n defaultPreloadStaleTime: 0,\n})\n```\n\n\n\nThis would then allow you, for instance, to use an option like React Query's `staleTime` to control the freshness of your preloads.\n\n## Preloading Manually\n\nIf you need to manually preload a route, you can use the router's `preloadRoute` method. It accepts a standard TanStack `NavigateOptions` object and returns a promise that resolves when the route is preloaded.\n\n\n\n# React\n\n```tsx\nfunction Component() {\n const router = useRouter()\n\n useEffect(() => {\n async function preload() {\n try {\n const matches = await router.preloadRoute({\n to: postRoute,\n params: { id: 1 },\n })\n } catch (err) {\n // Failed to preload route\n }\n }\n\n preload()\n }, [router])\n\n return \u003cdiv />\n}\n```\n\n# Solid\n\n```tsx\nfunction Component() {\n const router = useRouter()\n\n createEffect(() => {\n async function preload() {\n try {\n const matches = await router.preloadRoute({\n to: postRoute,\n params: { id: 1 },\n })\n } catch (err) {\n // Failed to preload route\n }\n }\n\n preload()\n })\n\n return \u003cdiv />\n}\n```\n\n\n\nIf you need to preload only the JS chunk of a route, you can use the router's `loadRouteChunk` method. It accepts a route object and returns a promise that resolves when the route chunk is loaded.\n\n\n\n# React\n\n```tsx\nfunction Component() {\n const router = useRouter()\n\n useEffect(() => {\n async function preloadRouteChunks() {\n try {\n const postsRoute = router.routesByPath['/posts']\n await Promise.all([\n router.loadRouteChunk(router.routesByPath['/']),\n router.loadRouteChunk(postsRoute),\n router.loadRouteChunk(postsRoute.parentRoute),\n ])\n } catch (err) {\n // Failed to preload route chunk\n }\n }\n\n preloadRouteChunks()\n }, [router])\n\n return \u003cdiv />\n}\n```\n\n# Solid\n\n```tsx\nfunction Component() {\n const router = useRouter()\n\n createEffect(() => {\n async function preloadRouteChunks() {\n try {\n const postsRoute = router.routesByPath['/posts']\n await Promise.all([\n router.loadRouteChunk(router.routesByPath['/']),\n router.loadRouteChunk(postsRoute),\n router.loadRouteChunk(postsRoute.parentRoute),\n ])\n } catch (err) {\n // Failed to preload route chunk\n }\n }\n\n preloadRouteChunks()\n })\n\n return \u003cdiv />\n}\n```\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7611,"content_sha256":"cfed6acd7a65518997211063834a23208daa2e3d4bd3387dc5da929efc09e2d8"},{"filename":"references/docs/router/guide/render-optimizations.md","content":"---\ntitle: Render Optimizations\n---\n\nTanStack Router includes several optimizations to ensure your components only re-render when necessary. These optimizations include:\n\n## structural sharing\n\nTanStack Router uses a technique called \"structural sharing\" to preserve as many references as possible between re-renders, which is particularly useful for state stored in the URL, such as search parameters.\n\nFor example, consider a `details` route with two search parameters, `foo` and `bar`, accessed like this:\n\n```tsx\nconst search = Route.useSearch()\n```\n\nWhen only `bar` is changed by navigating from `/details?foo=f1&bar=b1` to `/details?foo=f1&bar=b2`, `search.foo` will be referentially stable and only `search.bar` will be replaced.\n\n## fine-grained selectors\n\nYou can access and subscribe to the router state using various hooks like `useRouterState`, `useSearch`, and others. If you only want a specific component to re-render when a particular subset of the router state such as a subset of the search parameters changes, you can use partial subscriptions with the `select` property.\n\n```tsx\n// component won't re-render when `bar` changes\nconst foo = Route.useSearch({ select: ({ foo }) => foo })\n```\n\n### structural sharing with fine-grained selectors\n\nThe `select` function can perform various calculations on the router state, allowing you to return different types of values, such as objects. For example:\n\n```tsx\nconst result = Route.useSearch({\n select: (search) => {\n return {\n foo: search.foo,\n hello: `hello ${search.foo}`,\n }\n },\n})\n```\n\nAlthough this works, it will cause your component to re-render each time, since `select` is now returning a new object each time it’s called.\n\nYou can avoid this re-rendering issue by using \"structural sharing\" as described above. By default, structural sharing is turned off to maintain backward compatibility, but this may change in v2.\n\nTo enable structural sharing for fine grained selectors, you have two options:\n\n#### Enable it by default in the router options:\n\n```tsx\nconst router = createRouter({\n routeTree,\n defaultStructuralSharing: true,\n})\n```\n\n#### Enable it per hook usage as shown here:\n\n```tsx\nconst result = Route.useSearch({\n select: (search) => {\n return {\n foo: search.foo,\n hello: `hello ${search.foo}`,\n }\n },\n structuralSharing: true,\n})\n```\n\n> [!IMPORTANT]\n> Structural sharing only works with JSON-compatible data. This means you cannot use `select` to return items like class instances if structural sharing is enabled.\n\nIn line with TanStack Router's type-safe design, TypeScript will raise an error if you attempt the following:\n\n```tsx\nconst result = Route.useSearch({\n select: (search) => {\n return {\n date: new Date(),\n }\n },\n structuralSharing: true,\n})\n```\n\nIf structural sharing is enabled by default in the router options, you can prevent this error by setting `structuralSharing: false`.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2938,"content_sha256":"97eb6b4e715873cd3cb30a67efe7696b1a792f197f0a8d362c32252f2fe07da6"},{"filename":"references/docs/router/guide/route-masking.md","content":"---\ntitle: Route Masking\n---\n\nRoute masking is a way to mask the actual URL of a route that gets persisted to the browser's history and URL bar. This is useful for scenarios where you want to show a different URL than the one that is actually being navigated to and then falling back to the displayed URL when it is shared and (optionally) when the page is reloaded. Here's a few examples:\n\n- Navigating to a modal route like `/photo/5/modal`, but masking the actual URL as `/photos/5`\n- Navigating to a modal route like `/post/5/comments`, but masking the actual URL as `/posts/5`\n- Navigating to a route with the search param `?showLogin=true`, but masking the URL to _not_ contain the search param\n- Navigating to a route with the search param `?modal=settings`, but masking the URL as `/settings'\n\nEach of these scenarios can be achieved with route masking and even extended to support more advanced patterns like [parallel routes](./parallel-routes.md).\n\n## How does route masking work?\n\n> [!IMPORTANT]\n> You **do not** need to understand how route masking works in order to use it. This section is for those who are curious about how it works under the hood. Skip to [How do I use route masking?](#how-do-i-use-route-masking) to learn how to use it!.\n\nRoute masking utilizes the `location.state` API to store the desired runtime location inside of the location that will get written to the URL. It stores this runtime location under the `__tempLocation` state property:\n\n```tsx\nconst location = {\n pathname: '/photos/5',\n search: '',\n hash: '',\n state: {\n key: 'wesdfs',\n __tempKey: 'sadfasd',\n __tempLocation: {\n pathname: '/photo/5/modal',\n search: '',\n hash: '',\n state: {},\n },\n },\n}\n```\n\nWhen the router parses a location from history with the `location.state.__tempLocation` property, it will use that location instead of the one that was parsed from the URL. This allows you to navigate to a route like `/photos/5` and have the router actually navigate to `/photo/5/modal` instead. When this happens, the history location is saved back into the `location.maskedLocation` property, just in case we need to know what the **actual URL** is. One example of where this is used is in the Devtools where we detect if a route is masked and show the actual URL instead of the masked one!\n\nRemember, you don't need to worry about any of this. It's all handled for you automatically under the hood!\n\n## How do I use route masking?\n\nRoute masking is a simple API that can be used in 2 ways:\n\n- Imperatively via the `mask` option available on the `\u003cLink>` and `navigate()` APIs\n- Declaratively via the Router's `routeMasks` option\n\nWhen using either route masking APIs, the `mask` option accepts the same navigation object that the `\u003cLink>` and `navigate()` APIs accept. This means you can use the same `to`, `replace`, `state`, and `search` options that you're already familiar with. The only difference is that the `mask` option will be used to mask the URL of the route being navigated to.\n\n> The mask option is also **type-safe**! This means that if you're using TypeScript, you'll get type errors if you try to pass an invalid navigation object to the `mask` option. Booyah!\n\n### Imperative route masking\n\nThe `\u003cLink>` and `navigate()` APIs both accept a `mask` option that can be used to mask the URL of the route being navigated to. Here's an example of using it with the `\u003cLink>` component:\n\n```tsx\n\u003cLink\n to=\"/photos/$photoId/modal\"\n params={{ photoId: 5 }}\n mask={{\n to: '/photos/$photoId',\n params: {\n photoId: 5,\n },\n }}\n>\n Open Photo\n\u003c/Link>\n```\n\nAnd here's an example of using it with the `navigate()` API:\n\n```tsx\nconst navigate = useNavigate()\n\nfunction onOpenPhoto() {\n navigate({\n to: '/photos/$photoId/modal',\n params: { photoId: 5 },\n mask: {\n to: '/photos/$photoId',\n params: {\n photoId: 5,\n },\n },\n })\n}\n```\n\n### Declarative route masking\n\nIn addition to the imperative API, you can also use the Router's `routeMasks` option to declaratively mask routes. Instead of needing to pass the `mask` option to every `\u003cLink>` or `navigate()` call, you can instead create a route mask on the Router to mask routes that match a certain pattern. Here's an example of the same route mask from above, but using the `routeMasks` option instead:\n\n\n\n# React\n\n```tsx\nimport { createRouteMask } from '@tanstack/react-router'\n\nconst photoModalToPhotoMask = createRouteMask({\n routeTree,\n from: '/photos/$photoId/modal',\n to: '/photos/$photoId',\n params: (prev) => ({\n photoId: prev.photoId,\n }),\n})\n\nconst router = createRouter({\n routeTree,\n routeMasks: [photoModalToPhotoMask],\n})\n```\n\n# Solid\n\n```tsx\nimport { createRouteMask } from '@tanstack/solid-router'\n\nconst photoModalToPhotoMask = createRouteMask({\n routeTree,\n from: '/photos/$photoId/modal',\n to: '/photos/$photoId',\n params: (prev) => ({\n photoId: prev.photoId,\n }),\n})\n\nconst router = createRouter({\n routeTree,\n routeMasks: [photoModalToPhotoMask],\n})\n```\n\n\n\nWhen creating a route mask, you'll need to pass 1 argument with at least:\n\n- `routeTree` - The route tree that the route mask will be applied to\n- `from` - The route ID that the route mask will be applied to\n- `...navigateOptions` - The standard `to`, `search`, `params`, `replace`, etc options that the `\u003cLink>` and `navigate()` APIs accept\n\n> The `createRouteMask` option is also **type-safe**! This means that if you're using TypeScript, you'll get type errors if you try to pass an invalid route mask to the `routeMasks` option.\n\n## Unmasking when sharing the URL\n\nURLs are automatically unmasked when they are shared since as soon as a URL is detached from your browsers local history stack, the URL masking data is no longer available. Essentially, as soon as you copy and paste a URL out of your history, its masking data is lost... after all, that's the point of masking a URL!\n\n## Local Unmasking Defaults\n\n**By default, URLs are not unmasked when the page is reloaded locally**. Masking data is stored in the `location.state` property of the history location, so as long as the history location is still in memory in your history stack, the masking data will be available and the URL will continue to be masked.\n\n## Unmasking on page reload\n\n**As stated above, URLs are not unmasked when the page is reloaded by default**.\n\nIf you want to unmask a URL locally when the page is reloaded, you have 3 options, each overriding the previous one in priority if passed:\n\n- Set the Router's default `unmaskOnReload` option to `true`\n- Return the `unmaskOnReload: true` option from the masking function when creating a route mask with `createRouteMask()`\n- Pass the `unmaskOnReload: true` option to the `\u003cLink`> component or `navigate()` API\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6785,"content_sha256":"49952446bacf1271998b327d2a95a895d6dad716bde581dc2335f7a3c4870d5a"},{"filename":"references/docs/router/guide/router-context.md","content":"---\ntitle: Router Context\n---\n\nTanStack Router's router context is a very powerful tool that can be used for dependency injection among many other things. Aptly named, the router context is passed through the router and down through each matching route. At each route in the hierarchy, the context can be modified or added to. Here's a few ways you might use the router context practically:\n\n- Dependency Injection\n - You can supply dependencies (e.g. a loader function, a data fetching client, a mutation service) which the route and all child routes can access and use without importing or creating directly.\n- Breadcrumbs\n - While the main context object for each route is merged as it descends, each route's unique context is also stored making it possible to attach breadcrumbs or methods to each route's context.\n- Dynamic meta tag management\n - You can attach meta tags to each route's context and then use a meta tag manager to dynamically update the meta tags on the page as the user navigates the site.\n\nThese are just suggested uses of the router context. You can use it for whatever you want!\n\n## Typed Router Context\n\nLike everything else, the root router context is strictly typed. This type can be augmented via any route's `beforeLoad` option as it is merged down the route match tree. To constrain the type of the root router context, you must use the `createRootRouteWithContext\u003cYourContextTypeHere>()(routeOptions)` function to create a new router context instead of the `createRootRoute()` function to create your root route. Here's an example:\n\n\n\n# React\n\n```tsx\nimport {\n createRootRouteWithContext,\n createRouter,\n} from '@tanstack/react-router'\n\ninterface MyRouterContext {\n user: User\n}\n\n// Use the routerContext to create your root route\nconst rootRoute = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n\nconst routeTree = rootRoute.addChildren([\n // ...\n])\n\n// Use the routerContext to create your router\nconst router = createRouter({\n routeTree,\n})\n```\n\n# Solid\n\n```tsx\nimport {\n createRootRouteWithContext,\n createRouter,\n} from '@tanstack/solid-router'\n\ninterface MyRouterContext {\n user: User\n}\n\n// Use the routerContext to create your root route\nconst rootRoute = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n\nconst routeTree = rootRoute.addChildren([\n // ...\n])\n\n// Use the routerContext to create your router\nconst router = createRouter({\n routeTree,\n})\n```\n\n\n\n> [!TIP]\n> `MyRouterContext` only needs to contain content that will be passed directly to `createRouter` below. All other context added in `beforeLoad` will be inferred.\n\n## Passing the initial Router Context\n\nThe router context is passed to the router at instantiation time. You can pass the initial router context to the router via the `context` option:\n\n> [!TIP]\n> If your context has any required properties, you will see a TypeScript error if you don't pass them in the initial router context. If all of your context properties are optional, you will not see a TypeScript error and passing the context will be optional. If you don't pass a router context, it defaults to `{}`.\n\n\n\n# React\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\n// Use the routerContext you created to create your router\nconst router = createRouter({\n routeTree,\n context: {\n user: {\n id: '123',\n name: 'John Doe',\n },\n },\n})\n```\n\n# Solid\n\n```tsx\nimport { createRouter } from '@tanstack/solid-router'\n\n// Use the routerContext you created to create your router\nconst router = createRouter({\n routeTree,\n context: {\n user: {\n id: '123',\n name: 'John Doe',\n },\n },\n})\n```\n\n\n\n### Invalidating the Router Context\n\nIf you need to invalidate the context state you are passing into the router, you can call the `invalidate` method to tell the router to recompute the context. This is useful when you need to update the context state and have the router recompute the context for all routes.\n\n\n\n# React\n\n```tsx\nfunction useAuth() {\n const router = useRouter()\n const [user, setUser] = useState\u003cUser | null>(null)\n\n useEffect(() => {\n const unsubscribe = auth.onAuthStateChanged((user) => {\n setUser(user)\n router.invalidate()\n })\n\n return unsubscribe\n }, [])\n\n return user\n}\n```\n\n# Solid\n\n```tsx\nfunction useAuth() {\n const router = useRouter()\n const [user, setUser] = createSignal\u003cUser | null>(null)\n\n createEffect(() => {\n const unsubscribe = auth.onAuthStateChanged((user) => {\n setUser(user)\n router.invalidate()\n })\n\n return unsubscribe\n }, [])\n\n return user()\n}\n```\n\n\n\n## Using the Router Context\n\nOnce you have defined the router context type, you can use it in your route definitions:\n\n```tsx\n// src/routes/todos.tsx\nexport const Route = createFileRoute('/todos')({\n component: Todos,\n loader: ({ context }) => fetchTodosByUserId(context.user.id),\n})\n```\n\nYou can even inject data fetching and mutation implementations themselves! In fact, this is highly recommended \n\nLet's try this with a simple function to fetch some todos:\n\n```tsx\nconst fetchTodosByUserId = async ({ userId }) => {\n const response = await fetch(`/api/todos?userId=${userId}`)\n const data = await response.json()\n return data\n}\n\nconst router = createRouter({\n routeTree: rootRoute,\n context: {\n userId: '123',\n fetchTodosByUserId,\n },\n})\n```\n\nThen, in your route:\n\n```tsx\n// src/routes/todos.tsx\nexport const Route = createFileRoute('/todos')({\n component: Todos,\n loader: ({ context }) => context.fetchTodosByUserId(context.userId),\n})\n```\n\n### How about an external data fetching library?\n\n\n\n# React\n\n```tsx\nimport {\n createRootRouteWithContext,\n createRouter,\n} from '@tanstack/react-router'\n\ninterface MyRouterContext {\n queryClient: QueryClient\n}\n\nconst rootRoute = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n\nconst queryClient = new QueryClient()\n\nconst router = createRouter({\n routeTree: rootRoute,\n context: {\n queryClient,\n },\n})\n```\n\n# Solid\n\n```tsx\nimport {\n createRootRouteWithContext,\n createRouter,\n} from '@tanstack/solid-router'\n\ninterface MyRouterContext {\n queryClient: QueryClient\n}\n\nconst rootRoute = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n\nconst queryClient = new QueryClient()\n\nconst router = createRouter({\n routeTree: rootRoute,\n context: {\n queryClient,\n },\n})\n```\n\n\n\nThen, in your route:\n\n```tsx\n// src/routes/todos.tsx\nexport const Route = createFileRoute('/todos')({\n component: Todos,\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData({\n queryKey: ['todos', { userId: user.id }],\n queryFn: fetchTodos,\n })\n },\n})\n```\n\n\n\n# React\n\n## How about using React Context/Hooks?\n\nWhen trying to use React Context or Hooks in your route's `beforeLoad` or `loader` functions, it's important to remember React's Rules of Hooks. You can't use hooks in a non-React function, so you can't use hooks in your `beforeLoad` or `loader` functions.\n\nSo, how do we use React Context or Hooks in our route's `beforeLoad` or `loader` functions? We can use the router context to pass down the React Context or Hooks to our route's `beforeLoad` or `loader` functions.\n\nLet's look at the setup for an example, where we pass down a `useNetworkStrength` hook to our route's `loader` function:\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\n// First, make sure the context for the root route is typed\nimport { createRootRouteWithContext } from '@tanstack/react-router'\nimport { useNetworkStrength } from '@/hooks/useNetworkStrength'\n\ninterface MyRouterContext {\n networkStrength: ReturnType\u003ctypeof useNetworkStrength>\n}\n\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n```\n\n\n\nIn this example, we'd instantiate the hook before rendering the router using the `\u003cRouterProvider />`. This way, the hook would be called in React-land, therefore adhering to the Rules of Hooks.\n\n\n\n```tsx title=\"src/router.tsx\"\nimport { createRouter } from '@tanstack/react-router'\n\nimport { routeTree } from './routeTree.gen'\n\nexport const router = createRouter({\n routeTree,\n context: {\n networkStrength: undefined!, // We'll set this in React-land\n },\n})\n```\n\n\n\nThen, we can call the `useNetworkStrength` hook in our `App` component and pass the returned value into the router context via the `\u003cRouterProvider />`:\n\n\n\n```tsx title=\"src/main.tsx\"\nimport { RouterProvider } from '@tanstack/react-router'\nimport { router } from './router'\n\nimport { useNetworkStrength } from '@/hooks/useNetworkStrength'\n\nfunction App() {\n const networkStrength = useNetworkStrength()\n // Inject the returned value from the hook into the router context\n return \u003cRouterProvider router={router} context={{ networkStrength }} />\n}\n\n// ...\n```\n\n\n\nSo, now in our route's `loader` function, we can access the `networkStrength` hook from the router context:\n\n\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts')({\n component: Posts,\n loader: ({ context }) => {\n if (context.networkStrength === 'STRONG') {\n // Do something\n }\n },\n})\n```\n\n\n\n\n\n## Modifying the Router Context\n\nThe router context is passed down the route tree and is merged at each route. This means that you can modify the context at each route and the modifications will be available to all child routes. Here's an example:\n\n\n\n# React\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRouteWithContext } from '@tanstack/react-router'\n\ninterface MyRouterContext {\n foo: boolean\n}\n\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n```\n\n\n\n\n\n```tsx title=\"src/router.tsx\"\nimport { createRouter } from '@tanstack/react-router'\n\nimport { routeTree } from './routeTree.gen'\n\nconst router = createRouter({\n routeTree,\n context: {\n foo: true,\n },\n})\n```\n\n\n\n\n\n```tsx title=\"src/routes/todos.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/todos')({\n component: Todos,\n beforeLoad: () => {\n return {\n bar: true,\n }\n },\n loader: ({ context }) => {\n context.foo // true\n context.bar // true\n },\n})\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRouteWithContext } from '@tanstack/solid-router'\n\ninterface MyRouterContext {\n foo: boolean\n}\n\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()({\n component: App,\n})\n```\n\n\n\n\n\n```tsx title=\"src/router.tsx\"\nimport { createRouter } from '@tanstack/solid-router'\n\nimport { routeTree } from './routeTree.gen'\n\nconst router = createRouter({\n routeTree,\n context: {\n foo: true,\n },\n})\n```\n\n\n\n\n\n```tsx title=\"src/routes/todos.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/todos')({\n component: Todos,\n beforeLoad: () => {\n return {\n bar: true,\n }\n },\n loader: ({ context }) => {\n context.foo // true\n context.bar // true\n },\n})\n```\n\n\n\n\n\n## Processing Accumulated Route Context\n\nContext, especially the isolated route `context` objects, make it trivial to accumulate and process the route context objects for all matched routes. Here's an example where we use all of the matched route contexts to generate a breadcrumb trail:\n\n```tsx title=\"src/routes/__root.tsx\"\nexport const Route = createRootRoute({\n component: () => {\n const matches = useRouterState({ select: (s) => s.matches })\n\n const breadcrumbs = matches\n .filter((match) => match.context.getTitle)\n .map(({ pathname, context }) => {\n return {\n title: context.getTitle(),\n path: pathname,\n }\n })\n\n // ...\n },\n})\n```\n\nUsing that same route context, we could also generate a title tag for our page's `\u003chead>`:\n\n```tsx title=\"src/routes/__root.tsx\"\nexport const Route = createRootRoute({\n component: () => {\n const matches = useRouterState({ select: (s) => s.matches })\n\n const matchWithTitle = [...matches]\n .reverse()\n .find((d) => d.context.getTitle)\n\n const title = matchWithTitle?.context.getTitle() || 'My App'\n\n return (\n \u003chtml>\n \u003chead>\n \u003ctitle>{title}\u003c/title>\n \u003c/head>\n \u003cbody>{/* ... */}\u003c/body>\n \u003c/html>\n )\n },\n})\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12303,"content_sha256":"2bdf4bab3d60e8972856db61f81e303309919c76b370ef2358ba3c7931ac5e82"},{"filename":"references/docs/router/guide/scroll-restoration.md","content":"---\nid: scroll-restoration\ntitle: Scroll Restoration\n---\n\n## Hash/Top-of-Page Scrolling\n\nOut of the box, TanStack Router supports both **hash scrolling** and **top-of-page scrolling** without any additional configuration.\n\n## Scroll-to-top & Nested Scrollable Areas\n\nBy default, scroll-to-top mimics the behavior of the browser, which means only the `window` itself is scrolled to the top after successful navigation. For many apps however, it's common for the main scrollable area to be a nested div or similar because of advanced layouts. If you would like TanStack Router to also scroll these main scrollable areas for you, you can add selectors to target them using the `routerOptions.scrollToTopSelectors`:\n\n```tsx\nconst router = createRouter({\n scrollToTopSelectors: ['#main-scrollable-area'],\n})\n```\n\nFor complex selectors that cannot be simply resolved using `document.querySelector(selector)`, you can pass functions that return HTML elements to `routerOptions.scrollToTopSelectors`:\n\n```tsx\nconst selector = () =>\n document\n .querySelector('#shadowRootParent')\n ?.shadowRoot?.querySelector('#main-scrollable-area')\n\nconst router = createRouter({\n scrollToTopSelectors: [selector],\n})\n```\n\nThese selectors are handled **in addition to `window`** which cannot be disabled currently.\n\n## Scroll Restoration\n\nScroll restoration is the process of restoring the scroll position of a page when the user navigates back to it. This is normally a built-in feature for standard HTML based websites, but can be difficult to replicate for SPA applications because:\n\n- SPAs typically use the `history.pushState` API for navigation, so the browser doesn't know to restore the scroll position natively\n- SPAs sometimes render content asynchronously, so the browser doesn't know the height of the page until after it's rendered\n- SPAs can sometimes use nested scrollable containers to force specific layouts and features.\n\nNot only that, but it's very common for applications to have multiple scrollable areas within an app, not just the body. For example, a chat application might have a scrollable sidebar and a scrollable chat area. In this case, you would want to restore the scroll position of both areas independently.\n\nTo alleviate this problem, TanStack Router provides a scroll restoration component and hook that handle the process of monitoring, caching and restoring scroll positions for you.\n\nIt does this by:\n\n- Monitoring the DOM for scroll events\n- Registering scrollable areas with the scroll restoration cache\n- Listening to the proper router events to know when to cache and restore scroll positions\n- Storing scroll positions for each scrollable area in the cache (including `window` and `body`)\n- Restoring scroll positions after successful navigations before DOM paint\n\nThat may sound like a lot, but for you, it's as simple as this:\n\n```tsx\nconst router = createRouter({\n scrollRestoration: true,\n})\n```\n\n> [!NOTE]\n> The `\u003cScrollRestoration />` component still works, but has been deprecated.\n\n## Custom Cache Keys\n\nFalling in behind Remix's own Scroll Restoration APIs, you can also customize the key used to cache scroll positions for a given scrollable area using the `getKey` option. This could be used, for example, to force the same scroll position to be used regardless of the users browser history.\n\nThe `getKey` option receives the relevant `Location` state from TanStack Router and expects you to return a string to uniquely identify the scrollable measurements for that state.\n\nThe default `getKey` is `(location) => location.state.__TSR_key!`, where `__TSR_key` is the unique key generated for each entry in the history.\n\n> Older versions, prior to `v1.121.34`, used `state.key` as the default key, but this has been deprecated in favor of `state.__TSR_key`. For now, `location.state.key` will still be available for compatibility, but it will be removed in the next major version.\n\n## Examples\n\nYou could sync scrolling to the pathname:\n\n```tsx\nconst router = createRouter({\n getScrollRestorationKey: (location) => location.pathname,\n})\n```\n\nYou can conditionally sync only some paths, then use the key for the rest:\n\n```tsx\nconst router = createRouter({\n getScrollRestorationKey: (location) => {\n const paths = ['/', '/chat']\n return paths.includes(location.pathname)\n ? location.pathname\n : location.state.__TSR_key!\n },\n})\n```\n\n## Preventing Scroll Restoration\n\nSometimes you may want to prevent scroll restoration from happening. To do this you can utilize the `resetScroll` option available on the following APIs:\n\n- `\u003cLink resetScroll={false}>`\n- `navigate({ resetScroll: false })`\n- `redirect({ resetScroll: false })`\n\nWhen `resetScroll` is set to `false`, the scroll position for the next navigation will not be restored (if navigating to an existing history event in the stack) or reset to the top (if it's a new history event in the stack).\n\n## Manual Scroll Restoration\n\nMost of the time, you won't need to do anything special to get scroll restoration to work. However, there are some cases where you may need to manually control scroll restoration. The most common example is **virtualized lists**.\n\nTo manually control scroll restoration for virtualized lists within the whole browser window:\n\n```tsx\nfunction Component() {\n const scrollEntry = useElementScrollRestoration({\n getElement: () => window,\n })\n\n // Let's use TanStack Virtual to virtualize some content!\n const virtualizer = useWindowVirtualizer({\n count: 10000,\n estimateSize: () => 100,\n // We pass the scrollY from the scroll restoration entry to the virtualizer\n // as the initial offset\n initialOffset: scrollEntry?.scrollY,\n })\n\n return (\n \u003cdiv>\n {virtualizer.getVirtualItems().map(item => (\n ...\n ))}\n \u003c/div>\n )\n}\n```\n\nTo manually control scroll restoration for a specific element, you can use the `useElementScrollRestoration` hook and the `data-scroll-restoration-id` DOM attribute:\n\n\n\n# React\n\n```tsx\nfunction Component() {\n // We need a unique ID for manual scroll restoration on a specific element\n // It should be as unique as possible for this element across your app\n const scrollRestorationId = 'myVirtualizedContent'\n\n // We use that ID to get the scroll entry for this element\n const scrollEntry = useElementScrollRestoration({\n id: scrollRestorationId,\n })\n\n // Let's use TanStack Virtual to virtualize some content!\n const virtualizerParentRef = React.useRef\u003cHTMLDivElement>(null)\n const virtualizer = useVirtualizer({\n count: 10000,\n getScrollElement: () => virtualizerParentRef.current,\n estimateSize: () => 100,\n // We pass the scrollY from the scroll restoration entry to the virtualizer\n // as the initial offset\n initialOffset: scrollEntry?.scrollY,\n })\n\n return (\n \u003cdiv\n ref={virtualizerParentRef}\n // We pass the scroll restoration ID to the element\n // as a custom attribute that will get picked up by the\n // scroll restoration watcher\n data-scroll-restoration-id={scrollRestorationId}\n className=\"flex-1 border rounded-lg overflow-auto relative\"\n >\n ...\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx\nfunction Component() {\n // We need a unique ID for manual scroll restoration on a specific element\n // It should be as unique as possible for this element across your app\n const scrollRestorationId = 'myVirtualizedContent'\n\n // We use that ID to get the scroll entry for this element\n const scrollEntry = useElementScrollRestoration({\n id: scrollRestorationId,\n })\n\n // Let's use TanStack Virtual to virtualize some content!\n let virtualizerParentRef: any\n const virtualizer = createVirtualizer({\n count: 10000,\n getScrollElement: () => virtualizerParentRef,\n estimateSize: () => 100,\n // We pass the scrollY from the scroll restoration entry to the virtualizer\n // as the initial offset\n initialOffset: scrollEntry?.scrollY,\n })\n\n return (\n \u003cdiv\n ref={virtualizerParentRef}\n // We pass the scroll restoration ID to the element\n // as a custom attribute that will get picked up by the\n // scroll restoration watcher\n data-scroll-restoration-id={scrollRestorationId}\n class=\"flex-1 border rounded-lg overflow-auto relative\"\n >\n ...\n \u003c/div>\n )\n}\n```\n\n\n\n## Scroll Behavior\n\nTo control the scroll behavior when navigating between pages, you can use the `scrollRestorationBehavior` option. This allows you to make the transition between pages instant instead of a smooth scroll. The global configuration of scroll restoration behavior has the same options as those supported by the browser, which are `smooth`, `instant`, and `auto` (see MDN for more information).\n\n```tsx\nconst router = createRouter({\n scrollRestorationBehavior: 'instant',\n})\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8800,"content_sha256":"775fea4b267092cc6534924b12a60c11c7553f85d4de283f3804ed586d47488b"},{"filename":"references/docs/router/guide/search-params.md","content":"---\ntitle: Search Params\n---\n\nSimilar to how TanStack Query made handling server-state in your React and Solid applications a breeze, TanStack Router aims to unlock the power of URL search params in your applications.\n\n> If you are on a really old browser, like IE11, you may need to use a polyfill for `URLSearchParams`.\n\n## Why not just use `URLSearchParams`?\n\nWe get it, you've been hearing a lot of \"use the platform\" lately and for the most part, we agree. However, we also believe it's important to recognize where the platform falls short for more advanced use-cases and we believe `URLSearchParams` is one of these circumstances.\n\nTraditional Search Param APIs usually assume a few things:\n\n- Search params are always strings\n- They are _mostly_ flat\n- Serializing and deserializing using `URLSearchParams` is good enough (Spoiler alert: it's not.)\n- Search params modifications are tightly coupled with the URL's pathname and must be updated together, even if the pathname is not changing.\n\nReality is very different from these assumptions though.\n\n- Search params represent application state, so inevitably, we will expect them to have the same DX associated with other state managers. This means having the capability of distinguishing between primitive value types and efficiently storing and manipulating complex data structures like nested arrays and objects.\n- There are many ways to serialize and deserialize state with different tradeoffs. You should be able to choose the best one for your application or at the very least get a better default than `URLSearchParams`.\n- Immutability & Structural Sharing. Every time you stringify and parse url search params, referential integrity and object identity is lost because each new parse creates a brand new data structure with a unique memory reference. If not properly managed over its lifetime, this constant serialization and parsing can result in unexpected and undesirable performance issues, especially in frameworks like React that choose to track reactivity via immutability or in Solid that normally relies on reconciliation to detect changes from deserialized data sources.\n- Search params, while an important part of the URL, do frequently change independently of the URL's pathname. For example, a user may want to change the page number of a paginated list without touching the URL's pathname.\n\n## Search Params, the \"OG\" State Manager\n\nYou've probably seen search params like `?page=3` or `?filter-name=tanner` in the URL. There is no question that this is truly **a form of global state** living inside of the URL. It's valuable to store specific pieces of state in the URL because:\n\n- Users should be able to:\n - Cmd/Ctrl + Click to open a link in a new tab and reliably see the state they expected\n - Bookmark and share links from your application with others with assurances that they will see exactly the state as when the link was copied.\n - Refresh your app or navigate back and forth between pages without losing their state\n- Developers should be able to easily:\n - Add, remove or modify state in the URL with the same great DX as other state managers\n - Easily validate search params coming from the URL in a format and type that is safe for their application to consume\n - Read and write to search params without having to worry about the underlying serialization format\n\n## JSON-first Search Params\n\nTo achieve the above, the first step built in to TanStack Router is a powerful search param parser that automatically converts the search string of your URL to structured JSON. This means that you can store any JSON-serializable data structure in your search params and it will be parsed and serialized as JSON. This is a huge improvement over `URLSearchParams` which has limited support for array-like structures and nested data.\n\nFor example, navigating to the following route:\n\n```tsx\nconst link = (\n \u003cLink\n to=\"/shop\"\n search={{\n pageIndex: 3,\n includeCategories: ['electronics', 'gifts'],\n sortBy: 'price',\n desc: true,\n }}\n />\n)\n```\n\nWill result in the following URL:\n\n```\n/shop?pageIndex=3&includeCategories=%5B%22electronics%22%2C%22gifts%22%5D&sortBy=price&desc=true\n```\n\nWhen this URL is parsed, the search params will be accurately converted back to the following JSON:\n\n```json\n{\n \"pageIndex\": 3,\n \"includeCategories\": [\"electronics\", \"gifts\"],\n \"sortBy\": \"price\",\n \"desc\": true\n}\n```\n\nIf you noticed, there are a few things going on here:\n\n- The first level of the search params is flat and string based, just like `URLSearchParams`.\n- First level values that are not strings are accurately preserved as actual numbers and booleans.\n- Nested data structures are automatically converted to URL-safe JSON strings\n\n> It's common for other tools to assume that search params are always flat and string-based which is why we've chosen to keep things URLSearchParam compliant at the first level. This ultimately means that even though TanStack Router is managing your nested search params as JSON, other tools will still be able to write to the URL and read first-level params normally.\n\n## Validating and Typing Search Params\n\nDespite TanStack Router being able to parse search params into reliable JSON, they ultimately still came from **a user-facing raw-text input**. Similar to other serialization boundaries, this means that before you consume search params, they should be validated into a format that your application can trust and rely on.\n\n### Enter Validation + TypeScript!\n\nTanStack Router provides convenient APIs for validating and typing search params. This all starts with the `Route`'s `validateSearch` option:\n\n```tsx title=\"src/routes/shop/products.tsx\"\ntype ProductSearchSortOptions = 'newest' | 'oldest' | 'price'\n\ntype ProductSearch = {\n page: number\n filter: string\n sort: ProductSearchSortOptions\n}\n\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: (search: Record\u003cstring, unknown>): ProductSearch => {\n // validate and parse the search params into a typed state\n return {\n page: Number(search?.page ?? 1),\n filter: (search.filter as string) || '',\n sort: (search.sort as ProductSearchSortOptions) || 'newest',\n }\n },\n})\n```\n\nIn the above example, we're validating the search params of the `Route` and returning a typed `ProductSearch` object. This typed object is then made available to this route's other options **and any child routes, too!**\n\n### Validating Search Params\n\nThe `validateSearch` option is a function that is provided the JSON parsed (but non-validated) search params as a `Record\u003cstring, unknown>` and returns a typed object of your choice. It's usually best to provide sensible fallbacks for malformed or unexpected search params so your users' experience stays non-interrupted.\n\nHere's an example:\n\n```tsx title=\"src/routes/shop/products.tsx\"\ntype ProductSearchSortOptions = 'newest' | 'oldest' | 'price'\n\ntype ProductSearch = {\n page: number\n filter: string\n sort: ProductSearchSortOptions\n}\n\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: (search: Record\u003cstring, unknown>): ProductSearch => {\n // validate and parse the search params into a typed state\n return {\n page: Number(search?.page ?? 1),\n filter: (search.filter as string) || '',\n sort: (search.sort as ProductSearchSortOptions) || 'newest',\n }\n },\n})\n```\n\nHere's an example using the Zod library (but feel free to use any validation library you want) to both validate and type the search params in a single step:\n\n```tsx title=\"src/routes/shop/products.tsx\"\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n page: z.number().catch(1),\n filter: z.string().catch(''),\n sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),\n})\n\ntype ProductSearch = z.infer\u003ctypeof productSearchSchema>\n\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: (search) => productSearchSchema.parse(search),\n})\n```\n\nBecause `validateSearch` also accepts an object with the `parse` property, this can be shortened to:\n\n```tsx\nvalidateSearch: productSearchSchema\n```\n\nIn the above example, we used Zod's `.catch()` modifier instead of `.default()` to avoid showing an error to the user because we firmly believe that if a search parameter is malformed, you probably don't want to halt the user's experience through the app to show a big fat error message. That said, there may be times that you **do want to show an error message**. In that case, you can use `.default()` instead of `.catch()`.\n\nThe underlying mechanics why this works relies on the `validateSearch` function throwing an error. If an error is thrown, the route's `onError` option will be triggered (and `error.routerCode` will be set to `VALIDATE_SEARCH` and the `errorComponent` will be rendered instead of the route's `component` where you can handle the search param error however you'd like.\n\n#### Adapters\n\nWhen using a library like Zod to validate search params you might want to `transform` search params before committing the search params to the URL. A common `zod` `transform` is `default` for example.\n\n```tsx\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n page: z.number().default(1),\n filter: z.string().default(''),\n sort: z.enum(['newest', 'oldest', 'price']).default('newest'),\n})\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: productSearchSchema,\n})\n```\n\nIt might be surprising that when you try to navigate to this route, `search` is required. The following `Link` will type error as `search` is missing.\n\n```tsx\n\u003cLink to=\"/shop/products\" />\n```\n\nFor validation libraries we recommend using adapters which infer the correct `input` and `output` types.\n\n### Zod\n\nAn adapter is provided for Zod which will pipe through the correct `input` type and `output` type\n\n```tsx\nimport { zodValidator } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n page: z.number().default(1),\n filter: z.string().default(''),\n sort: z.enum(['newest', 'oldest', 'price']).default('newest'),\n})\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: zodValidator(productSearchSchema),\n})\n```\n\nThe important part here is the following use of `Link` no longer requires `search` params\n\n```tsx\n\u003cLink to=\"/shop/products\" />\n```\n\nHowever the use of `catch` here overrides the types and makes `page`, `filter` and `sort` `unknown` causing type loss. We have handled this case by providing a `fallback` generic function which retains the types but provides a `fallback` value when validation fails\n\n```tsx\nimport { fallback, zodValidator } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n page: fallback(z.number(), 1).default(1),\n filter: fallback(z.string(), '').default(''),\n sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(\n 'newest',\n ),\n})\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: zodValidator(productSearchSchema),\n})\n```\n\nTherefore when navigating to this route, `search` is optional and retains the correct types.\n\nWhile not recommended, it is also possible to configure `input` and `output` type in case the `output` type is more accurate than the `input` type\n\n```tsx\nconst productSearchSchema = z.object({\n page: fallback(z.number(), 1).default(1),\n filter: fallback(z.string(), '').default(''),\n sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(\n 'newest',\n ),\n})\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: zodValidator({\n schema: productSearchSchema,\n input: 'output',\n output: 'input',\n }),\n})\n```\n\nThis provides flexibility in which type you want to infer for navigation and which types you want to infer for reading search params.\n\n### Valibot\n\n> [!WARNING]\n> Router expects the valibot 1.0 package to be installed.\n\nWhen using Valibot an adapter is not needed to ensure the correct `input` and `output` types are used for navigation and reading search params. This is because `valibot` implements Standard Schema\n\n```tsx\nimport * as v from 'valibot'\n\nconst productSearchSchema = v.object({\n page: v.optional(v.fallback(v.number(), 1), 1),\n filter: v.optional(v.fallback(v.string(), ''), ''),\n sort: v.optional(\n v.fallback(v.picklist(['newest', 'oldest', 'price']), 'newest'),\n 'newest',\n ),\n})\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: productSearchSchema,\n})\n```\n\n### Arktype\n\n> [!WARNING]\n> Router expects the arktype 2.0-rc package to be installed.\n\nWhen using ArkType an adapter is not needed to ensure the correct `input` and `output` types are used for navigation and reading search params. This is because ArkType implements Standard Schema\n\n```tsx\nimport { type } from 'arktype'\n\nconst productSearchSchema = type({\n page: 'number = 1',\n filter: 'string = \"\"',\n sort: '\"newest\" | \"oldest\" | \"price\" = \"newest\"',\n})\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: productSearchSchema,\n})\n```\n\n### Effect/Schema\n\nWhen using Effect/Schema an adapter is not needed to ensure the correct `input` and `output` types are used for navigation and reading search params. This is because Effect/Schema implements Standard Schema\n\n```tsx\nimport { Schema as S } from 'effect'\n\nconst productSearchSchema = S.standardSchemaV1(\n S.Struct({\n page: S.NumberFromString.pipe(\n S.optional,\n S.withDefaults({\n constructor: () => 1,\n decoding: () => 1,\n }),\n ),\n filter: S.String.pipe(\n S.optional,\n S.withDefaults({\n constructor: () => '',\n decoding: () => '',\n }),\n ),\n sort: S.Literal('newest', 'oldest', 'price').pipe(\n S.optional,\n S.withDefaults({\n constructor: () => 'newest' as const,\n decoding: () => 'newest' as const,\n }),\n ),\n }),\n)\n\nexport const Route = createFileRoute('/shop/products/')({\n validateSearch: productSearchSchema,\n})\n```\n\n## Reading Search Params\n\nOnce your search params have been validated and typed, you're finally ready to start reading and writing to them. There are a few ways to do this in TanStack Router, so let's check them out.\n\n### Using Search Params in Loaders\n\nPlease read the [Search Params in Loaders](./data-loading.md#using-loaderdeps-to-access-search-params) section for more information about how to read search params in loaders with the `loaderDeps` option.\n\n### Search Params are inherited from Parent Routes\n\nThe search parameters and types of parents are merged as you go down the route tree, so child routes also have access to their parent's search params:\n\n\n\n```tsx title=\"src/routes/shop/products.tsx\"\nconst productSearchSchema = z.object({\n page: z.number().catch(1),\n filter: z.string().catch(''),\n sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),\n})\n\ntype ProductSearch = z.infer\u003ctypeof productSearchSchema>\n\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: productSearchSchema,\n})\n```\n\n```tsx title=\"src/routes/shop/products/$productId.tsx\"\nexport const Route = createFileRoute('/shop/products/$productId')({\n beforeLoad: ({ search }) => {\n search\n // ^? ProductSearch ✅\n },\n})\n```\n\n\n\n### Search Params in Components\n\nYou can access your route's validated search params in your route's `component` via the `useSearch` hook.\n\n```tsx title=\"src/routes/shop/products.tsx\"\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: productSearchSchema,\n})\n\nconst ProductList = () => {\n const { page, filter, sort } = Route.useSearch()\n\n return \u003cdiv>...\u003c/div>\n}\n```\n\n> [!TIP]\n> If your component is code-split, you can use the [getRouteApi function](./code-splitting.md#manually-accessing-route-apis-in-other-files-with-the-getrouteapi-helper) to avoid having to import the `Route` configuration to get access to the typed `useSearch()` hook.\n\n### Search Params outside of Route Components\n\nYou can access your route's validated search params anywhere in your app using the `useSearch` hook. By passing the `from` id/path of your origin route, you'll get even better type safety:\n\n```tsx\n// src/routes/shop.products.tsx\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: productSearchSchema,\n // ...\n})\n\n// Somewhere else...\n\n// src/components/product-list-sidebar.tsx\nconst routeApi = getRouteApi('/shop/products')\n\nconst ProductList = () => {\n const routeSearch = routeApi.useSearch()\n\n // OR\n\n const { page, filter, sort } = useSearch({\n from: Route.fullPath,\n })\n\n return \u003cdiv>...\u003c/div>\n}\n```\n\nOr, you can loosen up the type-safety and get an optional `search` object by passing `strict: false`:\n\n```tsx\nfunction ProductList() {\n const search = useSearch({\n strict: false,\n })\n // {\n // page: number | undefined\n // filter: string | undefined\n // sort: 'newest' | 'oldest' | 'price' | undefined\n // }\n\n return \u003cdiv>...\u003c/div>\n}\n```\n\n## Writing Search Params\n\nNow that you've learned how to read your route's search params, you'll be happy to know that you've already seen the primary APIs to modify and update them. Let's remind ourselves a bit\n\n### `\u003cLink search />`\n\nThe best way to update search params is to use the `search` prop on the `\u003cLink />` component.\n\nIf the search for the current page shall be updated and the `from` prop is specified, the `to` prop can be omitted. \nHere's an example:\n\n```tsx title=\"src/routes/shop/products.tsx\"\nexport const Route = createFileRoute('/shop/products')({\n validateSearch: productSearchSchema,\n})\n\nconst ProductList = () => {\n return (\n \u003cdiv>\n \u003cLink from={Route.fullPath} search={(prev) => ({ page: prev.page + 1 })}>\n Next Page\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\nIf you want to update the search params in a generic component that is rendered on multiple routes, specifying `from` can be challenging.\n\nIn this scenario you can set `to=\".\"` which will give you access to loosely typed search params. \nHere is an example that illustrates this:\n\n```tsx\n// `page` is a search param that is defined in the __root route and hence available on all routes.\nconst PageSelector = () => {\n return (\n \u003cdiv>\n \u003cLink to=\".\" search={(prev) => ({ ...prev, page: prev.page + 1 })}>\n Next Page\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\nIf the generic component is only rendered in a specific subtree of the route tree, you can specify that subtree using `from`. Here you can omit `to='.'` if you want.\n\n```tsx\n// `page` is a search param that is defined in the /posts route and hence available on all of its child routes.\nconst PageSelector = () => {\n return (\n \u003cdiv>\n \u003cLink\n from=\"/posts\"\n to=\".\"\n search={(prev) => ({ ...prev, page: prev.page + 1 })}\n >\n Next Page\n \u003c/Link>\n \u003c/div>\n )\n```\n\n### `useNavigate(), navigate({ search })`\n\nThe `navigate` function also accepts a `search` option that works the same way as the `search` prop on `\u003cLink />`:\n\n```tsx title=\"src/routes/shop/products.tsx\"\nexport const Route = createFileRoute('/shop/products/$productId')({\n validateSearch: productSearchSchema,\n})\n\nconst ProductList = () => {\n const navigate = useNavigate({ from: Route.fullPath })\n\n return (\n \u003cdiv>\n \u003cbutton\n onClick={() => {\n navigate({\n search: (prev) => ({ page: prev.page + 1 }),\n })\n }}\n >\n Next Page\n \u003c/button>\n \u003c/div>\n )\n}\n```\n\n### `router.navigate({ search })`\n\nThe `router.navigate` function works exactly the same way as the `useNavigate`/`navigate` hook/function above.\n\n### `\u003cNavigate search />`\n\nThe `\u003cNavigate search />` component works exactly the same way as the `useNavigate`/`navigate` hook/function above, but accepts its options as props instead of a function argument.\n\n## Transforming search with search middlewares\n\nWhen link hrefs are built, by default the only thing that matters for the query string part is the `search` property of a `\u003cLink>`.\n\nTanStack Router provides a way to manipulate search params before the href is generated via **search middlewares**.\nSearch middlewares are functions that transform the search parameters when generating new links for a route or its descendants.\nThey are also executed upon navigation after search validation to allow manipulation of the query string.\n\nThe following example shows how to make sure that for **every** link that is being built, the `rootValue` search param is added _if_ it is part of the current search params. If a link specifies `rootValue` inside `search`, then that value is used for building the link.\n\n```tsx\nimport { z } from 'zod'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst searchSchema = z.object({\n rootValue: z.string().optional(),\n})\n\nexport const Route = createRootRoute({\n validateSearch: zodValidator(searchSchema),\n search: {\n middlewares: [\n ({ search, next }) => {\n const result = next(search)\n return {\n rootValue: search.rootValue,\n ...result,\n }\n },\n ],\n },\n})\n```\n\nSince this specific use case is quite common, TanStack Router provides a generic implementation to retain search params via `retainSearchParams`:\n\n\n\n# React\n\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute, retainSearchParams } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst searchSchema = z.object({\n rootValue: z.string().optional(),\n})\n\nexport const Route = createRootRoute({\n validateSearch: zodValidator(searchSchema),\n search: {\n middlewares: [retainSearchParams(['rootValue'])],\n },\n})\n```\n\n# Solid\n\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute, retainSearchParams } from '@tanstack/solid-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst searchSchema = z.object({\n rootValue: z.string().optional(),\n})\n\nexport const Route = createRootRoute({\n validateSearch: zodValidator(searchSchema),\n search: {\n middlewares: [retainSearchParams(['rootValue'])],\n },\n})\n```\n\n\n\nAnother common use case is to strip out search params from links if their default value is set. TanStack Router provides a generic implementation for this use case via `stripSearchParams`:\n\n\n\n# React\n\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute, stripSearchParams } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst defaultValues = {\n one: 'abc',\n two: 'xyz',\n}\n\nconst searchSchema = z.object({\n one: z.string().default(defaultValues.one),\n two: z.string().default(defaultValues.two),\n})\n\nexport const Route = createFileRoute('/hello')({\n validateSearch: zodValidator(searchSchema),\n search: {\n // strip default values\n middlewares: [stripSearchParams(defaultValues)],\n },\n})\n```\n\n# Solid\n\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute, stripSearchParams } from '@tanstack/solid-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst defaultValues = {\n one: 'abc',\n two: 'xyz',\n}\n\nconst searchSchema = z.object({\n one: z.string().default(defaultValues.one),\n two: z.string().default(defaultValues.two),\n})\n\nexport const Route = createFileRoute('/hello')({\n validateSearch: zodValidator(searchSchema),\n search: {\n // strip default values\n middlewares: [stripSearchParams(defaultValues)],\n },\n})\n```\n\n\n\nMultiple middlewares can be chained. The following example shows how to combine both `retainSearchParams` and `stripSearchParams`.\n\n\n\n# React\n\n```tsx\nimport {\n Link,\n createFileRoute,\n retainSearchParams,\n stripSearchParams,\n} from '@tanstack/react-router'\nimport { z } from 'zod'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst defaultValues = ['foo', 'bar']\n\nexport const Route = createFileRoute('/search')({\n validateSearch: zodValidator(\n z.object({\n retainMe: z.string().optional(),\n arrayWithDefaults: z.string().array().default(defaultValues),\n required: z.string(),\n }),\n ),\n search: {\n middlewares: [\n retainSearchParams(['retainMe']),\n stripSearchParams({ arrayWithDefaults: defaultValues }),\n ],\n },\n})\n```\n\n# Solid\n\n```tsx\nimport {\n Link,\n createFileRoute,\n retainSearchParams,\n stripSearchParams,\n} from '@tanstack/solid-router'\nimport { z } from 'zod'\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nconst defaultValues = ['foo', 'bar']\n\nexport const Route = createFileRoute('/search')({\n validateSearch: zodValidator(\n z.object({\n retainMe: z.string().optional(),\n arrayWithDefaults: z.string().array().default(defaultValues),\n required: z.string(),\n }),\n ),\n search: {\n middlewares: [\n retainSearchParams(['retainMe']),\n stripSearchParams({ arrayWithDefaults: defaultValues }),\n ],\n },\n})\n```\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":24831,"content_sha256":"6f4bbeb7a4859a1c23e3b79dd3704a9c23250d98a3ebfcecef963573bf583264"},{"filename":"references/docs/router/guide/ssr.md","content":"---\nid: ssr\ntitle: SSR\n---\n\n> [!WARNING]\n> While every effort has been made to separate these APIs from changes to Tanstack Start, there are underlying shared implementations internally. Therefore these can be subject to change and should be regarded as experimental until Start reaches stable status.\n\nServer Side Rendering (SSR) is the process of rendering a component on the server and sending the HTML markup to the client. The client then hydrates the markup into a fully interactive component.\n\nThere are usually two different flavors of SSR to be considered:\n\n- Non-streaming SSR\n - The entire page is rendered on the server and sent to the client in one single HTML request, including the serialized data the application needs to hydrate on the client.\n- Streaming SSR\n - The critical first paint of the page is rendered on the server and sent to the client in one single HTML request, including the serialized data the application needs to hydrate on the client\n - The rest of the page is then streamed to the client as it is rendered on the server.\n\nThis guide will explain how to implement both flavors of SSR with TanStack Router!\n\n## Non-Streaming SSR\n\nNon-Streaming server-side rendering is the classic process of rendering the markup for your entire application page on the server and sending the completed HTML markup (and data) to the client. The client then hydrates the markup into a fully interactive application again.\n\nTo implement non-streaming SSR with TanStack Router, you will need the following utilities:\n\n\n\n# React\n\n- `RouterClient` from `@tanstack/react-router`\n - e.g. `\u003cRouterClient router={router} />`\n - Rendering this component in your client entry will render your application and also automatically implement the `Wrap` component option on `Router`\n- And, either:\n - `defaultRenderHandler` from `@tanstack/react-router`\n - This will render your application in your server entry and also automatically handle application-level hydration/dehydration and also automatically implement the RouterServer component.\n or:\n - `renderRouterToString` from `@tanstack/react-router`\n - This differs from defaultRenderHandler in that it allows you to manually specify the `Wrap` component option on `Router` together with any other providers you may need to wrap it with.\n - `RouterServer` from `@tanstack/react-router`\n - This implements the `Wrap` component option on `Router`\n\n# Solid\n\n- `RouterClient` from `@tanstack/solid-router`\n - e.g. `\u003cRouterClient router={router} />`\n - Rendering this component in your client entry will render your application and also automatically implement the `Wrap` component option on `Router`\n- And, either:\n - `defaultRenderHandler` from `@tanstack/solid-router`\n - This will render your application in your server entry and also automatically handle application-level hydration/dehydration and also automatically implement the RouterServer component.\n or:\n - `renderRouterToString` from `@tanstack/solid-router`\n - This differs from defaultRenderHandler in that it allows you to manually specify the `Wrap` component option on `Router` together with any other providers you may need to wrap it with.\n - `RouterServer` from `@tanstack/solid-router`\n - This implements the `Wrap` component option on `Router`\n\n\n\n### Automatic Server History\n\nOn the client, Router defaults to using an instance of `createBrowserHistory`, which is the preferred type of history to use on the client. On the server, however, you will want to use an instance of `createMemoryHistory` instead. This is because `createBrowserHistory` uses the `window` object, which does not exist on the server. This is handled automatically for you in the RouterServer component.\n\n### Automatic Loader Dehydration/Hydration\n\nResolved loader data fetched by routes is automatically dehydrated and rehydrated by TanStack Router so long as you complete the standard SSR steps outlined in this guide.\n\n If you are using deferred data streaming, you will also need to ensure that you have implemented the [SSR Streaming & Stream Transform](#streaming-ssr) pattern near the end of this guide.\n\nFor more information on how to utilize data loading, see the [Data Loading](./data-loading.md) guide.\n\n### Router Creation\n\nSince your router will exist both on the server and the client, it's important that you create your router in a way that is consistent between both of these environments. The easiest way to do this is to expose a `createRouter` function in a shared file that can be imported and called by both your server and client entry files.\n\n\n\n# React\n\n```tsx title='src/router.tsx'\nimport { createRouter as createTanstackRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n\nexport function createRouter() {\n return createTanstackRouter({ routeTree })\n}\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: ReturnType\u003ctypeof createRouter>\n }\n}\n```\n\n# Solid\n\n```tsx title='src/router.tsx'\nimport { createRouter as createTanstackRouter } from '@tanstack/solid-router'\nimport { routeTree } from './routeTree.gen'\n\nexport function createRouter() {\n return createTanstackRouter({ routeTree })\n}\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: ReturnType\u003ctypeof createRouter>\n }\n}\n```\n\n\n\n### Rendering the Application on the Server\n\nNow that you have a router instance that has loaded all the critical data for the current URL, you can render your application on the server:\n\nusing `defaultRenderHandler`\n\n\n\n# React\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n defaultRenderHandler,\n} from '@tanstack/react-router/ssr/server'\nimport { createRouter } from './router'\n\nexport async function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return await handler(defaultRenderHandler)\n}\n```\n\n# Solid\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n defaultRenderHandler,\n} from '@tanstack/solid-router/ssr/server'\nimport { createRouter } from './router'\n\nexport async function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return await handler(defaultRenderHandler)\n}\n```\n\n\n\nusing `renderRouterToString`\n\n\n\n# React\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n renderRouterToString,\n RouterServer,\n} from '@tanstack/react-router/ssr/server'\nimport { createRouter } from './router'\n\nexport function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return handler(({ request, responseHeaders, router }) =>\n renderRouterToString({\n request,\n responseHeaders,\n router,\n children: \u003cRouterServer router={router} />,\n }),\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n renderRouterToString,\n RouterServer,\n} from '@tanstack/react-router/ssr/server'\nimport { createRouter } from './router'\n\nexport function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return handler(({ request, responseHeaders, router }) =>\n renderRouterToString({\n request,\n responseHeaders,\n router,\n children: \u003cRouterServer router={router} />,\n }),\n )\n}\n```\n\n\n\nNOTE: The createRequestHandler method requires a web api standard Request object, while the handler method will return a web api standard Response promise.\n\nShould you be using a server framework like Express that uses its own Request and Response objects you would need to convert from the one to the other. Please have a look at the examples for how such an implementation might look like.\n\n## Rendering the Application on the Client\n\nOn the client, things are much simpler.\n\n- Create your router instance\n- Render your application using the `\u003cRouterClient />` component\n\n\n\n# React\n\n```tsx title=\"src/entry-client.tsx\"\nimport { hydrateRoot } from 'react-dom/client'\nimport { RouterClient } from '@tanstack/react-router/ssr/client'\nimport { createRouter } from './router'\n\nconst router = createRouter()\n\nhydrateRoot(document, \u003cRouterClient router={router} />)\n```\n\n# Solid\n\n```tsx title=\"src/entry-client.tsx\"\nimport { hydrate } from 'solid-js/web'\nimport { RouterClient } from '@tanstack/solid-router/ssr/client'\nimport { createRouter } from './router'\n\nconst router = createRouter()\n\nhydrate(() => \u003cRouterClient router={router} />, document.body)\n```\n\n\n\nWith this setup, your application will be rendered on the server and then hydrated on the client!\n\n## Streaming SSR\n\nStreaming SSR is the most modern flavor of SSR and is the process of continuously and incrementally sending HTML markup to the client as it is rendered on the server. This is slightly different from traditional SSR in concept because beyond being able to dehydrate and rehydrate a critical first paint, markup and data with lower priority or slower response times can be streamed to the client after the initial render, but in the same request.\n\nThis pattern can be useful for pages that have slow or high-latency data fetching requirements. For example, if you have a page that needs to fetch data from a third-party API, you can stream the critical initial markup and data to the client and then stream the less-critical third-party data to the client as it is resolved.\n\n> [!NOTE]\n> This streaming pattern is all automatic as long as you are using either `defaultStreamHandler` or `renderRouterToStream`.\n\nusing `defaultStreamHandler`\n\n\n\n# React\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n defaultStreamHandler,\n} from '@tanstack/react-router/ssr/server'\nimport { createRouter } from './router'\n\nexport async function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return await handler(defaultStreamHandler)\n}\n```\n\n# Solid\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n defaultStreamHandler,\n} from '@tanstack/solid-router/ssr/server'\nimport { createRouter } from './router'\n\nexport async function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return await handler(defaultStreamHandler)\n}\n```\n\n\n\nusing `renderRouterToStream`\n\n\n\n# React\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n renderRouterToStream,\n RouterServer,\n} from '@tanstack/react-router/ssr/server'\nimport { createRouter } from './router'\n\nexport function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return handler(({ request, responseHeaders, router }) =>\n renderRouterToStream({\n request,\n responseHeaders,\n router,\n children: \u003cRouterServer router={router} />,\n }),\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/entry-server.tsx\"\nimport {\n createRequestHandler,\n renderRouterToStream,\n RouterServer,\n} from '@tanstack/solid-router/ssr/server'\nimport { createRouter } from './router'\n\nexport function render({ request }: { request: Request }) {\n const handler = createRequestHandler({ request, createRouter })\n\n return handler(({ request, responseHeaders, router }) =>\n renderRouterToStream({\n request,\n responseHeaders,\n router,\n children: \u003cRouterServer router={router} />,\n }),\n )\n}\n```\n\n\n\n## Streaming Dehydration/Hydration\n\nStreaming dehydration/hydration is an advanced pattern that goes beyond markup and allows you to dehydrate and stream any supporting data from the server to the client and rehydrate it on arrival. This is useful for applications that may need to further use/manage the underlying data that was used to render the initial markup on the server.\n\n## Data Serialization\n\nWhen using SSR, data passed between the server and the client must be serialized before it is sent across network-boundaries. TanStack Router handles this serialization using a very lightweight serializer that supports common data types beyond JSON.stringify/JSON.parse.\n\nOut of the box, the following types are supported:\n\n- `undefined`\n- `Date`\n- `Error`\n- `FormData`\n\nIf you feel that there are other types that should be supported by default, please open an issue on the TanStack Router repository.\n\nIf you are using more complex data types like `Map`, `Set`, `BigInt`, etc, you may need to use a custom serializer to ensure that your type-definitions are accurate and your data is correctly serialized and deserialized. We are currently working on both a more robust serializer and a way to customize the serializer for your application. Open an issue if you are interested in helping out!\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12770,"content_sha256":"5cea2ccf7822fcff73eedf2db38f5215394471bcc5451f8e301c24b64611e5b3"},{"filename":"references/docs/router/guide/static-route-data.md","content":"---\ntitle: Static Route Data\n---\n\nWhen creating routes, you can optionally specify a `staticData` property in the route's options. This object can literally contain anything you want as long as it's synchronously available when you create your route.\n\nIn addition to being able to access this data from the route itself, you can also access it from any match under the `match.staticData` property.\n\n## Example\n\n\n\n# React\n\n\n\n```tsx title='src/routes/posts.tsx'\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts')({\n staticData: {\n customData: 'Hello!',\n },\n})\n```\n\n\n\nYou can then access this data anywhere you have access to your routes, including matches that can be mapped back to their routes.\n\n\n\n```tsx title='src/routes/__root.tsx'\nimport { createRootRoute } from '@tanstack/react-router'\n\nexport const Route = createRootRoute({\n component: () => {\n const matches = useMatches()\n\n return (\n \u003cdiv>\n {matches.map((match) => {\n return \u003cdiv key={match.id}>{match.staticData.customData}\u003c/div>\n })}\n \u003c/div>\n )\n },\n})\n```\n\n\n\n# Solid\n\n\n\n```tsx title='src/routes/posts.tsx'\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/posts')({\n staticData: {\n customData: 'Hello!',\n },\n})\n```\n\n\n\nYou can then access this data anywhere you have access to your routes, including matches that can be mapped back to their routes.\n\n\n\n```tsx title='src/routes/__root.tsx'\nimport { createRootRoute, useMatches } from '@tanstack/solid-router'\nimport { For } from 'solid-js'\n\nexport const Route = createRootRoute({\n component: () => {\n const matches = useMatches()\n\n return (\n \u003cdiv>\n \u003cFor each={matches()}>\n {(match) => \u003cdiv>{match.staticData.customData}\u003c/div>}\n \u003c/For>\n \u003c/div>\n )\n },\n})\n```\n\n\n\n\n\n## Enforcing Static Data\n\nIf you want to enforce that a route has static data, you can use declaration merging to add a type to the route's static option:\n\n\n\n# React\n\n```tsx\ndeclare module '@tanstack/react-router' {\n interface StaticDataRouteOption {\n customData: string\n }\n}\n```\n\n# Solid\n\n```tsx\ndeclare module '@tanstack/solid-router' {\n interface StaticDataRouteOption {\n customData: string\n }\n}\n```\n\n\n\nNow, if you try to create a route without the `customData` property, you'll get a type error:\n\n```tsx\nexport const Route = createFileRoute('/posts')({\n staticData: {\n // Property 'customData' is missing in type '{ customData: number; }' but required in type 'StaticDataRouteOption'.ts(2741)\n },\n})\n```\n\n## Optional Static Data\n\nIf you want to make static data optional, simply add a `?` to the property:\n\n\n\n# React\n\n```tsx\ndeclare module '@tanstack/react-router' {\n interface StaticDataRouteOption {\n customData?: string\n }\n}\n```\n\n# Solid\n\n```tsx\ndeclare module '@tanstack/solid-router' {\n interface StaticDataRouteOption {\n customData?: string\n }\n}\n```\n\n\n\nAs long as there are any required properties on the `StaticDataRouteOption`, you'll be required to pass in an object.\n\n## Common Patterns\n\n### Controlling Layout Visibility\n\nUse staticData to control which routes show or hide layout elements:\n\n\n\n```tsx title='src/routes/admin/route.tsx'\nexport const Route = createFileRoute('/admin')({\n staticData: { showNavbar: false },\n component: AdminLayout,\n})\n```\n\n\n\n\n\n```tsx\n// routes/__root.tsx\nfunction RootComponent() {\n const showNavbar = useMatches({\n select: (matches) =>\n !matches.some((m) => m.staticData?.showNavbar === false),\n })\n\n return showNavbar ? (\n \u003cNavbar>\n \u003cOutlet />\n \u003c/Navbar>\n ) : (\n \u003cOutlet />\n )\n}\n```\n\n\n\n### Route Titles for Breadcrumbs\n\n\n\n# React\n\n\n\n```tsx title='src/routes/posts/$postId.tsx'\nexport const Route = createFileRoute('/posts/$postId')({\n staticData: {\n getTitle: () => 'Post Details',\n },\n})\n```\n\n\n\n\n\n```tsx title='src/components/Breadcrumbs.tsx'\nfunction Breadcrumbs() {\n const matches = useMatches()\n\n return (\n \u003cnav>\n {matches\n .filter((m) => m.staticData?.getTitle)\n .map((m) => (\n \u003cspan key={m.id}>{m.staticData.getTitle()}\u003c/span>\n ))}\n \u003c/nav>\n )\n}\n```\n\n\n\n# Solid\n\n\n\n```tsx title='src/routes/posts/$postId.tsx'\nexport const Route = createFileRoute('/posts/$postId')({\n staticData: {\n getTitle: () => 'Post Details',\n },\n})\n```\n\n\n\n\n\n```tsx title='src/components/Breadcrumbs.tsx'\nimport { useMatches } from '@tanstack/solid-router'\nimport { For } from 'solid-js'\n\nfunction Breadcrumbs() {\n const matches = useMatches()\n\n return (\n \u003cnav>\n \u003cFor each={matches().filter((m) => m.staticData?.getTitle)}>\n {(m) => \u003cspan>{m.staticData.getTitle()}\u003c/span>}\n \u003c/For>\n \u003c/nav>\n )\n}\n```\n\n\n\n\n\n### When to Use staticData vs Context\n\n| staticData | context |\n| -------------------------------------- | ------------------------------- |\n| Synchronous, defined at route creation | Can be async (via `beforeLoad`) |\n| Available before loading starts | Can depend on params/search |\n| Same for all instances of a route | Passed down to child routes |\n\nUse staticData for static route metadata. Use context for dynamic data or auth state that varies per request.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5266,"content_sha256":"2aa92fd924d7c11899addb6deb4f85cf64e134df8c6c70e2af075b7109cd05ce"},{"filename":"references/docs/router/guide/type-safety.md","content":"---\ntitle: Type Safety\n---\n\nTanStack Router is built to be as type-safe as possible within the limits of the TypeScript compiler and runtime. This means that it's not only written in TypeScript, but that it also **fully infers the types it's provided and tenaciously pipes them through the entire routing experience**.\n\nUltimately, this means that you write **less types as a developer** and have **more confidence in your code** as it evolves.\n\n## Route Definitions\n\n### File-based Routing\n\nRoutes are hierarchical, and so are their definitions. If you're using file-based routing, much of the type-safety is already taken care of for you.\n\n### Code-based Routing\n\nIf you're using the `Route` class directly, you'll need to be aware of how to ensure your routes are typed properly using the `Route`'s `getParentRoute` option. This is because child routes need to be aware of **all** of their parent routes types. Without this, those precious search params you parsed out of your _layout_ and _pathless layout_ routes, 3 levels up, would be lost to the JS void.\n\nSo, don't forget to pass the parent route to your child routes!\n\n```tsx\nconst parentRoute = createRoute({\n getParentRoute: () => parentRoute,\n})\n```\n\n## Exported Hooks, Components, and Utilities\n\nFor the types of your router to work with top-level exports like `Link`, `useNavigate`, `useParams`, etc. they must permeate the TypeScript module boundary and be registered right into the library. To do this, we use declaration merging on the exported `Register` interface.\n\n\n\n# React\n\n```ts\nconst router = createRouter({\n // ...\n})\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n```\n\n# Solid\n\n```ts\nconst router = createRouter({\n // ...\n})\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: typeof router\n }\n}\n```\n\n\n\nBy registering your router with the module, you can now use the exported hooks, components, and utilities with your router's exact types.\n\n## Fixing the Component Context Problem\n\nComponent context is a wonderful tool in React and other frameworks for providing dependencies to components. However, if that context is changing types as it moves throughout your component hierarchy, it becomes impossible for TypeScript to know how to infer those changes. To get around this, context-based hooks and components require that you give them a hint on how and where they are being used.\n\n```tsx\nexport const Route = createFileRoute('/posts')({\n component: PostsComponent,\n})\n\nfunction PostsComponent() {\n // Each route has type-safe versions of most of the built-in hooks from TanStack Router\n const params = Route.useParams()\n const search = Route.useSearch()\n\n // Some hooks require context from the *entire* router, not just the current route. To achieve type-safety here,\n // we must pass the `from` param to tell the hook our relative position in the route hierarchy.\n const navigate = useNavigate({ from: Route.fullPath })\n // ... etc\n}\n```\n\nEvery hook and component that requires a context hint will have a `from` param where you can pass the ID or path of the route you are rendering within.\n\n> Quick tip: If your component is code-split, you can use the [getRouteApi function](./code-splitting.md#manually-accessing-route-apis-in-other-files-with-the-getrouteapi-helper) to avoid having to pass in the `Route.fullPath` to get access to the typed `useParams()` and `useSearch()` hooks.\n\n### What if I don't know the route? What if it's a shared component?\n\nThe `from` property is optional, which means if you don't pass it, you'll get the router's best guess on what types will be available. Usually, that means you'll get a union of all of the types of all of the routes in the router.\n\n### What if I pass the wrong `from` path?\n\nIt's technically possible to pass a `from` that satisfies TypeScript, but may not match the actual route you are rendering within at runtime. In this case, each hook and component that supports `from` will detect if your expectations don't match the actual route you are rendering within, and will throw a runtime error.\n\n### What if I don't know the route, or it's a shared component, and I can't pass `from`?\n\nIf you are rendering a component that is shared across multiple routes, or you are rendering a component that is not within a route, you can pass `strict: false` instead of a `from` option. This will not only silence the runtime error, but will also give you relaxed, but accurate types for the potential hook you are calling. A good example of this is calling `useSearch` from a shared component:\n\n```tsx\nfunction MyComponent() {\n const search = useSearch({ strict: false })\n}\n```\n\nIn this case, the `search` variable will be typed as a union of all possible search params from all routes in the router.\n\n## Router Context\n\nRouter context is so extremely useful as it's the ultimate hierarchical dependency injection. You can supply context to the router and to each and every route it renders. As you build up this context, TanStack Router will merge it down with the hierarchy of routes, so that each route has access to the context of all of its parents.\n\nThe `createRootRouteWithContext` factory creates a new router with the instantiated type, which then creates a requirement for you to fulfill the same type contract to your router, and will also ensure that your context is properly typed throughout the entire route tree.\n\n```tsx\nconst rootRoute = createRootRouteWithContext\u003c{ whateverYouWant: true }>()({\n component: App,\n})\n\nconst routeTree = rootRoute.addChildren([\n // ... all child routes will have access to `whateverYouWant` in their context\n])\n\nconst router = createRouter({\n routeTree,\n context: {\n // This will be required to be passed now\n whateverYouWant: true,\n },\n})\n```\n\n## Performance Recommendations\n\nAs your application scales, TypeScript check times will naturally increase. There are a few things to keep in mind when your application scales to keep your TS check times down.\n\n### Only infer types you need\n\nA great pattern with client side data caches (TanStack Query, etc.) is to prefetch data. For example with TanStack Query you might have a route which calls `queryClient.ensureQueryData` in a `loader`.\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId/deep')({\n loader: ({ context: { queryClient }, params: { postId } }) =>\n queryClient.ensureQueryData(postQueryOptions(postId)),\n component: PostDeepComponent,\n})\n\nfunction PostDeepComponent() {\n const params = Route.useParams()\n const data = useSuspenseQuery(postQueryOptions(params.postId))\n\n return \u003c>\u003c/>\n}\n```\n\nThis may look fine and for small route trees and you may not notice any TS performance issues. However in this case TS has to infer the loader's return type, despite it never being used in your route. If the loader data is a complex type with many routes that prefetch in this manner, it can slow down editor performance. In this case, the change is quite simple and let typescript infer Promise\u003cvoid>.\n\n```tsx\nexport const Route = createFileRoute('/posts/$postId/deep')({\n loader: async ({ context: { queryClient }, params: { postId } }) => {\n await queryClient.ensureQueryData(postQueryOptions(postId))\n },\n component: PostDeepComponent,\n})\n\nfunction PostDeepComponent() {\n const params = Route.useParams()\n const data = useSuspenseQuery(postQueryOptions(params.postId))\n\n return \u003c>\u003c/>\n}\n```\n\nThis way the loader data is never inferred and it moves the inference out of the route tree to the first time you use `useSuspenseQuery`.\n\n### Narrow to relevant routes as much as you possibly can\n\nConsider the following usage of `Link`\n\n```tsx\n\u003cLink to=\"..\" search={{ page: 0 }} />\n\u003cLink to=\".\" search={{ page: 0 }} />\n```\n\n**These examples are bad for TS performance**. That's because `search` resolves to a union of all `search` params for all routes and TS has to check whatever you pass to the `search` prop against this potentially big union. As your application grows, this check time will increase linearly to number of routes and search params. We have done our best to optimize for this case (TypeScript will typically do this work once and cache it) but the initial check against this large union is expensive. This also applies to `params` and other API's such as `useSearch`, `useParams`, `useNavigate` etc.\n\nInstead you should try to narrow to relevant routes with `from` or `to`.\n\n```tsx\n\u003cLink from={Route.fullPath} to=\"..\" search={{page: 0}} />\n\u003cLink from=\"/posts\" to=\"..\" search={{page: 0}} />\n```\n\nRemember you can always pass a union to `to` or `from` to narrow the routes you're interested in.\n\n```tsx\nconst from: '/posts/$postId/deep' | '/posts/' = '/posts/'\n\u003cLink from={from} to='..' />\n```\n\nYou can also pass branches to `from` to only resolve `search` or `params` to be from any descendants of that branch:\n\n```tsx\nconst from = '/posts'\n\u003cLink from={from} to='..' />\n```\n\n`/posts` could be a branch with many descendants which share the same `search` or `params`\n\n### Consider using the object syntax of `addChildren`\n\nIt's typical of routes to have `params` `search`, `loaders` or `context` that can even reference external dependencies which are also heavy on TS inference. For such applications, using objects for creating the route tree can be more performant than tuples.\n\n`createChildren` also can accept an object. For large route trees with complex routes and external libraries, objects can be much faster for TS to type check as opposed to large tuples. The performance gains depend on your project, what external dependencies you have and how the types for those libraries are written\n\n```tsx\nconst routeTree = rootRoute.addChildren({\n postsRoute: postsRoute.addChildren({ postRoute, postsIndexRoute }),\n indexRoute,\n})\n```\n\nNote this syntax is more verbose but has better TS performance. With file based routing, the route tree is generated for you so a verbose route tree is not a concern\n\n### Avoid internal types without narrowing\n\nIt's common you might want to re-use types exposed. For example you might be tempted to use `LinkProps` like so\n\n```tsx\nconst props: LinkProps = {\n to: '/posts/',\n}\n\nreturn (\n \u003cLink {...props}>\n)\n```\n\n**This is VERY bad for TS Performance**. The problem here is `LinkProps` has no type arguments and is therefore an extremely large type. It includes `search` which is a union of all `search` params, it contains `params` which is a union of all `params`. When merging this object with `Link` it will do a structural comparison of this huge type.\n\nInstead you can use `as const satisfies` to infer a precise type and not `LinkProps` directly to avoid the huge check\n\n```tsx\nconst props = {\n to: '/posts/',\n} as const satisfies LinkProps\n\nreturn (\n \u003cLink {...props}>\n)\n```\n\nAs `props` is not of type `LinkProps` and therefore this check is cheaper because the type is much more precise. You can also improve type checking further by narrowing `LinkProps`\n\n```tsx\nconst props = {\n to: '/posts/',\n} as const satisfies LinkProps\u003cRegisteredRouter, string '/posts/'>\n\nreturn (\n \u003cLink {...props}>\n)\n```\n\nThis is even faster as we're checking against the narrowed `LinkProps` type.\n\nYou can also use this to narrow the type of `LinkProps` to a specific type to be used as a prop or parameter to a function\n\n```tsx\nexport const myLinkProps = [\n {\n to: '/posts',\n },\n {\n to: '/posts/$postId',\n params: { postId: 'postId' },\n },\n] as const satisfies ReadonlyArray\u003cLinkProps>\n\nexport type MyLinkProps = (typeof myLinkProps)[number]\n\nconst MyComponent = (props: { linkProps: MyLinkProps }) => {\n return \u003cLink {...props.linkProps} />\n}\n```\n\nThis is faster than using `LinkProps` directly in a component because `MyLinkProps` is a much more precise type\n\nAnother solution is not to use `LinkProps` and to provide inversion of control to render a `Link` component narrowed to a specific route. Render props are a good method of inverting control to the user of a component\n\n```tsx\nexport interface MyComponentProps {\n readonly renderLink: () => React.ReactNode\n}\n\nconst MyComponent = (props: MyComponentProps) => {\n return \u003cdiv>{props.renderLink()}\u003c/div>\n}\n\nconst Page = () => {\n return \u003cMyComponent renderLink={() => \u003cLink to=\"/absolute\" />} />\n}\n```\n\nThis particular example is very fast as we've inverted control of where we're navigating to the user of the component. The `Link` is narrowed to the exact route\nwe want to navigate to\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12468,"content_sha256":"fc0e380d3b50cfc8574b21204337c898d3330a2e290921e9a9f71c1c79205de4"},{"filename":"references/docs/router/guide/type-utilities.md","content":"---\nid: type-utilities\ntitle: Type Utilities\n---\n\nMost types exposed by TanStack Router are internal, subject to breaking changes and not always easy to use. That is why TanStack Router has a subset of exposed types focused on ease of use with the intension to be used externally. These types provide the same type safe experience from TanStack Router's runtime concepts on the type level, with flexibility of where to provide type checking\n\n## Type checking Link options with `ValidateLinkOptions`\n\n`ValidateLinkOptions` type checks object literal types to ensure they conform to `Link` options at inference sites. For example, you may have a generic `HeadingLink` component which accepts a `title` prop along with `linkOptions`, the idea being this component can be re-used for any navigation.\n\n\n\n# React\n\n```tsx\nexport interface HeaderLinkProps\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TOptions = unknown,\n> {\n title: string\n linkOptions: ValidateLinkOptions\u003cTRouter, TOptions>\n}\n\nexport function HeadingLink\u003cTRouter extends RegisteredRouter, TOptions>(\n props: HeaderLinkProps\u003cTRouter, TOptions>,\n): React.ReactNode\nexport function HeadingLink(props: HeaderLinkProps): React.ReactNode {\n return (\n \u003c>\n \u003ch1>{props.title}\u003c/h1>\n \u003cLink {...props.linkOptions} />\n \u003c/>\n )\n}\n```\n\n# Solid\n\n```tsx\nexport interface HeaderLinkProps\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TOptions = unknown,\n> {\n title: string\n linkOptions: ValidateLinkOptions\u003cTRouter, TOptions>\n}\n\nexport function HeadingLink\u003cTRouter extends RegisteredRouter, TOptions>(\n props: HeaderLinkProps\u003cTRouter, TOptions>,\n): Solid.JSX.Element\nexport function HeadingLink(props: HeaderLinkProps): Solid.JSX.Element {\n return (\n \u003c>\n \u003ch1>{props.title}\u003c/h1>\n \u003cLink {...props.linkOptions} />\n \u003c/>\n )\n}\n```\n\n\n\nA more permissive overload of `HeadingLink` is used to avoid type assertions you would otherwise have to do with the generic signature. Using a looser signature without type parameters is an easy way to avoid type assertions in the implementation of `HeadingLink`\n\nAll type parameters for utilities are optional but for the best TypeScript performance `TRouter` should always be specified for the public facing signature. And `TOptions` should always be used at inference sites like `HeadingLink` to infer the `linkOptions` to correctly narrow `params` and `search`\n\nThe result of this is that `linkOptions` in the following is completely type-safe\n\n```tsx\n\u003cHeadingLink title=\"Posts\" linkOptions={{ to: '/posts' }} />\n\u003cHeadingLink title=\"Post\" linkOptions={{ to: '/posts/$postId', params: {postId: 'postId'} }} />\n```\n\n## Type checking an array of Link options with `ValidateLinkOptionsArray`\n\nAll navigation type utilities have an array variant. `ValidateLinkOptionsArray` enables type checking of an array of `Link` options. For example, you might have a generic `Menu` component where each item is a `Link`.\n\n\n\n# React\n\n```tsx\nexport interface MenuProps\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown> = ReadonlyArray\u003cunknown>,\n> {\n items: ValidateLinkOptionsArray\u003cTRouter, TItems>\n}\n\nexport function Menu\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown>,\n>(props: MenuProps\u003cTRouter, TItems>): React.ReactNode\nexport function Menu(props: MenuProps): React.ReactNode {\n return (\n \u003cul>\n {props.items.map((item) => (\n \u003cli>\n \u003cLink {...item} />\n \u003c/li>\n ))}\n \u003c/ul>\n )\n}\n```\n\n# Solid\n\n```tsx\nimport { For } from 'solid-js'\n\nexport interface MenuProps\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown> = ReadonlyArray\u003cunknown>,\n> {\n items: ValidateLinkOptionsArray\u003cTRouter, TItems>\n}\n\nexport function Menu\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown>,\n>(props: MenuProps\u003cTRouter, TItems>): Solid.JSX.Element\nexport function Menu(props: MenuProps): Solid.JSX.Element {\n return (\n \u003cul>\n \u003cFor each={props.items}>\n {(item) => (\n \u003cli>\n \u003cLink {...item} />\n \u003c/li>\n )}\n \u003c/For>\n \u003c/ul>\n )\n}\n```\n\n\n\nThis of course allows the following `items` prop to be completely type-safe\n\n```tsx\n\u003cMenu\n items={[\n { to: '/posts' },\n { to: '/posts/$postId', params: { postId: 'postId' } },\n ]}\n/>\n```\n\nIt is also possible to fix `from` for each `Link` options in the array. This would allow all `Menu` items to navigate relative to `from`. Additional type checking of `from` can be provided by the `ValidateFromPath` utility\n\n\n\n# React\n\n```ts\nexport interface MenuProps\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown> = ReadonlyArray\u003cunknown>,\n TFrom extends string = string,\n> {\n from: ValidateFromPath\u003cTRouter, TFrom>\n items: ValidateLinkOptionsArray\u003cTRouter, TItems, TFrom>\n}\n\nexport function Menu\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown>,\n TFrom extends string = string,\n>(props: MenuProps\u003cTRouter, TItems, TFrom>): React.ReactNode\nexport function Menu(props: MenuProps): React.ReactNode {\n return (\n \u003cul>\n {props.items.map((item) => (\n \u003cli>\n \u003cLink {...item} from={props.from} />\n \u003c/li>\n ))}\n \u003c/ul>\n )\n}\n```\n\n# Solid\n\n```ts\nimport { For } from 'solid-js'\n\nexport interface MenuProps\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown> = ReadonlyArray\u003cunknown>,\n TFrom extends string = string,\n> {\n from: ValidateFromPath\u003cTRouter, TFrom>\n items: ValidateLinkOptionsArray\u003cTRouter, TItems, TFrom>\n}\n\nexport function Menu\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TItems extends ReadonlyArray\u003cunknown>,\n TFrom extends string = string,\n>(props: MenuProps\u003cTRouter, TItems, TFrom>): Solid.JSX.Element\nexport function Menu(props: MenuProps): Solid.JSX.Element {\n return (\n \u003cul>\n \u003cFor each={props.items}>\n {(item) => (\n \u003cli>\n \u003cLink {...item} from={props.from} />\n \u003c/li>\n )}\n \u003c/For>\n \u003c/ul>\n )\n}\n```\n\n\n\n`ValidateLinkOptionsArray` allows you to fix `from` by providing an extra type parameter. The result is a type safe array of `Link` options providing navigation relative to `from`\n\n```tsx\n\u003cMenu\n from=\"/posts\"\n items={[{ to: '.' }, { to: './$postId', params: { postId: 'postId' } }]}\n/>\n```\n\n## Type checking redirect options with `ValidateRedirectOptions`\n\n`ValidateRedirectOptions` type checks object literal types to ensure they conform to redirect options at inference sites. For example, you may need a generic `fetchOrRedirect` function which accepts a `url` along with `redirectOptions`, the idea being this function will redirect when the `fetch` fails.\n\n```tsx\nexport async function fetchOrRedirect\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TOptions,\n>(\n url: string,\n redirectOptions: ValidateRedirectOptions\u003cTRouter, TOptions>,\n): Promise\u003cunknown>\nexport async function fetchOrRedirect(\n url: string,\n redirectOptions: ValidateRedirectOptions,\n): Promise\u003cunknown> {\n const response = await fetch(url)\n\n if (!response.ok && response.status === 401) {\n throw redirect(redirectOptions)\n }\n\n return await response.json()\n}\n```\n\nThe result is that `redirectOptions` passed to `fetchOrRedirect` is completely type-safe\n\n```tsx\nfetchOrRedirect('http://example.com/', { to: '/login' })\n```\n\n## Type checking navigate options with `ValidateNavigateOptions`\n\n`ValidateNavigateOptions` type checks object literal types to ensure they conform to navigate options at inference sites. For example, you may want to write a custom hook to enable/disable navigation.\n\n\n\n# React\n\n```tsx\nexport interface UseConditionalNavigateResult {\n enable: () => void\n disable: () => void\n navigate: () => void\n}\n\nexport function useConditionalNavigate\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TOptions,\n>(\n navigateOptions: ValidateNavigateOptions\u003cTRouter, TOptions>,\n): UseConditionalNavigateResult\nexport function useConditionalNavigate(\n navigateOptions: ValidateNavigateOptions,\n): UseConditionalNavigateResult {\n const [enabled, setEnabled] = useState(false)\n const navigate = useNavigate()\n return {\n enable: () => setEnabled(true),\n disable: () => setEnabled(false),\n navigate: () => {\n if (enabled) {\n navigate(navigateOptions)\n }\n },\n }\n}\n```\n\n# Solid\n\n```tsx\nimport { createSignal } from 'solid-js'\n\nexport interface UseConditionalNavigateResult {\n enable: () => void\n disable: () => void\n navigate: () => void\n}\n\nexport function useConditionalNavigate\u003c\n TRouter extends RegisteredRouter = RegisteredRouter,\n TOptions = unknown,\n>(\n navigateOptions: ValidateNavigateOptions\u003cTRouter, TOptions>,\n): UseConditionalNavigateResult\nexport function useConditionalNavigate(\n navigateOptions: ValidateNavigateOptions,\n): UseConditionalNavigateResult {\n const [enabled, setEnabled] = createSignal(false)\n const navigate = useNavigate()\n return {\n enable: () => setEnabled(true),\n disable: () => setEnabled(false),\n navigate: () => {\n if (enabled()) {\n navigate(navigateOptions)\n }\n },\n }\n}\n```\n\n\n\nThe result of this is that `navigateOptions` passed to `useConditionalNavigate` is completely type-safe and we can enable/disable navigation based on react state\n\n```tsx\nconst { enable, disable, navigate } = useConditionalNavigate({\n to: '/posts/$postId',\n params: { postId: 'postId' },\n})\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9576,"content_sha256":"a4d46210b5fb0dc2de207c0bf3d82f7c88ce51b050afe1261745c528d19d3c79"},{"filename":"references/docs/router/guide/url-rewrites.md","content":"---\ntitle: URL Rewrites\n---\n\nURL rewrites allow you to transform URLs bidirectionally between what the browser displays and what the router interprets internally. This powerful feature enables patterns like locale prefixes, subdomain routing, legacy URL migration, and multi-tenant applications without duplicating routes or complicating your route tree.\n\n## When to Use URL Rewrites\n\nURL rewrites are useful when you need to:\n\n- **i18n locale prefixes**: Display `/en/about` in the browser but route to `/about` internally\n- **Subdomain routing**: Route `admin.example.com/users` to `/admin/users` internally\n- **Legacy URL migration**: Support old URLs like `/old-path` that map to new routes\n- **Multi-tenant applications**: Route `tenant1.example.com` to tenant-specific routes\n- **Custom URL schemes**: Transform any URL pattern to match your route structure\n\n## How URL Rewrites Work\n\nURL rewrites operate in two directions:\n\n1. **Input rewrite**: Transforms the URL **from the browser** before the router interprets it\n2. **Output rewrite**: Transforms the URL **from the router** before it's written to the browser\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│ Browser URL Bar │\n│ /en/about?q=test │\n└─────────────────────────┬───────────────────────────────────────┘\n │\n ▼ input rewrite\n┌─────────────────────────────────────────────────────────────────┐\n│ Router Internal URL │\n│ /about?q=test │\n│ │\n│ (matches routes, runs loaders) │\n└─────────────────────────┬───────────────────────────────────────┘\n │\n ▼ output rewrite\n┌─────────────────────────────────────────────────────────────────┐\n│ Browser URL Bar │\n│ /en/about?q=test │\n└─────────────────────────────────────────────────────────────────┘\n```\n\nThe router exposes two href properties on the location object:\n\n- `location.href` - The internal URL (after input rewrite)\n- `location.publicHref` - The external URL displayed in the browser (after output rewrite)\n\n## Basic Usage\n\nConfigure rewrites when creating your router:\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\n\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => {\n // Transform browser URL → router internal URL\n // Return the modified URL, a new URL, or undefined to skip\n return url\n },\n output: ({ url }) => {\n // Transform router internal URL → browser URL\n // Return the modified URL, a new URL, or undefined to skip\n return url\n },\n },\n})\n```\n\nThe `input` and `output` functions receive a `URL` object and can:\n\n- Mutate and return the same `url` object\n- Return a new `URL` instance\n- Return a full href string (will be parsed into a URL)\n- Return `undefined` to skip the rewrite\n\n## Common Patterns\n\n### Pattern 1: i18n Locale Prefix\n\nStrip locale prefixes on input and add them back on output:\n\n```tsx\nconst locales = ['en', 'fr', 'es', 'de']\nconst defaultLocale = 'en'\n\n// Get current locale (from cookie, localStorage, or detection)\nfunction getLocale() {\n return localStorage.getItem('locale') || defaultLocale\n}\n\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => {\n // Check if pathname starts with a locale prefix\n const segments = url.pathname.split('/').filter(Boolean)\n const firstSegment = segments[0]\n\n if (firstSegment && locales.includes(firstSegment)) {\n // Strip the locale prefix: /en/about → /about\n url.pathname = '/' + segments.slice(1).join('/') || '/'\n }\n return url\n },\n output: ({ url }) => {\n const locale = getLocale()\n // Add locale prefix: /about → /en/about\n if (locale !== defaultLocale || true) {\n // Always prefix, or conditionally skip default locale\n url.pathname = `/${locale}${url.pathname === '/' ? '' : url.pathname}`\n }\n return url\n },\n },\n})\n```\n\nFor production i18n, consider using a library like Paraglide that provides `localizeUrl` and `deLocalizeUrl` functions. See the [Internationalization guide](./internationalization-i18n.md) for integration details.\n\n### Pattern 2: Subdomain to Path Routing\n\nRoute subdomain requests to path-based routes:\n\n```tsx\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => {\n const subdomain = url.hostname.split('.')[0]\n\n // admin.example.com/users → /admin/users\n if (subdomain === 'admin') {\n url.pathname = '/admin' + url.pathname\n }\n // api.example.com/v1/users → /api/v1/users\n else if (subdomain === 'api') {\n url.pathname = '/api' + url.pathname\n }\n\n return url\n },\n output: ({ url }) => {\n // Reverse the transformation for link generation\n if (url.pathname.startsWith('/admin')) {\n url.hostname = 'admin.example.com'\n url.pathname = url.pathname.replace(/^\\/admin/, '') || '/'\n } else if (url.pathname.startsWith('/api')) {\n url.hostname = 'api.example.com'\n url.pathname = url.pathname.replace(/^\\/api/, '') || '/'\n }\n return url\n },\n },\n})\n```\n\n### Pattern 3: Legacy URL Migration\n\nSupport old URLs while maintaining new route structure:\n\n```tsx\nconst legacyPaths: Record\u003cstring, string> = {\n '/old-about': '/about',\n '/old-contact': '/contact',\n '/blog-posts': '/blog',\n '/user-profile': '/account/profile',\n}\n\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => {\n const newPath = legacyPaths[url.pathname]\n if (newPath) {\n url.pathname = newPath\n }\n return url\n },\n // No output rewrite needed - new URLs will be used going forward\n },\n})\n```\n\n### Pattern 4: Multi-tenant Routing\n\nRoute tenant-specific domains to a unified route structure:\n\n```tsx\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => {\n // Extract tenant from subdomain: acme.app.com → acme\n const parts = url.hostname.split('.')\n if (parts.length >= 3) {\n const tenant = parts[0]\n // Inject tenant into the path: /dashboard → /tenant/acme/dashboard\n url.pathname = `/tenant/${tenant}${url.pathname}`\n }\n return url\n },\n output: ({ url }) => {\n // Extract tenant from path and move to subdomain\n const match = url.pathname.match(/^\\/tenant\\/([^/]+)(.*)$/)\n if (match) {\n const [, tenant, rest] = match\n url.hostname = `${tenant}.app.com`\n url.pathname = rest || '/'\n }\n return url\n },\n },\n})\n```\n\n### Pattern 5: Search Parameter Transformation\n\nTransform search parameters during rewrites:\n\n```tsx\nconst router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => {\n // Convert legacy search param format\n // ?filter_status=active → ?status=active\n const filterStatus = url.searchParams.get('filter_status')\n if (filterStatus) {\n url.searchParams.delete('filter_status')\n url.searchParams.set('status', filterStatus)\n }\n return url\n },\n output: ({ url }) => {\n // Optionally transform back for external display\n return url\n },\n },\n})\n```\n\n## Composing Multiple Rewrites\n\nWhen you need multiple independent rewrite transformations, use `composeRewrites` to combine them:\n\n```tsx\nimport { composeRewrites } from '@tanstack/react-router'\n\nconst localeRewrite = {\n input: ({ url }) => {\n // Strip locale prefix\n const match = url.pathname.match(/^\\/(en|fr|es)(\\/.*)$/)\n if (match) {\n url.pathname = match[2] || '/'\n }\n return url\n },\n output: ({ url }) => {\n // Add locale prefix\n url.pathname = `/en${url.pathname === '/' ? '' : url.pathname}`\n return url\n },\n}\n\nconst legacyRewrite = {\n input: ({ url }) => {\n if (url.pathname === '/old-page') {\n url.pathname = '/new-page'\n }\n return url\n },\n}\n\nconst router = createRouter({\n routeTree,\n rewrite: composeRewrites([localeRewrite, legacyRewrite]),\n})\n```\n\n**Order of operations:**\n\n- **Input rewrites**: Execute in order (first to last)\n- **Output rewrites**: Execute in reverse order (last to first)\n\nThis ensures that composed rewrites \"unwrap\" correctly. In the example above:\n\n- Input: locale strips `/en`, then legacy redirects `/old-page`\n- Output: legacy runs first (no-op), then locale adds `/en` back\n\n## Interaction with Basepath\n\nWhen you configure a `basepath`, the router internally implements it as a rewrite. If you also provide a custom `rewrite`, they are automatically composed together:\n\n```tsx\nconst router = createRouter({\n routeTree,\n basepath: '/app',\n rewrite: {\n input: ({ url }) => {\n // This runs AFTER basepath is stripped\n // Browser: /app/en/about → After basepath: /en/about → Your rewrite: /about\n return url\n },\n output: ({ url }) => {\n // This runs BEFORE basepath is added\n // Your rewrite: /about → After your rewrite: /en/about → Basepath adds: /app/en/about\n return url\n },\n },\n})\n```\n\nThe composition order ensures:\n\n1. **Input**: Basepath stripped first, then your rewrite runs\n2. **Output**: Your rewrite runs first, then basepath added\n\n## Working with Links and Navigation\n\n### Link Component\n\nThe `\u003cLink>` component automatically applies output rewrites when generating `href` attributes:\n\n```tsx\n// With locale rewrite configured (adds /en prefix)\n\u003cLink to=\"/about\">About\u003c/Link>\n// Renders: \u003ca href=\"/en/about\">About\u003c/a>\n```\n\n### Programmatic Navigation\n\nProgrammatic navigation via `navigate()` or `router.navigate()` also respects rewrites:\n\n```tsx\nconst navigate = useNavigate()\n\n// Navigates to /about internally, displays /en/about in browser\nnavigate({ to: '/about' })\n```\n\n### Hard Links for Cross-Origin Rewrites\n\nWhen an output rewrite changes the origin (hostname), the `\u003cLink>` component automatically renders a standard anchor tag instead of using client-side navigation:\n\n```tsx\n// Rewrite that changes hostname for /admin paths\nconst router = createRouter({\n routeTree,\n rewrite: {\n output: ({ url }) => {\n if (url.pathname.startsWith('/admin')) {\n url.hostname = 'admin.example.com'\n url.pathname = url.pathname.replace(/^\\/admin/, '') || '/'\n }\n return url\n },\n },\n})\n\n// This link will be a hard navigation (full page load)\n\u003cLink to=\"/admin/dashboard\">Admin Dashboard\u003c/Link>\n// Renders: \u003ca href=\"https://admin.example.com/dashboard\">Admin Dashboard\u003c/a>\n```\n\n## The publicHref Property\n\nThe router's location object includes a `publicHref` property that contains the external URL (after output rewrite):\n\n```tsx\nfunction MyComponent() {\n const location = useLocation()\n\n // Internal URL used for routing\n console.log(location.href) // \"/about\"\n\n // External URL shown in browser\n console.log(location.publicHref) // \"/en/about\"\n\n return (\n \u003cdiv>\n {/* Use publicHref for sharing, canonical URLs, etc. */}\n \u003cShareButton url={window.location.origin + location.publicHref} />\n \u003c/div>\n )\n}\n```\n\nUse `publicHref` when you need the actual browser URL for:\n\n- Social sharing\n- Canonical URLs\n- Analytics tracking\n- Copying links to clipboard\n\n## Server-side Considerations\n\nURL rewrites apply on both client and server. When using TanStack Start:\n\n### Server Middleware\n\nRewrites are applied when parsing incoming requests:\n\n```tsx\n// router.tsx\nexport const router = createRouter({\n routeTree,\n rewrite: {\n input: ({ url }) => deLocalizeUrl(url),\n output: ({ url }) => localizeUrl(url),\n },\n})\n```\n\nThe server handler will use the same rewrite configuration to parse incoming URLs and generate responses with the correct external URLs.\n\n### SSR Hydration\n\nThe router ensures that the server-rendered HTML and client hydration use consistent URLs. The `publicHref` is serialized during SSR so the client can hydrate with the correct external URL.\n\n## API Reference\n\n### `rewrite` option\n\n- Type: [`LocationRewrite`](#locationrewrite-type)\n- Optional\n- Configures bidirectional URL transformation between browser and router.\n\n### LocationRewrite type\n\n```tsx\ntype LocationRewrite = {\n /**\n * Transform the URL before the router interprets it.\n * Called when reading from browser history.\n */\n input?: LocationRewriteFunction\n\n /**\n * Transform the URL before it's written to browser history.\n * Called when generating links and committing navigation.\n */\n output?: LocationRewriteFunction\n}\n```\n\n### LocationRewriteFunction type\n\n```tsx\ntype LocationRewriteFunction = (opts: { url: URL }) => undefined | string | URL\n```\n\n**Parameters:**\n\n- `url`: A `URL` object representing the current URL\n\n**Returns:**\n\n- `URL`: The transformed URL object (can be the same mutated object or a new instance)\n- `string`: A full href string that will be parsed into a URL\n- `undefined`: Skip the rewrite, use the original URL\n\n### composeRewrites function\n\n```tsx\nimport { composeRewrites } from '@tanstack/react-router'\n\nfunction composeRewrites(rewrites: Array\u003cLocationRewrite>): LocationRewrite\n```\n\nCombines multiple rewrite pairs into a single rewrite. Input rewrites execute in order, output rewrites execute in reverse order.\n\n**Example:**\n\n```tsx\nconst composedRewrite = composeRewrites([\n { input: rewrite1Input, output: rewrite1Output },\n { input: rewrite2Input, output: rewrite2Output },\n])\n\n// Input execution order: rewrite1Input → rewrite2Input\n// Output execution order: rewrite2Output → rewrite1Output\n```\n\n## Examples\n\nComplete working examples are available in the TanStack Router repository:\n\n- React + Paraglide (Client-side i18n)\n- React + TanStack Start + Paraglide (SSR i18n)\n- Solid + Paraglide (Client-side i18n)\n- Solid + TanStack Start + Paraglide (SSR i18n)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14962,"content_sha256":"74ffd300743f3f3696490ace7b010a9eb359de85b3a5ceeb6c43523469e6c015"},{"filename":"references/docs/router/how-to/arrays-objects-dates-search-params.md","content":"---\ntitle: Work with Arrays, Objects, and Dates in Search Parameters\n---\n\nLearn to handle arrays, objects, dates, and nested data structures in search parameters while maintaining type safety and URL compatibility.\n\n## Quick Start\n\nComplex search parameters go beyond simple strings and numbers. TanStack Router's JSON-first approach makes it easy to handle arrays, objects, dates, and nested structures:\n\n```tsx\n// Example of complex search parameters\nconst complexSearch = {\n tags: ['typescript', 'react', 'router'], // Array\n filters: {\n // Nested object\n category: 'web',\n minRating: 4.5,\n active: true,\n },\n dateRange: {\n // Date objects\n start: new Date('2024-01-01'),\n end: new Date('2024-12-31'),\n },\n pagination: {\n // Nested pagination\n page: 1,\n size: 20,\n sort: { field: 'name', direction: 'asc' },\n },\n}\n```\n\n## Working with Arrays\n\nArrays are commonly used for filters, tags, categories, and multi-select options.\n\n### Basic Array Validation\n\n```tsx\n// routes/products.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { z } from 'zod'\n\nconst searchSchema = z.object({\n categories: z.array(z.string()).default([]),\n tags: z.array(z.string()).optional(),\n priceRange: z.array(z.number()).length(2).optional(), // [min, max]\n})\n\nexport const Route = createFileRoute('/products')({\n validateSearch: searchSchema,\n component: ProductsComponent,\n})\n\nfunction ProductsComponent() {\n const { categories, tags, priceRange } = Route.useSearch()\n\n return (\n \u003cdiv>\n \u003ch2>Active Categories: {categories.join(', ')}\u003c/h2>\n {tags && \u003cp>Tags: {tags.join(', ')}\u003c/p>}\n {priceRange && (\n \u003cp>\n Price: ${priceRange[0]} - ${priceRange[1]}\n \u003c/p>\n )}\n \u003c/div>\n )\n}\n```\n\n### Navigating with Arrays\n\n```tsx\nimport { Link } from '@tanstack/react-router'\n\nfunction FilterControls() {\n return (\n \u003cdiv>\n {/* Add to existing array */}\n \u003cLink\n to=\"/products\"\n search={(prev) => ({\n ...prev,\n categories: [...(prev.categories || []), 'electronics'],\n })}\n >\n Add Electronics\n \u003c/Link>\n\n {/* Replace entire array */}\n \u003cLink to=\"/products\" search={{ categories: ['books', 'music'] }}>\n Books & Music Only\n \u003c/Link>\n\n {/* Remove from array */}\n \u003cLink\n to=\"/products\"\n search={(prev) => ({\n ...prev,\n categories:\n prev.categories?.filter((cat) => cat !== 'electronics') || [],\n })}\n >\n Remove Electronics\n \u003c/Link>\n\n {/* Clear array */}\n \u003cLink to=\"/products\" search={(prev) => ({ ...prev, categories: [] })}>\n Clear All\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\n### Advanced Array Patterns\n\n```tsx\n// routes/search.tsx\nconst advancedArraySchema = z.object({\n // Array of objects\n filters: z\n .array(\n z.object({\n field: z.string(),\n operator: z.enum(['eq', 'gt', 'lt', 'contains']),\n value: z.union([z.string(), z.number(), z.boolean()]),\n }),\n )\n .default([]),\n\n // Array with constraints\n selectedIds: z.array(z.string().uuid()).max(10).default([]),\n\n // Array with transformation\n sortFields: z\n .array(z.string())\n .transform((arr) =>\n arr.filter((field) => ['name', 'date', 'price'].includes(field)),\n )\n .default(['name']),\n})\n\nexport const Route = createFileRoute('/search')({\n validateSearch: advancedArraySchema,\n component: SearchComponent,\n})\n```\n\n## Working with Objects\n\nObjects are useful for grouped parameters, complex filters, and nested configurations.\n\n### Basic Object Validation\n\n```tsx\n// routes/dashboard.tsx\nconst dashboardSchema = z.object({\n view: z\n .object({\n layout: z.enum(['grid', 'list', 'cards']).default('grid'),\n columns: z.number().min(1).max(6).default(3),\n showDetails: z.boolean().default(false),\n })\n .default({}),\n\n filters: z\n .object({\n status: z.enum(['active', 'inactive', 'pending']).optional(),\n dateCreated: z\n .object({\n after: z.string().optional(),\n before: z.string().optional(),\n })\n .optional(),\n metadata: z.record(z.string()).optional(), // Dynamic object keys\n })\n .default({}),\n})\n\nexport const Route = createFileRoute('/dashboard')({\n validateSearch: dashboardSchema,\n component: DashboardComponent,\n})\n\nfunction DashboardComponent() {\n const { view, filters } = Route.useSearch()\n\n return (\n \u003cdiv>\n \u003cdiv className={`layout-${view.layout} columns-${view.columns}`}>\n {/* Render based on complex object state */}\n \u003c/div>\n\n {filters.status && \u003cp>Status: {filters.status}\u003c/p>}\n {filters.dateCreated?.after && (\n \u003cp>Created after: {filters.dateCreated.after}\u003c/p>\n )}\n \u003c/div>\n )\n}\n```\n\n### Navigating with Objects\n\n```tsx\nfunction ViewControls() {\n return (\n \u003cdiv>\n {/* Update nested object property */}\n \u003cLink\n to=\"/dashboard\"\n search={(prev) => ({\n ...prev,\n view: {\n ...prev.view,\n layout: 'list',\n },\n })}\n >\n List View\n \u003c/Link>\n\n {/* Update multiple nested properties */}\n \u003cLink\n to=\"/dashboard\"\n search={(prev) => ({\n ...prev,\n view: {\n ...prev.view,\n layout: 'grid',\n columns: 4,\n showDetails: true,\n },\n })}\n >\n 4-Column Grid with Details\n \u003c/Link>\n\n {/* Deep merge with library for complex updates */}\n \u003cLink\n to=\"/dashboard\"\n search={(prev) =>\n merge(prev, {\n filters: {\n dateCreated: { after: '2024-01-01' },\n },\n })\n }\n >\n Filter Recent Items\n \u003c/Link>\n \u003c/div>\n )\n}\n\n// For deep merging, use a well-tested library:\n\n// Option 1: Lodash (most popular, full-featured)\n// npm install lodash-es\n// import { merge } from 'lodash-es'\n\n// Option 2: deepmerge (lightweight, focused)\n// npm install deepmerge\n// import merge from 'deepmerge'\n\n// Option 3: Ramda (functional programming style)\n// npm install ramda\n// import { mergeDeepRight as merge } from 'ramda'\n\n// Example with deepmerge (recommended for most cases):\nimport merge from 'deepmerge'\n\n// Handles arrays intelligently - combines by default\nconst result = merge(\n { filters: { tags: ['react'] } },\n { filters: { tags: ['typescript'] } },\n)\n// Result: { filters: { tags: ['react', 'typescript'] } }\n\n// Override array merging behavior if needed\nconst overwriteResult = merge(\n { filters: { tags: ['react'] } },\n { filters: { tags: ['typescript'] } },\n { arrayMerge: (dest, source) => source }, // Overwrite instead of combine\n)\n// Result: { filters: { tags: ['typescript'] } }\n```\n\n## Working with Dates\n\nDates require special handling for URL serialization and validation.\n\n### Date Validation and Serialization\n\n```tsx\n// routes/events.tsx\nconst eventSchema = z.object({\n // ISO string dates\n startDate: z.string().datetime().optional(),\n endDate: z.string().datetime().optional(),\n\n // Date range as object\n dateRange: z\n .object({\n start: z.string().datetime(),\n end: z.string().datetime(),\n })\n .optional(),\n\n // Transform string to Date object\n selectedDate: z\n .string()\n .datetime()\n .transform((str) => new Date(str))\n .optional(),\n\n // Relative dates\n timeFilter: z.enum(['today', 'week', 'month', 'year']).default('week'),\n})\n\nexport const Route = createFileRoute('/events')({\n validateSearch: eventSchema,\n component: EventsComponent,\n})\n\nfunction EventsComponent() {\n const search = Route.useSearch()\n\n // Convert string dates back to Date objects for display\n const startDate = search.startDate ? new Date(search.startDate) : null\n const endDate = search.endDate ? new Date(search.endDate) : null\n\n return (\n \u003cdiv>\n {startDate && \u003cp>Events from: {startDate.toLocaleDateString()}\u003c/p>}\n {search.selectedDate && (\n \u003cp>Selected: {search.selectedDate.toLocaleDateString()}\u003c/p>\n )}\n \u003c/div>\n )\n}\n```\n\n### Date Navigation Patterns\n\n```tsx\nfunction DateControls() {\n const navigate = useNavigate()\n\n const setDateRange = (start: Date, end: Date) => {\n navigate({\n to: '/events',\n search: (prev) => ({\n ...prev,\n dateRange: {\n start: start.toISOString(),\n end: end.toISOString(),\n },\n }),\n })\n }\n\n const setRelativeDate = (period: string) => {\n const now = new Date()\n let start: Date\n\n switch (period) {\n case 'today':\n start = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n break\n case 'week':\n start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)\n break\n case 'month':\n start = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate())\n break\n default:\n start = now\n }\n\n setDateRange(start, now)\n }\n\n return (\n \u003cdiv>\n \u003cbutton onClick={() => setRelativeDate('today')}>Today\u003c/button>\n \u003cbutton onClick={() => setRelativeDate('week')}>Past Week\u003c/button>\n \u003cbutton onClick={() => setRelativeDate('month')}>Past Month\u003c/button>\n\n {/* Date picker integration */}\n \u003cinput\n type=\"date\"\n onChange={(e) => {\n const date = new Date(e.target.value)\n navigate({\n to: '/events',\n search: (prev) => ({\n ...prev,\n selectedDate: date.toISOString(),\n }),\n })\n }}\n />\n \u003c/div>\n )\n}\n```\n\n## Nested Data Structures\n\nComplex applications often need deeply nested search parameters.\n\n### Complex Nested Schema\n\n```tsx\n// routes/analytics.tsx\nconst analyticsSchema = z.object({\n dashboard: z\n .object({\n widgets: z\n .array(\n z.object({\n id: z.string(),\n type: z.enum(['chart', 'table', 'metric']),\n config: z.object({\n title: z.string(),\n dataSource: z.string(),\n filters: z.array(\n z.object({\n field: z.string(),\n operator: z.string(),\n value: z.any(),\n }),\n ),\n visualization: z\n .object({\n chartType: z.enum(['line', 'bar', 'pie']).optional(),\n colors: z.array(z.string()).optional(),\n axes: z\n .object({\n x: z.string(),\n y: z.array(z.string()),\n })\n .optional(),\n })\n .optional(),\n }),\n }),\n )\n .default([]),\n\n layout: z\n .object({\n columns: z.number().min(1).max(12).default(2),\n gap: z.number().default(16),\n responsive: z.boolean().default(true),\n })\n .default({}),\n\n timeRange: z\n .object({\n preset: z.enum(['1h', '24h', '7d', '30d', 'custom']).default('24h'),\n custom: z\n .object({\n start: z.string().datetime(),\n end: z.string().datetime(),\n })\n .optional(),\n })\n .default({}),\n })\n .default({}),\n})\n\nexport const Route = createFileRoute('/analytics')({\n validateSearch: analyticsSchema,\n component: AnalyticsComponent,\n})\n```\n\n### Managing Complex State Updates\n\n```tsx\nfunction AnalyticsControls() {\n const search = Route.useSearch()\n const navigate = useNavigate()\n\n // Helper to update nested widget config\n const updateWidgetConfig = (widgetId: string, configUpdate: any) => {\n navigate({\n to: '/analytics',\n search: (prev) => ({\n ...prev,\n dashboard: {\n ...prev.dashboard,\n widgets: prev.dashboard.widgets.map((widget) =>\n widget.id === widgetId\n ? {\n ...widget,\n config: { ...widget.config, ...configUpdate },\n }\n : widget,\n ),\n },\n }),\n })\n }\n\n // Helper to add new widget\n const addWidget = (widget: any) => {\n navigate({\n to: '/analytics',\n search: (prev) => ({\n ...prev,\n dashboard: {\n ...prev.dashboard,\n widgets: [...prev.dashboard.widgets, widget],\n },\n }),\n })\n }\n\n // Helper to update layout\n const updateLayout = (layoutUpdate: any) => {\n navigate({\n to: '/analytics',\n search: (prev) => ({\n ...prev,\n dashboard: {\n ...prev.dashboard,\n layout: { ...prev.dashboard.layout, ...layoutUpdate },\n },\n }),\n })\n }\n\n return (\n \u003cdiv>\n \u003cbutton onClick={() => updateLayout({ columns: 3 })}>3 Columns\u003c/button>\n\n \u003cbutton\n onClick={() =>\n addWidget({\n id: Date.now().toString(),\n type: 'chart',\n config: {\n title: 'New Chart',\n dataSource: 'default',\n filters: [],\n },\n })\n }\n >\n Add Chart Widget\n \u003c/button>\n \u003c/div>\n )\n}\n```\n\n## Performance Optimization\n\n### Selective Updates with Selectors\n\n```tsx\n// Only re-render when specific nested values change\nfunction WidgetComponent({ widgetId }: { widgetId: string }) {\n // Use selector to avoid unnecessary re-renders\n const widget = Route.useSearch({\n select: (search) => search.dashboard.widgets.find((w) => w.id === widgetId),\n })\n\n const layout = Route.useSearch({\n select: (search) => search.dashboard.layout,\n })\n\n if (!widget) return null\n\n return (\n \u003cdiv\n style={{\n gridColumn: `span ${Math.ceil(12 / layout.columns)}`,\n }}\n >\n \u003ch3>{widget.config.title}\u003c/h3>\n {/* Widget content */}\n \u003c/div>\n )\n}\n```\n\n### Memoization for Complex Transforms\n\n```tsx\nimport { useMemo } from 'react'\n\nfunction ComplexDataComponent() {\n const search = Route.useSearch()\n\n // Memoize expensive transformations\n const processedData = useMemo(() => {\n return search.dashboard.widgets\n .filter((widget) => widget.type === 'chart')\n .map((widget) => ({\n ...widget,\n computedMetrics: expensiveCalculation(widget.config),\n }))\n }, [search.dashboard.widgets])\n\n return (\n \u003cdiv>\n {processedData.map((widget) => (\n \u003cComplexChart key={widget.id} data={widget} />\n ))}\n \u003c/div>\n )\n}\n```\n\n## Production Checklist\n\n- [ ] **Array bounds validation** - Use `.min()`, `.max()`, `.length()` constraints\n- [ ] **Date format consistency** - Stick to ISO strings for URL compatibility\n- [ ] **Object depth limits** - Avoid excessively nested structures for URL length\n- [ ] **Performance testing** - Test with large arrays/objects in search params\n- [ ] **URL length limits** - Most browsers limit URLs to ~2000 characters\n- [ ] **Fallback values** - Provide sensible defaults for all complex types\n- [ ] **Type safety** - Ensure schemas match your component expectations\n- [ ] **Serialization testing** - Verify round-trip serialization works correctly\n\n## Common Problems\n\n### Problem: Array Parameters Not Updating\n\n**Symptoms:** Link clicks don't update array search parameters.\n\n**Cause:** Directly mutating arrays instead of creating new ones.\n\n**Solution:** Always create new arrays when updating:\n\n```tsx\n// ❌ Wrong - mutates existing array\nsearch={(prev) => {\n prev.categories.push('new-item')\n return prev\n}}\n\n// ✅ Correct - creates new array\nsearch={(prev) => ({\n ...prev,\n categories: [...prev.categories, 'new-item']\n})}\n```\n\n### Problem: Dates Not Serializing Correctly\n\n**Symptoms:** Date objects become `[object Object]` in URL.\n\n**Cause:** Attempting to serialize Date objects directly.\n\n**Solution:** Convert dates to ISO strings:\n\n```tsx\n// ❌ Wrong - Date objects don't serialize\nsearch={{\n startDate: new Date() // Becomes \"[object Object]\"\n}}\n\n// ✅ Correct - Use ISO strings\nsearch={{\n startDate: new Date().toISOString()\n}}\n```\n\n### Problem: Deep Object Updates Not Working\n\n**Symptoms:** Nested object properties don't update as expected.\n\n**Cause:** Shallow merging doesn't update nested properties.\n\n**Solution:** Use proper deep merging or spread operators:\n\n```tsx\n// ❌ Wrong - shallow merge loses nested properties\nsearch={(prev) => ({\n ...prev,\n filters: { category: 'new' } // Loses other filter properties\n})}\n\n// ✅ Correct - preserve nested properties\nsearch={(prev) => ({\n ...prev,\n filters: {\n ...prev.filters,\n category: 'new'\n }\n})}\n```\n\n### Problem: URL Too Long Error\n\n**Symptoms:** Browser errors with very complex search parameters.\n\n**Cause:** Exceeding browser URL length limits (~2000 characters).\n\n**Solutions:**\n\n1. **Simplify data structures** - Remove unnecessary nesting\n2. **Use compression** - Implement custom serialization\n3. **Store in session** - Keep complex state in sessionStorage with URL key\n4. **Pagination** - Break large arrays into pages\n\n```tsx\n// Option 3: Session storage approach\nconst sessionKey = Route.useSearch({ select: (s) => s.sessionKey })\nconst complexData = useMemo(() => {\n if (sessionKey) {\n return JSON.parse(sessionStorage.getItem(sessionKey) || '{}')\n }\n return {}\n}, [sessionKey])\n```\n\n### Problem: Performance Issues with Large Objects\n\n**Symptoms:** Slow navigation and re-renders with complex search parameters.\n\n**Cause:** Large objects causing expensive serialization and comparison operations.\n\n**Solutions:**\n\n1. **Use selectors** to limit re-renders\n2. **Memoize expensive calculations**\n3. **Consider alternatives** like context or state management\n\n```tsx\n// Use selector to minimize re-renders\nconst onlyNeededData = Route.useSearch({\n select: (search) => ({\n currentPage: search.pagination.page,\n pageSize: search.pagination.size,\n }),\n})\n```\n\n## Common Next Steps\n\n\n\n\n\n\n## Related Resources\n\n- [Search Params Guide](../guide/search-params.md) - Core concepts and JSON-first approach\n- Zod Documentation - Schema validation library\n- MDN Date.toISOString() - Date serialization reference\n- URLSearchParams Limits - Browser URL length limits\n\n**Deep Merging Libraries:**\n\n- deepmerge - Lightweight, focused deep merging utility\n- Lodash merge - Full-featured utility library with deep merging\n- Ramda mergeDeepRight - Functional programming approach\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18349,"content_sha256":"752048632e21aa1cf9bb4f4ec2c5c9867ad834cf1cee7ece47c1019d70114c0a"},{"filename":"references/docs/router/how-to/debug-router-issues.md","content":"---\ntitle: How to Debug Common Router Issues\n---\n\nThis guide covers debugging common TanStack Router problems, from route matching failures to navigation issues and performance problems.\n\n## Quick Start\n\nUse TanStack Router DevTools for real-time debugging, add strategic console logging, and follow systematic troubleshooting patterns to identify and resolve router issues quickly.\n\n---\n\n## Essential Debugging Tools\n\n### 1. TanStack Router DevTools\n\nInstall and configure the DevTools for the best debugging experience:\n\n```bash\nnpm install @tanstack/router-devtools\n```\n\n```tsx\n// src/App.tsx\nimport { TanStackRouterDevtools } from '@tanstack/router-devtools'\n\nfunction App() {\n return (\n \u003cdiv>\n \u003cRouterProvider router={router} />\n {/* Only shows in development */}\n \u003cTanStackRouterDevtools router={router} />\n \u003c/div>\n )\n}\n```\n\n**DevTools Features:**\n\n- **Route Tree Visualization** - See your entire route structure\n- **Current Route State** - Inspect active route data, params, and search\n- **Navigation History** - Track navigation events and timing\n- **Route Matching** - See which routes match current URL\n- **Performance Metrics** - Monitor route load times and re-renders\n\n### 2. Debug Mode Configuration\n\nEnable debug mode for detailed console logging:\n\n```tsx\nconst router = createRouter({\n routeTree,\n defaultPreload: 'intent',\n context: {\n // your context\n },\n // Enable debug mode\n debug: true,\n})\n```\n\n### 3. Browser DevTools Setup\n\n**Add router to global scope for debugging:**\n\n```tsx\n// In development only\nif (import.meta.env.DEV) {\n window.router = router\n}\n\n// Console debugging commands:\n// router.state - current router state\n// router.navigate() - programmatic navigation\n// router.history - navigation history\n```\n\n---\n\n## Route Matching Issues\n\n### Problem: Route Not Found (404)\n\n**Symptoms:**\n\n- Route exists but shows 404 or \"Not Found\"\n- Console shows route matching failures\n\n**Debugging Steps:**\n\n1. **Check Route Path Definition**\n\n```tsx\n// ❌ Common mistake - missing leading slash\nconst route = createRoute({\n path: 'about', // Should be '/about'\n // ...\n})\n\n// ✅ Correct\nconst route = createRoute({\n path: '/about',\n // ...\n})\n```\n\n2. **Verify Route Tree Structure**\n\n```tsx\n// Debug route tree in console\nconsole.log('Route tree:', router.routeTree)\nconsole.log('All routes:', router.routesById)\n```\n\n3. **Check Parent Route Configuration**\n\n```tsx\n// Ensure parent route is properly defined\nconst childRoute = createRoute({\n getParentRoute: () => parentRoute, // Must return correct parent\n path: '/child',\n // ...\n})\n```\n\n### Problem: Route Parameters Not Working\n\n**Symptoms:**\n\n- `useParams()` returns undefined or wrong values\n- Route params not being parsed correctly\n\n**Debugging Steps:**\n\n1. **Verify Parameter Syntax**\n\n```tsx\n// ❌ Wrong parameter syntax\npath: '/users/{id}' // Should use $\n\n// ✅ Correct parameter syntax\npath: '/users/$userId'\n```\n\n2. **Check Parameter Parsing**\n\n```tsx\nconst route = createRoute({\n path: '/users/$userId',\n // Add parameter validation/parsing\n params: {\n parse: (params) => ({\n userId: Number(params.userId), // Convert to number\n }),\n stringify: (params) => ({\n userId: String(params.userId), // Convert back to string\n }),\n },\n component: () => {\n const { userId } = Route.useParams()\n console.log('User ID:', userId, typeof userId) // Debug output\n return \u003cdiv>User {userId}\u003c/div>\n },\n})\n```\n\n3. **Debug Current URL and Params**\n\n```tsx\nfunction DebugParams() {\n const location = useLocation()\n const params = Route.useParams()\n\n console.log('Current pathname:', location.pathname)\n console.log('Parsed params:', params)\n\n return null // Just for debugging\n}\n```\n\n---\n\n## Navigation Issues\n\n### Problem: Navigation Not Working\n\n**Symptoms:**\n\n- Links don't navigate\n- Programmatic navigation fails silently\n- Browser URL doesn't update\n\n**Debugging Steps:**\n\n1. **Check Link Configuration**\n\n```tsx\n// ❌ Common mistakes\n\u003cLink to=\"about\">About\u003c/Link> // Missing leading slash\n\u003cLink href=\"/about\">About\u003c/Link> // Wrong prop (href instead of to)\n\n// ✅ Correct\n\u003cLink to=\"/about\">About\u003c/Link>\n```\n\n2. **Debug Navigation Calls**\n\n```tsx\nfunction NavigationDebug() {\n const navigate = useNavigate()\n\n const handleNavigate = () => {\n console.log('Attempting navigation...')\n navigate({\n to: '/dashboard',\n search: { tab: 'settings' },\n })\n .then(() => console.log('Navigation successful'))\n .catch((err) => console.error('Navigation failed:', err))\n }\n\n return \u003cbutton onClick={handleNavigate}>Navigate\u003c/button>\n}\n```\n\n3. **Check Router Context**\n\n```tsx\n// Ensure component is inside RouterProvider\nfunction ComponentWithNavigation() {\n const router = useRouter() // Will throw error if outside provider\n console.log('Router state:', router.state)\n\n return \u003cdiv>...\u003c/div>\n}\n```\n\n### Problem: Navigation Redirects Unexpectedly\n\n**Symptoms:**\n\n- Navigating to one route but ending up somewhere else\n- Infinite redirect loops\n\n**Debugging Steps:**\n\n1. **Check Route Guards**\n\n```tsx\nconst route = createRoute({\n path: '/dashboard',\n beforeLoad: ({ context, location }) => {\n console.log('Before load - location:', location.pathname)\n console.log('Auth state:', context.auth)\n\n if (!context.auth.isAuthenticated) {\n console.log('Redirecting to login...')\n throw redirect({ to: '/login' })\n }\n },\n // ...\n})\n```\n\n2. **Debug Redirect Chains**\n\n```tsx\n// Add to router configuration\nconst router = createRouter({\n routeTree,\n context: {\n /* ... */\n },\n // Log all navigation events\n onNavigate: ({ location, type }) => {\n console.log(`Navigation (${type}):`, location.pathname)\n },\n})\n```\n\n---\n\n## Data Loading Problems\n\n### Problem: Route Data Not Loading\n\n**Symptoms:**\n\n- `useLoaderData()` returns undefined\n- Loading states not working correctly\n- Data not refreshing\n\n**Debugging Steps:**\n\n1. **Check Loader Implementation**\n\n```tsx\nconst route = createRoute({\n path: '/posts',\n loader: async ({ params, context }) => {\n console.log('Loader called with params:', params)\n\n try {\n const data = await fetchPosts()\n console.log('Loader data:', data)\n return data\n } catch (error) {\n console.error('Loader error:', error)\n throw error\n }\n },\n component: () => {\n const data = Route.useLoaderData()\n console.log('Component data:', data)\n\n return \u003cdiv>{/* render data */}\u003c/div>\n },\n})\n```\n\n2. **Debug Loading States**\n\n```tsx\nfunction DataLoadingDebug() {\n const location = useLocation()\n\n console.log('Route status:', {\n isLoading: location.isLoading,\n isTransitioning: location.isTransitioning,\n })\n\n return null\n}\n```\n\n3. **Check Loader Dependencies**\n\n```tsx\nconst route = createRoute({\n path: '/posts/$postId',\n loader: async ({ params }) => {\n // Loader will re-run when params change\n console.log('Loading post:', params.postId)\n return fetchPost(params.postId)\n },\n // Add dependencies for explicit re-loading\n loaderDeps: ({ search }) => ({\n refresh: search.refresh,\n }),\n})\n```\n\n---\n\n## Search Parameters Issues\n\n### Problem: Search Params Not Updating\n\n**Symptoms:**\n\n- URL search params don't update\n- `useSearch()` returns stale data\n- Search validation errors\n\n**Debugging Steps:**\n\n1. **Check Search Validation Schema**\n\n```tsx\nconst route = createRoute({\n path: '/search',\n validateSearch: (search) => {\n console.log('Raw search params:', search)\n\n const validated = {\n q: (search.q as string) || '',\n page: Number(search.page) || 1,\n }\n\n console.log('Validated search params:', validated)\n return validated\n },\n component: () => {\n const search = Route.useSearch()\n console.log('Component search:', search)\n\n return \u003cdiv>Query: {search.q}\u003c/div>\n },\n})\n```\n\n2. **Debug Search Navigation**\n\n```tsx\nfunction SearchDebug() {\n const navigate = useNavigate()\n const currentSearch = Route.useSearch()\n\n const updateSearch = (newSearch: any) => {\n console.log('Current search:', currentSearch)\n console.log('New search:', newSearch)\n\n navigate({\n to: '.',\n search: (prev) => {\n const updated = { ...prev, ...newSearch }\n console.log('Final search:', updated)\n return updated\n },\n })\n }\n\n return (\n \u003cbutton onClick={() => updateSearch({ q: 'test' })}>Update Search\u003c/button>\n )\n}\n```\n\n---\n\n## Performance Issues\n\n### Problem: Excessive Re-renders\n\n**Symptoms:**\n\n- Components re-rendering too often\n- Performance lag during navigation\n- Memory usage increasing\n\n**Debugging Steps:**\n\n1. **Use React DevTools Profiler**\n\n```tsx\n// Wrap your app for profiling\nimport { Profiler } from 'react'\n\nfunction App() {\n return (\n \u003cProfiler\n id=\"Router\"\n onRender={(id, phase, actualDuration) => {\n console.log(`${id} ${phase} took ${actualDuration}ms`)\n }}\n >\n \u003cRouterProvider router={router} />\n \u003c/Profiler>\n )\n}\n```\n\n2. **Optimize Route Subscriptions**\n\n```tsx\n// ❌ Subscribes to all search params\nfunction MyComponent() {\n const search = Route.useSearch()\n return \u003cdiv>{search.someSpecificField}\u003c/div>\n}\n\n// ✅ Subscribe only to specific field\nfunction MyComponent() {\n const someSpecificField = Route.useSearch({\n select: (search) => search.someSpecificField,\n })\n return \u003cdiv>{someSpecificField}\u003c/div>\n}\n```\n\n3. **Monitor Route State Changes**\n\n```tsx\n// Add to router configuration\nconst router = createRouter({\n routeTree,\n context: {\n /* ... */\n },\n onUpdate: (router) => {\n console.log('Router state updated:', {\n pathname: router.state.location.pathname,\n isLoading: router.state.isLoading,\n matches: router.state.matches.length,\n })\n },\n})\n```\n\n### Problem: Memory Leaks\n\n**Symptoms:**\n\n- Memory usage constantly increasing\n- Browser becomes slow over time\n- Route components not cleaning up\n\n**Debugging Steps:**\n\n1. **Check Component Cleanup**\n\n```tsx\nfunction MyComponent() {\n const [data, setData] = useState(null)\n\n useEffect(() => {\n const subscription = someService.subscribe(setData)\n\n // ✅ Always clean up subscriptions\n return () => {\n subscription.unsubscribe()\n }\n }, [])\n\n return \u003cdiv>{data}\u003c/div>\n}\n```\n\n2. **Monitor Route Unmounting**\n\n```tsx\nfunction DebuggableComponent() {\n useEffect(() => {\n console.log('Component mounted')\n\n return () => {\n console.log('Component unmounted')\n }\n }, [])\n\n return \u003cdiv>Content\u003c/div>\n}\n```\n\n---\n\n## TypeScript Issues\n\n### Problem: Type Errors with Router\n\n**Symptoms:**\n\n- TypeScript errors in route definitions\n- Type inference not working\n- Parameter types incorrect\n\n**Debugging Steps:**\n\n1. **Check Route Tree Type Registration**\n\n```tsx\n// Ensure this declaration exists\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n```\n\n2. **Debug Route Type Generation**\n\n```bash\n# Check if route types are being generated\nls src/routeTree.gen.ts\n\n# Regenerate route types if needed\nnpx @tanstack/router-cli generate\n```\n\n3. **Use Type Assertions for Debugging**\n\n```tsx\nfunction TypeDebugComponent() {\n const params = Route.useParams()\n const search = Route.useSearch()\n\n // Add type assertions to check what TypeScript infers\n console.log('Params type:', params as any)\n console.log('Search type:', search as any)\n\n return null\n}\n```\n\n---\n\n## Systematic Debugging Process\n\n### 1. Information Gathering\n\nWhen debugging any router issue, start by collecting this information:\n\n```tsx\nfunction RouterDebugInfo() {\n const router = useRouter()\n const location = useLocation()\n\n useEffect(() => {\n console.group('🐛 Router Debug Info')\n console.log('Current pathname:', location.pathname)\n console.log('Search params:', location.search)\n console.log('Router state:', router.state)\n console.log('Active matches:', router.state.matches)\n console.log('Route tree:', router.routeTree)\n console.groupEnd()\n }, [location.pathname])\n\n return null\n}\n\n// Add to your app during debugging\n;\u003cRouterDebugInfo />\n```\n\n### 2. Isolation Testing\n\nCreate minimal reproduction:\n\n```tsx\n// Minimal route for testing\nconst testRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/debug',\n component: () => {\n console.log('Test route rendered')\n return \u003cdiv>Debug Route\u003c/div>\n },\n})\n\n// Add to route tree temporarily\nconst routeTree = rootRoute.addChildren([\n // ... other routes\n testRoute, // Add test route\n])\n```\n\n### 3. Step-by-Step Debugging\n\n1. **Verify basic setup** - Router provider, route tree structure\n2. **Check route definitions** - Paths, parent routes, configuration\n3. **Test navigation** - Links, programmatic navigation\n4. **Validate data flow** - Loaders, search params, context\n5. **Monitor performance** - Re-renders, memory usage\n\n---\n\n## Browser Debugging Tips\n\n### Console Commands\n\n```js\n// In browser console (when router is on window)\n\n// Current router state\nrouter.state\n\n// Navigate programmatically\nrouter.navigate({ to: '/some-path' })\n\n// Get route by path\nrouter.getRoute('/users/$userId')\n\n// Check if route exists\nrouter.buildLocation({ to: '/some-path' })\n\n// View all registered routes\nObject.keys(router.routesById)\n```\n\n### Network Tab\n\nMonitor these requests when debugging:\n\n- **Route code chunks** - Check if lazy routes are loading\n- **Loader data requests** - Verify API calls from loaders\n- **Failed requests** - Look for 404s or failed API calls\n\n### React DevTools\n\n1. **Components Tab** - Find router components and inspect props\n2. **Profiler Tab** - Identify performance bottlenecks\n3. **Search for components** - Find specific route components quickly\n\n---\n\n## Common Error Messages\n\n### \"Route not found\"\n\n- Check route path spelling and case sensitivity\n- Verify route is added to route tree\n- Ensure parent routes are properly configured\n\n### \"Cannot read property 'useParams' of undefined\"\n\n- Component is likely outside RouterProvider\n- Route might not be properly registered\n- Check if using correct Route object\n\n### \"Invalid search params\"\n\n- Check validateSearch schema\n- Verify search param types match schema\n- Look for required vs optional parameters\n\n### \"Navigation was interrupted\"\n\n- Usually caused by redirect in beforeLoad\n- Check for redirect loops\n- Verify authentication logic\n\n---\n\n## Performance Monitoring\n\n### Enable Performance Tracking\n\n```tsx\nconst router = createRouter({\n routeTree,\n context: {\n /* ... */\n },\n onUpdate: (router) => {\n performance.mark('router-update')\n },\n onLoad: (router) => {\n performance.mark('router-load')\n performance.measure('router-load-time', 'router-update', 'router-load')\n },\n})\n```\n\n### Monitor Route Loading Times\n\n```tsx\nconst route = createRoute({\n path: '/slow-route',\n loader: async () => {\n const start = performance.now()\n const data = await fetchData()\n const end = performance.now()\n\n console.log(`Loader took ${end - start}ms`)\n return data\n },\n})\n```\n\n---\n\n## Common Next Steps\n\nAfter debugging router issues, you might want to:\n\n- [How to Set Up Testing](./setup-testing.md) - Add tests to prevent regressions\n- [How to Deploy to Production](./deploy-to-production.md) - Ensure issues don't occur in production\n\n\n\n## Related Resources\n\n- TanStack Router DevTools - Official debugging tools\n- React DevTools - React-specific debugging\n- [Router Core Documentation](../overview.md) - Understanding router internals\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":15445,"content_sha256":"67f12d37187180d75130751b0064392beef3242559edca2fb3a059b4e10a6154"},{"filename":"references/docs/router/how-to/deploy-to-production.md","content":"---\ntitle: How to Deploy TanStack Router to Production\n---\n\nThis guide covers deploying TanStack Router applications to popular hosting platforms.\n\n## Quick Start\n\nSingle Page Applications (SPAs) need special server configuration to handle client-side routing. Configure your hosting platform to serve `index.html` for all routes, allowing TanStack Router to handle navigation.\n\n---\n\n## Netlify Deployment\n\n### 1. Create `_redirects` File\n\nCreate a `public/_redirects` file (or `_redirects` in your build output):\n\n```\n/* /index.html 200\n```\n\n### 2. Alternative: `netlify.toml`\n\nCreate a `netlify.toml` file in your project root:\n\n```toml\n[[redirects]]\n from = \"/*\"\n to = \"/index.html\"\n status = 200\n\n[build]\n publish = \"dist\"\n command = \"npm run build\"\n```\n\n### 3. For TanStack Start (SSR)\n\n```toml\n[build]\n publish = \".output/public\"\n command = \"npm run build\"\n\n[functions]\n directory = \".output/server\"\n\n[[redirects]]\n from = \"/api/*\"\n to = \"/.netlify/functions/server\"\n status = 200\n\n[[redirects]]\n from = \"/*\"\n to = \"/index.html\"\n status = 200\n```\n\n---\n\n## Cloudflare Pages\n\n### 1. Create `_redirects` File\n\nCreate a `public/_redirects` file:\n\n```\n/* /index.html 200\n```\n\n### 2. Alternative: `_routes.json`\n\nCreate a `public/_routes.json` file for more control:\n\n```json\n{\n \"version\": 1,\n \"include\": [\"/*\"],\n \"exclude\": [\"/api/*\"]\n}\n```\n\n### 3. For TanStack Start (SSR)\n\nCreate `functions/_middleware.ts` for SSR support:\n\n```ts\nexport const onRequest: PagesFunction = async (context) => {\n // Handle SSR requests\n return await handleSSR(context)\n}\n```\n\n### 4. Deploy via Git\n\n1. Connect your GitHub repository to Cloudflare Pages\n2. Set build settings:\n - **Build command:** `npm run build`\n - **Build output directory:** `dist`\n - **Root directory:** (leave empty)\n\n### 5. Deploy via Wrangler CLI\n\n```bash\n# Install Wrangler\nnpm install -g wrangler\n\n# Deploy\nwrangler pages publish dist --project-name=my-app\n```\n\n---\n\n## Vercel Deployment\n\n### 1. Create `vercel.json`\n\nCreate a `vercel.json` file in your project root:\n\n```json\n{\n \"rewrites\": [\n {\n \"source\": \"/(.*)\",\n \"destination\": \"/index.html\"\n }\n ]\n}\n```\n\n### 2. For TanStack Start (SSR) Applications\n\nIf using TanStack Start with SSR, use this configuration instead:\n\n```json\n{\n \"functions\": {\n \"app/server.ts\": {\n \"runtime\": \"nodejs18.x\"\n }\n },\n \"routes\": [\n {\n \"src\": \"/(.*)\",\n \"dest\": \"/api/server\"\n }\n ]\n}\n```\n\n### 3. Build Configuration\n\nEnsure your `package.json` has the correct build script:\n\n```json\n{\n \"scripts\": {\n \"build\": \"vite build\",\n \"preview\": \"vite preview\"\n }\n}\n```\n\n### 4. Deploy\n\n```bash\n# Install Vercel CLI\nnpm i -g vercel\n\n# Deploy\nvercel\n```\n\n---\n\n## GitHub Pages\n\n### 1. Create `404.html`\n\nGitHub Pages requires a `404.html` file that duplicates `index.html`:\n\n```bash\n# After building\ncp dist/index.html dist/404.html\n```\n\n### 2. Update `vite.config.js`\n\n```js\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n base: '/your-repo-name/', // Replace with your repository name\n plugins: [\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n }),\n react(),\n ],\n build: {\n outDir: 'dist',\n },\n})\n```\n\n### 3. GitHub Actions Workflow\n\nCreate `.github/workflows/deploy.yml`:\n\n```yaml\nname: Deploy to GitHub Pages\n\non:\n push:\n branches: [main]\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n\n - name: Setup Node.js\n uses: actions/setup-node@v3\n with:\n node-version: '18'\n cache: 'npm'\n\n - name: Install dependencies\n run: npm ci\n\n - name: Build\n run: npm run build\n\n - name: Create 404.html\n run: cp dist/index.html dist/404.html\n\n - name: Deploy to GitHub Pages\n uses: peaceiris/actions-gh-pages@v3\n with:\n github_token: ${{ secrets.GITHUB_TOKEN }}\n publish_dir: ./dist\n```\n\n---\n\n## Firebase Hosting\n\n### 1. Create `firebase.json`\n\n```json\n{\n \"hosting\": {\n \"public\": \"dist\",\n \"ignore\": [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"],\n \"rewrites\": [\n {\n \"source\": \"**\",\n \"destination\": \"/index.html\"\n }\n ]\n }\n}\n```\n\n### 2. Deploy\n\n```bash\n# Install Firebase CLI\nnpm install -g firebase-tools\n\n# Login and initialize\nfirebase login\nfirebase init hosting\n\n# Build and deploy\nnpm run build\nfirebase deploy\n```\n\n---\n\n## Apache Server\n\nCreate a `.htaccess` file in your build output directory:\n\n```apache\n\u003cIfModule mod_rewrite.c>\n RewriteEngine On\n RewriteBase /\n RewriteRule ^index\\.html$ - [L]\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteRule . /index.html [L]\n\u003c/IfModule>\n```\n\n---\n\n## Nginx\n\nAdd this configuration to your Nginx server block:\n\n```nginx\nserver {\n listen 80;\n server_name your-domain.com;\n root /path/to/your/dist;\n index index.html;\n\n location / {\n try_files $uri $uri/ /index.html;\n }\n\n # Optional: Cache static assets\n location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg)$ {\n expires 1y;\n add_header Cache-Control \"public, immutable\";\n }\n}\n```\n\n---\n\n## Docker Deployment\n\n### 1. Create `Dockerfile`\n\n```dockerfile\n# Build stage\nFROM node:18-alpine AS build\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\n# Production stage\nFROM nginx:alpine\nCOPY --from=build /app/dist /usr/share/nginx/html\nCOPY nginx.conf /etc/nginx/conf.d/default.conf\nEXPOSE 80\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n```\n\n### 2. Create `nginx.conf`\n\n```nginx\nserver {\n listen 80;\n server_name localhost;\n root /usr/share/nginx/html;\n index index.html;\n\n location / {\n try_files $uri $uri/ /index.html;\n }\n}\n```\n\n### 3. Build and Run\n\n```bash\ndocker build -t my-tanstack-app .\ndocker run -p 80:80 my-tanstack-app\n```\n\n---\n\n## Production Checklist\n\nBefore deploying, ensure you have:\n\n- [ ] Created hosting platform configuration file\n- [ ] Set correct base path if deploying to subdirectory\n- [ ] Configured environment variables with `VITE_` prefix\n- [ ] Tested all routes by direct URL access\n- [ ] Verified static assets load correctly\n\n---\n\n## Common Problems\n\n### 404 Errors on Page Refresh\n\n**Problem:** Routes work when navigating within the app, but refreshing the page shows 404.\n\n**Cause:** The server looks for files like `/about/index.html` which don't exist in SPAs.\n\n**Solution:** Add the configuration files shown above for your hosting platform.\n\n### App Works Locally But Breaks When Deployed\n\n**Problem:** App works in development but shows errors in production.\n\n**Solutions:**\n\n- **Subdirectory deployment:** Configure base path in `vite.config.js`:\n ```js\n export default defineConfig({\n base: '/my-app/', // Match your deployment path\n })\n ```\n- **Build output mismatch:** Ensure build directory matches hosting config:\n ```js\n export default defineConfig({\n build: {\n outDir: 'dist', // Must match hosting platform setting\n },\n })\n ```\n- **Environment variables:** Prefix with `VITE_` and rebuild:\n ```bash\n # .env\n VITE_API_URL=https://api.example.com\n ```\n\n### Assets Not Loading (CSS/JS 404s)\n\n**Problem:** App loads but styling is broken or JavaScript fails to load.\n\n**Solutions:**\n\n- Check build output directory in hosting configuration\n- Verify public path configuration in Vite\n- Ensure static file serving is properly configured\n\n---\n\n## Common Next Steps\n\nAfter deployment, you might want to:\n\n- [How to Set Up Basic Authentication](./setup-authentication.md) - Secure your application with auth\n- [Migrate from React Router v7](./migrate-from-react-router.md) - Complete migration guide if you're coming from React Router\n- [How to Set Up Server-Side Rendering (SSR)](./setup-ssr.md)\n\n\n\n## Related Resources\n\n- Deployment Examples - Official examples\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7930,"content_sha256":"740f268646a94ab896e06880ffbb17c0bd8c0b285478637de034c7080307e264"},{"filename":"references/docs/router/how-to/drafts/build-search-filtering-systems.draft.md","content":"# DRAFT: Build Search-Based Filtering Systems\n\n**Final Destination:** `docs/router/framework/react/how-to/build-search-filtering-systems.md` \n**Progressive Series Position:** Specialized Use Cases - Guide #9 \n**Depends On:** `setup-basic-search-params.md`, `navigate-with-search-params.md`, `validate-search-params.md` \n**Status:** Ready for implementation - comprehensive UI patterns available\n\n---\n\n## Content Staged from navigate-with-search-params.md\n\n### Search Result Navigation\n\nHandle search result navigation with query preservation:\n\n```tsx\nimport { Link, useSearch } from '@tanstack/react-router'\n\nfunction SearchResults() {\n const search = useSearch({ from: '/search' })\n\n return (\n \u003cdiv>\n {/* Preserve search query, change view */}\n \u003cnav>\n \u003cLink search={(prev) => ({ ...prev, view: 'grid' })}>Grid View\u003c/Link>\n \u003cLink search={(prev) => ({ ...prev, view: 'list' })}>List View\u003c/Link>\n \u003c/nav>\n\n {/* Pagination with query preservation */}\n \u003cdiv>\n {search.page > 1 && (\n \u003cLink search={(prev) => ({ ...prev, page: prev.page - 1 })}>\n Previous\n \u003c/Link>\n )}\n\n \u003cLink search={(prev) => ({ ...prev, page: (prev.page || 1) + 1 })}>\n Next\n \u003c/Link>\n \u003c/div>\n\n {/* Related searches */}\n \u003cdiv>\n Related searches:\n {['laptops gaming', 'laptops business', 'laptops student'].map(\n (suggestion) => (\n \u003cLink\n key={suggestion}\n search={(prev) => ({ ...prev, query: suggestion, page: 1 })}\n >\n {suggestion}\n \u003c/Link>\n ),\n )}\n \u003c/div>\n \u003c/div>\n )\n}\n```\n\n### Filter Navigation\n\nBuild filtering interfaces with search parameter navigation:\n\n```tsx\nimport { Link, useSearch } from '@tanstack/react-router'\n\nconst categories = ['electronics', 'clothing', 'books', 'home']\nconst sortOptions = [\n { value: 'relevance', label: 'Relevance' },\n { value: 'price-asc', label: 'Price: Low to High' },\n { value: 'price-desc', label: 'Price: High to Low' },\n { value: 'rating', label: 'Customer Rating' },\n]\n\nfunction FilterNavigation() {\n const search = useSearch({ from: '/products' })\n\n return (\n \u003caside>\n {/* Category filters */}\n \u003cdiv>\n \u003ch3>Categories\u003c/h3>\n {categories.map((category) => (\n \u003cLink\n key={category}\n search={(prev) => ({\n ...prev,\n category: prev.category === category ? undefined : category,\n page: 1,\n })}\n className={search.category === category ? 'active' : ''}\n >\n {category}\n \u003c/Link>\n ))}\n \u003c/div>\n\n {/* Sort options */}\n \u003cdiv>\n \u003ch3>Sort By\u003c/h3>\n {sortOptions.map((option) => (\n \u003cLink\n key={option.value}\n search={(prev) => ({ ...prev, sort: option.value, page: 1 })}\n className={search.sort === option.value ? 'active' : ''}\n >\n {option.label}\n \u003c/Link>\n ))}\n \u003c/div>\n\n {/* Clear all filters */}\n \u003cLink\n search={(prev) => {\n const { category, sort, minPrice, maxPrice, ...rest } = prev\n return rest\n }}\n >\n Clear All Filters\n \u003c/Link>\n \u003c/aside>\n )\n}\n```\n\n### Programmatic Search Controls\n\nNavigate programmatically with search parameter updates:\n\n```tsx\nimport { useNavigate } from '@tanstack/react-router'\n\nfunction SearchControls() {\n const navigate = useNavigate()\n\n const handleSortChange = (sortBy: string) => {\n navigate({\n search: (prev) => ({ ...prev, sort: sortBy, page: 1 }),\n })\n }\n\n const handleClearFilters = () => {\n navigate({\n search: (prev) => {\n const { category, minPrice, maxPrice, ...rest } = prev\n return rest\n },\n })\n }\n\n return (\n \u003cdiv>\n \u003cselect onChange={(e) => handleSortChange(e.target.value)}>\n \u003coption value=\"relevance\">Sort by Relevance\u003c/option>\n \u003coption value=\"price-asc\">Price: Low to High\u003c/option>\n \u003coption value=\"price-desc\">Price: High to Low\u003c/option>\n \u003c/select>\n\n \u003cbutton onClick={handleClearFilters}>Clear Filters\u003c/button>\n \u003c/div>\n )\n}\n```\n\n---\n\n## Implementation Notes\n\n### Additional Content Needed:\n\n- [ ] Complete e-commerce filtering example\n- [ ] Advanced filter combinations (price ranges, multi-select)\n- [ ] Filter state persistence and sharing\n- [ ] Search result highlighting and sorting\n- [ ] Infinite scroll pagination patterns\n- [ ] Filter URL state management best practices\n- [ ] Accessibility considerations for filter UIs\n- [ ] Mobile-responsive filter patterns\n\n### Cross-References to Add:\n\n- Link to `setup-basic-search-params.md` for foundation\n- Link to `navigate-with-search-params.md` for navigation patterns\n- Link to `validate-search-params.md` for filter validation\n- Forward link to `search-params-with-data-loading.md` for data integration\n\n### README Update Required:\n\n- [ ] Mark guide as completed in progressive series\n- [ ] Uncomment \"Common Next Steps\" in related guides\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5110,"content_sha256":"581671e8c7815f6efcc73da41b462d3444fec3c19903c9779aa11669b17462db"},{"filename":"references/docs/router/how-to/drafts/optimize-search-param-performance.draft.md","content":"# DRAFT: Optimize Search Parameter Performance\n\n**Final Destination:** `docs/router/framework/react/how-to/optimize-search-param-performance.md` \n**Progressive Series Position:** Advanced Level (Power User Patterns) - Guide #7 \n**Depends On:** `setup-basic-search-params.md`, `navigate-with-search-params.md` \n**Status:** Ready for implementation - performance patterns available\n\n---\n\n## Content Staged from navigate-with-search-params.md\n\n### Performance Issues with Functional Updates\n\n**Problem:** Complex functional updates cause unnecessary re-renders.\n\n```tsx\n// ❌ Wrong - complex computation in render\n\u003cLink search={(prev) => {\n // Expensive computation on every render\n const result = expensiveCalculation(prev)\n return { ...prev, computed: result }\n}}>\n Update\n\u003c/Link>\n\n// ✅ Correct - memoize or use callback\nconst updateSearch = useCallback((prev) => {\n const result = expensiveCalculation(prev)\n return { ...prev, computed: result }\n}, [])\n\n\u003cLink search={updateSearch}>Update\u003c/Link>\n```\n\n### Navigation During Render\n\n**Problem:** Calling navigate during component render causes infinite loops.\n\n```tsx\nfunction ProblematicComponent() {\n const navigate = useNavigate()\n\n // ❌ Wrong - navigate during render\n if (someCondition) {\n navigate({ search: { redirect: true } })\n }\n\n return \u003cdiv>Content\u003c/div>\n}\n\nfunction FixedComponent() {\n const navigate = useNavigate()\n\n // ✅ Correct - navigate in effect\n useEffect(() => {\n if (someCondition) {\n navigate({ search: { redirect: true } })\n }\n }, [someCondition, navigate])\n\n return \u003cdiv>Content\u003c/div>\n}\n```\n\n---\n\n## Implementation Notes\n\n### Additional Content Needed:\n\n- [ ] Search parameter selectors to prevent unnecessary re-renders\n- [ ] Debouncing search input updates\n- [ ] Memoization strategies for expensive search computations\n- [ ] React.memo usage with search parameters\n- [ ] useMemo patterns for derived search state\n- [ ] Search parameter batching techniques\n- [ ] Performance monitoring and profiling search params\n- [ ] Bundle size optimization strategies\n\n### Cross-References to Add:\n\n- Link to `setup-basic-search-params.md` for foundation\n- Link to `navigate-with-search-params.md` for navigation patterns\n- Link to `search-params-in-forms.md` for debouncing forms\n- Forward link to `debug-search-param-issues.md` for debugging performance\n\n### README Update Required:\n\n- [ ] Mark guide as completed in progressive series\n- [ ] Uncomment \"Common Next Steps\" in related guides\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2496,"content_sha256":"8c1c3ae0a386794cd0ddbd7c0d3ea7a9e31d0dd8f20b13813d0efa11c5f8961e"},{"filename":"references/docs/router/how-to/drafts/README.md","content":"# How-To Guide Drafts\n\nThis directory contains draft content for upcoming how-to guides in the Progressive Search Parameters Series. Each draft file contains:\n\n- **Metadata** about the final destination and dependencies\n- **Staged content** extracted from other guides to avoid scope creep\n- **Implementation notes** for future development\n- **Cross-reference planning** for proper guide linking\n\n## File Naming Convention\n\n- `{guide-name}.draft.md` - Draft content for upcoming guides\n- Clear metadata header with destination path and status\n\n## Current Drafts\n\n### Ready for Implementation (Substantial Content Available)\n\n1. **`validate-search-params.draft.md`**\n - **Destination:** `validate-search-params.md`\n - **Position:** Intermediate Level - Guide #3\n - **Content:** Schema validation, type safety, error handling\n\n2. **`build-search-filtering-systems.draft.md`**\n - **Destination:** `build-search-filtering-systems.md`\n - **Position:** Specialized Use Cases - Guide #9\n - **Content:** Complete filtering UIs, search results, pagination\n\n3. **`search-params-in-forms.draft.md`**\n - **Destination:** `search-params-in-forms.md`\n - **Position:** Specialized Use Cases - Guide #10\n - **Content:** Form synchronization, state management\n\n4. **`optimize-search-param-performance.draft.md`**\n - **Destination:** `optimize-search-param-performance.md`\n - **Position:** Advanced Level - Guide #7\n - **Content:** Performance optimization, memoization patterns\n\n## Implementation Workflow\n\nWhen implementing a guide from a draft:\n\n1. **Copy the staged content** to the final destination\n2. **Expand with additional examples** specific to the guide's focus\n3. **Add comprehensive troubleshooting** for the domain\n4. **Update cross-references** in related guides\n5. **Update the main README** to mark guide as completed\n6. **Delete the draft file** once fully implemented\n\n## Benefits of This System\n\n- **Prevents scope creep** in individual guides\n- **Preserves valuable content** during refactoring\n- **Enables focused guide development**\n- **Maintains clear progression** through the series\n- **Facilitates parallel development** of multiple guides\n\n## Content Sources\n\nMost staged content originates from:\n\n- `navigate-with-search-params.md` - Content moved to maintain focus\n- Implementation planning sessions\n- User feedback and common questions\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2375,"content_sha256":"161574f035bc734c98d607dec05613d04bdce39296adc586bf22dea1a25e138f"},{"filename":"references/docs/router/how-to/drafts/search-params-in-forms.draft.md","content":"# DRAFT: Handle Search Parameters in Forms\n\n**Final Destination:** `docs/router/framework/react/how-to/search-params-in-forms.md` \n**Progressive Series Position:** Specialized Use Cases - Guide #10 \n**Depends On:** `setup-basic-search-params.md`, `navigate-with-search-params.md`, `validate-search-params.md` \n**Status:** Ready for implementation - form synchronization patterns available\n\n---\n\n## Content Staged from navigate-with-search-params.md\n\n### Navigation with State Synchronization\n\nKeep component state in sync with URL search parameters:\n\n```tsx\nimport { useState, useEffect } from 'react'\nimport { useNavigate, useSearch } from '@tanstack/react-router'\n\nfunction SynchronizedForm() {\n const navigate = useNavigate()\n const search = useSearch({ from: '/products' })\n\n // Local state synced with URL\n const [localFilters, setLocalFilters] = useState({\n minPrice: search.minPrice || 0,\n maxPrice: search.maxPrice || 1000,\n inStock: search.inStock || false,\n })\n\n // Update local state when URL changes\n useEffect(() => {\n setLocalFilters({\n minPrice: search.minPrice || 0,\n maxPrice: search.maxPrice || 1000,\n inStock: search.inStock || false,\n })\n }, [search.minPrice, search.maxPrice, search.inStock])\n\n const applyFilters = () => {\n navigate({\n search: (prev) => ({\n ...prev,\n ...localFilters,\n page: 1, // Reset pagination\n }),\n })\n }\n\n const resetFilters = () => {\n const defaultFilters = { minPrice: 0, maxPrice: 1000, inStock: false }\n setLocalFilters(defaultFilters)\n\n navigate({\n search: (prev) => {\n const { minPrice, maxPrice, inStock, ...rest } = prev\n return rest\n },\n })\n }\n\n return (\n \u003cdiv>\n \u003clabel>\n Min Price:\n \u003cinput\n type=\"number\"\n value={localFilters.minPrice}\n onChange={(e) =>\n setLocalFilters((prev) => ({\n ...prev,\n minPrice: parseInt(e.target.value) || 0,\n }))\n }\n />\n \u003c/label>\n\n \u003clabel>\n Max Price:\n \u003cinput\n type=\"number\"\n value={localFilters.maxPrice}\n onChange={(e) =>\n setLocalFilters((prev) => ({\n ...prev,\n maxPrice: parseInt(e.target.value) || 1000,\n }))\n }\n />\n \u003c/label>\n\n \u003clabel>\n \u003cinput\n type=\"checkbox\"\n checked={localFilters.inStock}\n onChange={(e) =>\n setLocalFilters((prev) => ({\n ...prev,\n inStock: e.target.checked,\n }))\n }\n />\n In Stock Only\n \u003c/label>\n\n \u003cbutton onClick={applyFilters}>Apply Filters\u003c/button>\n \u003cbutton onClick={resetFilters}>Reset\u003c/button>\n \u003c/div>\n )\n}\n```\n\n### Form with Search Parameter Validation\n\n```tsx\nconst handleFormSubmit = (formData: FormData) => {\n const query = formData.get('query') as string\n const page = parseInt(formData.get('page') as string) || 1\n\n safeNavigate({ query, page })\n}\n\nreturn (\n \u003cform action={handleFormSubmit}>\n \u003cinput name=\"query\" placeholder=\"Search...\" required />\n \u003cinput name=\"page\" type=\"number\" defaultValue=\"1\" />\n \u003cbutton type=\"submit\">Search\u003c/button>\n \u003c/form>\n)\n```\n\n---\n\n## Implementation Notes\n\n### Additional Content Needed:\n\n- [ ] Uncontrolled form patterns with search params\n- [ ] Controlled form patterns with real-time updates\n- [ ] Form library integration (React Hook Form, Formik)\n- [ ] Debounced form updates to URL\n- [ ] Form reset and default value handling\n- [ ] Multi-step form with URL state\n- [ ] File upload forms with search state\n- [ ] Form validation error handling with URL feedback\n\n### Cross-References to Add:\n\n- Link to `setup-basic-search-params.md` for foundation\n- Link to `navigate-with-search-params.md` for navigation patterns\n- Link to `validate-search-params.md` for form validation\n- Forward link to `optimize-search-param-performance.md` for debouncing\n\n### README Update Required:\n\n- [ ] Mark guide as completed in progressive series\n- [ ] Uncomment \"Common Next Steps\" in related guides\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4131,"content_sha256":"f9ecfc7504e3db95f4aaf5e96bc6b50985a1a6554c49411547344ed7a9897292"},{"filename":"references/docs/router/how-to/install.md","content":"---\ntitle: How to Install TanStack Router\n---\n\n## Prerequisites\n\n- React 18.x.x or 19.x.x\n- ReactDOM 18.x.x or 19.x.x with `createRoot` support\n- TypeScript 5.3.x or higher (recommended)\n\n## Installation Steps\n\n1. **Install the package**\n\n Choose your package manager:\n\n ```sh\n npm install @tanstack/react-router\n ```\n\n ```sh\n pnpm add @tanstack/react-router\n ```\n\n ```sh\n yarn add @tanstack/react-router\n ```\n\n ```sh\n bun add @tanstack/react-router\n ```\n\n ```sh\n deno add npm:@tanstack/react-router\n ```\n\n2. **Verify installation**\n\n Check that the package appears in your `package.json`:\n\n ```json\n {\n \"dependencies\": {\n \"@tanstack/react-router\": \"^x.x.x\"\n }\n }\n ```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":725,"content_sha256":"62ae43c31b8f7df7954af14502fd2c8c90d8ceb7a101583c3c232f8ac8622d72"},{"filename":"references/docs/router/how-to/integrate-chakra-ui.md","content":"---\ntitle: How to Integrate TanStack Router with Chakra UI\n---\n\nThis guide covers setting up Chakra UI with TanStack Router, including theme configuration and creating responsive, accessible components.\n\n## Quick Start\n\n**Time Required:** 30-40 minutes \n**Difficulty:** Beginner to Intermediate \n**Prerequisites:** Existing TanStack Router project\n\n### What You'll Accomplish\n\n- Install and configure Chakra UI with TanStack Router\n- Set up theme provider and custom theming\n- Create type-safe router-compatible Chakra components\n- Implement responsive navigation patterns\n- Build accessible UI components with router integration\n\n---\n\n## Installation and Setup\n\n### Step 1: Install Chakra UI Dependencies\n\n```bash\nnpm install @chakra-ui/react @emotion/react @emotion/styled framer-motion\n```\n\n### Step 2: Set Up Chakra Provider\n\n```tsx\n// src/components/chakra-provider.tsx\nimport { ChakraProvider, extendTheme, type ThemeConfig } from '@chakra-ui/react'\nimport { ReactNode } from 'react'\n\n// Extend the theme with custom colors and configurations\nconst config: ThemeConfig = {\n initialColorMode: 'light',\n useSystemColorMode: true,\n}\n\nconst theme = extendTheme({\n config,\n colors: {\n brand: {\n 50: '#e3f2fd',\n 100: '#bbdefb',\n 200: '#90caf9',\n 300: '#64b5f6',\n 400: '#42a5f5',\n 500: '#2196f3',\n 600: '#1e88e5',\n 700: '#1976d2',\n 800: '#1565c0',\n 900: '#0d47a1',\n },\n },\n fonts: {\n heading: 'Inter, sans-serif',\n body: 'Inter, sans-serif',\n },\n components: {\n Button: {\n defaultProps: {\n colorScheme: 'brand',\n },\n },\n Link: {\n baseStyle: {\n _hover: {\n textDecoration: 'none',\n },\n },\n },\n },\n})\n\ninterface ChakraAppProviderProps {\n children: ReactNode\n}\n\nexport function ChakraAppProvider({ children }: ChakraAppProviderProps) {\n return \u003cChakraProvider theme={theme}>{children}\u003c/ChakraProvider>\n}\n```\n\n### Step 3: Update Root Route\n\n```tsx\n// src/routes/__root.tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/router-devtools'\nimport { ChakraAppProvider } from '@/components/chakra-provider'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003cChakraAppProvider>\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/ChakraAppProvider>\n ),\n})\n```\n\n---\n\n## Creating Router-Compatible Components\n\n### Step 1: Create Router-Compatible Chakra Components\n\n```tsx\n// src/components/ui/chakra-router-link.tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Link as ChakraLink, Button, IconButton } from '@chakra-ui/react'\nimport { forwardRef } from 'react'\n\n// Router-compatible Chakra Link\nexport const RouterLink = createLink(\n forwardRef\u003cHTMLAnchorElement, any>((props, ref) => {\n return \u003cChakraLink ref={ref} {...props} />\n }),\n)\n\n// Router-compatible Chakra Button\nexport const RouterButton = createLink(\n forwardRef\u003cHTMLButtonElement, any>((props, ref) => {\n return \u003cButton ref={ref} as=\"button\" {...props} />\n }),\n)\n\n// Router-compatible Chakra IconButton\nexport const RouterIconButton = createLink(\n forwardRef\u003cHTMLButtonElement, any>((props, ref) => {\n return \u003cIconButton ref={ref} as=\"button\" {...props} />\n }),\n)\n```\n\n### Step 2: Create Navigation Components\n\n```tsx\n// src/components/navigation/chakra-nav.tsx\nimport { useMatchRoute } from '@tanstack/react-router'\nimport {\n Box,\n Flex,\n HStack,\n IconButton,\n useDisclosure,\n useColorModeValue,\n Stack,\n Collapse,\n} from '@chakra-ui/react'\nimport { HamburgerIcon, CloseIcon } from '@chakra-ui/icons'\nimport { RouterLink } from '@/components/ui/chakra-router-link'\n\ninterface NavItem {\n label: string\n to: string\n exact?: boolean\n}\n\ninterface ChakraNavProps {\n items: NavItem[]\n brand?: string\n brandTo?: string\n}\n\nexport function ChakraNav({\n items,\n brand = 'Logo',\n brandTo = '/',\n}: ChakraNavProps) {\n const { isOpen, onToggle } = useDisclosure()\n const matchRoute = useMatchRoute()\n\n return (\n \u003cBox>\n \u003cFlex\n bg={useColorModeValue('white', 'gray.800')}\n color={useColorModeValue('gray.600', 'white')}\n minH=\"60px\"\n py={{ base: 2 }}\n px={{ base: 4 }}\n borderBottom={1}\n borderStyle=\"solid\"\n borderColor={useColorModeValue('gray.200', 'gray.900')}\n align=\"center\"\n >\n \u003cFlex\n flex={{ base: 1, md: 'auto' }}\n ml={{ base: -2 }}\n display={{ base: 'flex', md: 'none' }}\n >\n \u003cIconButton\n onClick={onToggle}\n icon={\n isOpen ? \u003cCloseIcon w={3} h={3} /> : \u003cHamburgerIcon w={5} h={5} />\n }\n variant=\"ghost\"\n aria-label=\"Toggle Navigation\"\n />\n \u003c/Flex>\n\n \u003cFlex flex={{ base: 1 }} justify={{ base: 'center', md: 'start' }}>\n \u003cRouterLink\n to={brandTo}\n fontFamily=\"heading\"\n fontWeight=\"bold\"\n fontSize=\"xl\"\n color={useColorModeValue('gray.800', 'white')}\n >\n {brand}\n \u003c/RouterLink>\n\n \u003cFlex display={{ base: 'none', md: 'flex' }} ml={10}>\n \u003cDesktopNav items={items} />\n \u003c/Flex>\n \u003c/Flex>\n \u003c/Flex>\n\n \u003cCollapse in={isOpen} animateOpacity>\n \u003cMobileNav items={items} />\n \u003c/Collapse>\n \u003c/Box>\n )\n}\n\nfunction DesktopNav({ items }: { items: NavItem[] }) {\n const matchRoute = useMatchRoute()\n const linkColor = useColorModeValue('gray.600', 'gray.200')\n const linkHoverColor = useColorModeValue('gray.800', 'white')\n\n return (\n \u003cHStack spacing={4}>\n {items.map((item) => {\n const isActive = matchRoute({ to: item.to, fuzzy: !item.exact })\n\n return (\n \u003cRouterLink\n key={item.to}\n to={item.to}\n p={2}\n fontSize=\"sm\"\n fontWeight={isActive ? 'bold' : 'medium'}\n color={isActive ? 'brand.500' : linkColor}\n _hover={{\n textDecoration: 'none',\n color: linkHoverColor,\n }}\n >\n {item.label}\n \u003c/RouterLink>\n )\n })}\n \u003c/HStack>\n )\n}\n\nfunction MobileNav({ items }: { items: NavItem[] }) {\n const matchRoute = useMatchRoute()\n\n return (\n \u003cStack\n bg={useColorModeValue('white', 'gray.800')}\n p={4}\n display={{ md: 'none' }}\n >\n {items.map((item) => {\n const isActive = matchRoute({ to: item.to, fuzzy: !item.exact })\n\n return (\n \u003cRouterLink\n key={item.to}\n to={item.to}\n py={2}\n fontWeight={isActive ? 'bold' : 'medium'}\n color={\n isActive ? 'brand.500' : useColorModeValue('gray.600', 'gray.200')\n }\n _hover={{\n textDecoration: 'none',\n }}\n >\n {item.label}\n \u003c/RouterLink>\n )\n })}\n \u003c/Stack>\n )\n}\n```\n\n### Step 3: Create Breadcrumb Navigation\n\n```tsx\n// src/components/navigation/chakra-breadcrumb.tsx\nimport { useRouter } from '@tanstack/react-router'\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbSeparator,\n} from '@chakra-ui/react'\nimport { ChevronRightIcon } from '@chakra-ui/icons'\nimport { RouterLink } from '@/components/ui/chakra-router-link'\n\ninterface BreadcrumbConfig {\n [key: string]: string\n}\n\ninterface ChakraBreadcrumbProps {\n config?: BreadcrumbConfig\n separator?: React.ReactElement\n}\n\nexport function ChakraBreadcrumb({\n config = {},\n separator = \u003cChevronRightIcon color=\"gray.500\" />,\n}: ChakraBreadcrumbProps) {\n const router = useRouter()\n const pathSegments = router.state.location.pathname.split('/').filter(Boolean)\n\n if (pathSegments.length === 0) return null\n\n const breadcrumbItems = pathSegments.map((segment, index) => {\n const path = '/' + pathSegments.slice(0, index + 1).join('/')\n const label =\n config[segment] || segment.charAt(0).toUpperCase() + segment.slice(1)\n const isLast = index === pathSegments.length - 1\n\n return {\n path,\n label,\n isLast,\n }\n })\n\n return (\n \u003cBreadcrumb spacing=\"8px\" separator={separator}>\n \u003cBreadcrumbItem>\n \u003cBreadcrumbLink as={RouterLink} to=\"/\">\n Home\n \u003c/BreadcrumbLink>\n \u003c/BreadcrumbItem>\n\n {breadcrumbItems.map(({ path, label, isLast }) => (\n \u003cBreadcrumbItem key={path} isCurrentPage={isLast}>\n \u003cBreadcrumbLink\n as={isLast ? 'span' : RouterLink}\n to={isLast ? undefined : path}\n color={isLast ? 'gray.500' : undefined}\n >\n {label}\n \u003c/BreadcrumbLink>\n \u003c/BreadcrumbItem>\n ))}\n \u003c/Breadcrumb>\n )\n}\n```\n\n---\n\n## Responsive Design Patterns\n\n### Step 1: Create Responsive Layout Component\n\n```tsx\n// src/components/layout/chakra-layout.tsx\nimport { ReactNode } from 'react'\nimport {\n Box,\n Container,\n Flex,\n useColorModeValue,\n VStack,\n useBreakpointValue,\n} from '@chakra-ui/react'\nimport { ChakraNav } from '@/components/navigation/chakra-nav'\nimport { ChakraBreadcrumb } from '@/components/navigation/chakra-breadcrumb'\n\ninterface ChakraLayoutProps {\n children: ReactNode\n showBreadcrumb?: boolean\n maxWidth?: string\n}\n\nconst navItems = [\n { label: 'Home', to: '/', exact: true },\n { label: 'Posts', to: '/posts' },\n { label: 'About', to: '/about' },\n { label: 'Contact', to: '/contact' },\n]\n\nexport function ChakraLayout({\n children,\n showBreadcrumb = true,\n maxWidth = 'container.xl',\n}: ChakraLayoutProps) {\n const containerPadding = useBreakpointValue({ base: 4, md: 6 })\n\n return (\n \u003cBox minH=\"100vh\" bg={useColorModeValue('gray.50', 'gray.900')}>\n \u003cChakraNav items={navItems} brand=\"My App\" />\n\n \u003cContainer maxW={maxWidth} py={containerPadding}>\n {showBreadcrumb && (\n \u003cBox mb={6}>\n \u003cChakraBreadcrumb />\n \u003c/Box>\n )}\n\n \u003cBox>{children}\u003c/Box>\n \u003c/Container>\n \u003c/Box>\n )\n}\n```\n\n### Step 2: Create Responsive Card Grid\n\n```tsx\n// src/components/ui/chakra-card-grid.tsx\nimport { ReactNode } from 'react'\nimport { SimpleGrid, Box, useBreakpointValue } from '@chakra-ui/react'\n\ninterface ChakraCardGridProps {\n children: ReactNode\n minChildWidth?: string\n spacing?: number\n}\n\nexport function ChakraCardGrid({\n children,\n minChildWidth = '300px',\n spacing = 6,\n}: ChakraCardGridProps) {\n const columns = useBreakpointValue({\n base: 1,\n md: 2,\n lg: 3,\n xl: 4,\n })\n\n return (\n \u003cSimpleGrid\n columns={columns}\n spacing={spacing}\n minChildWidth={minChildWidth}\n >\n {children}\n \u003c/SimpleGrid>\n )\n}\n```\n\n---\n\n## Complete Usage Examples\n\n### Step 1: Posts List Page\n\n```tsx\n// src/routes/posts/index.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport {\n Box,\n Card,\n CardBody,\n CardHeader,\n Heading,\n Text,\n Badge,\n VStack,\n HStack,\n useColorModeValue,\n} from '@chakra-ui/react'\nimport { ChakraLayout } from '@/components/layout/chakra-layout'\nimport { ChakraCardGrid } from '@/components/ui/chakra-card-grid'\nimport { RouterLink, RouterButton } from '@/components/ui/chakra-router-link'\n\nexport const Route = createFileRoute('/posts/')({\n component: PostsPage,\n})\n\nfunction PostsPage() {\n const posts = [\n {\n id: '1',\n title: 'Getting Started with TanStack Router',\n excerpt: 'Learn how to build type-safe routing in React applications.',\n category: 'Tutorial',\n readTime: '5 min read',\n },\n {\n id: '2',\n title: 'Chakra UI Best Practices',\n excerpt: 'Tips and tricks for building beautiful UIs with Chakra UI.',\n category: 'Design',\n readTime: '8 min read',\n },\n ]\n\n return (\n \u003cChakraLayout>\n \u003cVStack spacing={8} align=\"stretch\">\n \u003cBox>\n \u003cHeading size=\"xl\" mb={4}>\n Blog Posts\n \u003c/Heading>\n \u003cText color={useColorModeValue('gray.600', 'gray.400')}>\n Discover our latest articles and tutorials\n \u003c/Text>\n \u003c/Box>\n\n \u003cBox>\n \u003cRouterButton colorScheme=\"brand\" mb={6}>\n Create New Post\n \u003c/RouterButton>\n\n \u003cChakraCardGrid>\n {posts.map((post) => (\n \u003cPostCard key={post.id} post={post} />\n ))}\n \u003c/ChakraCardGrid>\n \u003c/Box>\n \u003c/VStack>\n \u003c/ChakraLayout>\n )\n}\n\nfunction PostCard({ post }: { post: any }) {\n const cardBg = useColorModeValue('white', 'gray.800')\n const cardBorder = useColorModeValue('gray.200', 'gray.700')\n\n return (\n \u003cCard\n bg={cardBg}\n borderColor={cardBorder}\n borderWidth=\"1px\"\n _hover={{\n shadow: 'lg',\n transform: 'translateY(-2px)',\n transition: 'all 0.2s',\n }}\n >\n \u003cCardHeader pb={3}>\n \u003cHStack justify=\"space-between\" align=\"start\">\n \u003cBadge colorScheme=\"brand\" variant=\"subtle\">\n {post.category}\n \u003c/Badge>\n \u003cText fontSize=\"sm\" color=\"gray.500\">\n {post.readTime}\n \u003c/Text>\n \u003c/HStack>\n \u003c/CardHeader>\n\n \u003cCardBody pt={0}>\n \u003cVStack align=\"start\" spacing={3}>\n \u003cRouterLink to=\"/posts/$postId\" params={{ postId: post.id }}>\n \u003cHeading size=\"md\" _hover={{ color: 'brand.500' }}>\n {post.title}\n \u003c/Heading>\n \u003c/RouterLink>\n\n \u003cText color={useColorModeValue('gray.600', 'gray.400')}>\n {post.excerpt}\n \u003c/Text>\n\n \u003cRouterButton\n to=\"/posts/$postId\"\n params={{ postId: post.id }}\n variant=\"ghost\"\n colorScheme=\"brand\"\n size=\"sm\"\n alignSelf=\"flex-start\"\n >\n Read More →\n \u003c/RouterButton>\n \u003c/VStack>\n \u003c/CardBody>\n \u003c/Card>\n )\n}\n```\n\n### Step 2: Post Detail Page\n\n```tsx\n// src/routes/posts/$postId.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport {\n Box,\n Heading,\n Text,\n VStack,\n HStack,\n Button,\n useColorModeValue,\n Divider,\n Tag,\n TagLabel,\n} from '@chakra-ui/react'\nimport { ArrowBackIcon, EditIcon, DeleteIcon } from '@chakra-ui/icons'\nimport { ChakraLayout } from '@/components/layout/chakra-layout'\nimport { RouterButton, RouterLink } from '@/components/ui/chakra-router-link'\n\nexport const Route = createFileRoute('/posts/$postId')({\n component: PostPage,\n})\n\nfunction PostPage() {\n const { postId } = Route.useParams()\n const textColor = useColorModeValue('gray.600', 'gray.300')\n\n return (\n \u003cChakraLayout>\n \u003cVStack spacing={8} align=\"stretch\">\n {/* Back Navigation */}\n \u003cBox>\n \u003cRouterLink\n to=\"/posts\"\n color=\"brand.500\"\n _hover={{ textDecoration: 'none' }}\n >\n \u003cHStack spacing={2}>\n \u003cArrowBackIcon />\n \u003cText>Back to Posts\u003c/Text>\n \u003c/HStack>\n \u003c/RouterLink>\n \u003c/Box>\n\n {/* Post Header */}\n \u003cVStack spacing={4} align=\"start\">\n \u003cHeading size=\"2xl\">Understanding TanStack Router\u003c/Heading>\n\n \u003cHStack spacing={3}>\n \u003cTag colorScheme=\"brand\">\n \u003cTagLabel>Tutorial\u003c/TagLabel>\n \u003c/Tag>\n \u003cText color={textColor}>March 15, 2024\u003c/Text>\n \u003cText color={textColor}>•\u003c/Text>\n \u003cText color={textColor}>5 min read\u003c/Text>\n \u003c/HStack>\n\n \u003cDivider />\n \u003c/VStack>\n\n {/* Post Content */}\n \u003cBox>\n \u003cVStack spacing={4} align=\"start\">\n \u003cText color={textColor} lineHeight=\"tall\">\n This is the detailed content of post {postId}. In this\n comprehensive guide, we'll explore how to integrate TanStack\n Router with Chakra UI to create beautiful, accessible, and\n responsive web applications.\n \u003c/Text>\n\n \u003cText color={textColor} lineHeight=\"tall\">\n Chakra UI provides a simple, modular, and accessible component\n library that gives you the building blocks you need to build React\n applications with speed.\n \u003c/Text>\n \u003c/VStack>\n \u003c/Box>\n\n {/* Action Buttons */}\n \u003cHStack spacing={4}>\n \u003cRouterButton\n to=\"/posts/$postId/edit\"\n params={{ postId }}\n leftIcon={\u003cEditIcon />}\n colorScheme=\"brand\"\n variant=\"outline\"\n >\n Edit Post\n \u003c/RouterButton>\n\n \u003cButton leftIcon={\u003cDeleteIcon />} colorScheme=\"red\" variant=\"outline\">\n Delete Post\n \u003c/Button>\n \u003c/HStack>\n \u003c/VStack>\n \u003c/ChakraLayout>\n )\n}\n```\n\n---\n\n## Common Problems\n\n### Theme Provider Issues\n\n**Problem:** Chakra theme not applying correctly across routes.\n\n**Solution:** Ensure ChakraProvider wraps the entire app at the root level:\n\n```tsx\n// ❌ Don't put provider inside individual routes\nexport const Route = createFileRoute('/some-route')({\n component: () => (\n \u003cChakraProvider>\n \u003cSomeComponent />\n \u003c/ChakraProvider>\n ),\n})\n\n// ✅ Put provider at root level\nexport const Route = createRootRoute({\n component: () => (\n \u003cChakraProvider>\n \u003cOutlet />\n \u003c/ChakraProvider>\n ),\n})\n```\n\n### TypeScript Errors with Router Integration\n\n**Problem:** TypeScript errors when using Chakra components with TanStack Router.\n\n**Solution:** Use proper typing with `createLink`:\n\n```tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Button, type ButtonProps } from '@chakra-ui/react'\nimport { forwardRef } from 'react'\n\n// Properly typed router button\nexport const RouterButton = createLink(\n forwardRef\u003cHTMLButtonElement, ButtonProps>((props, ref) => {\n return \u003cButton ref={ref} as=\"button\" {...props} />\n }),\n)\n```\n\n### Color Mode Persistence\n\n**Problem:** Color mode doesn't persist across route changes.\n\n**Solution:** Set up proper color mode manager:\n\n```tsx\nimport { ColorModeScript } from '@chakra-ui/react'\n\n// Add to your index.html head\n;\u003cColorModeScript initialColorMode={theme.config.initialColorMode} />\n\n// Or use localStorage manager\nimport { localStorageManager } from '@chakra-ui/react'\n;\u003cChakraProvider theme={theme} colorModeManager={localStorageManager}>\n {children}\n\u003c/ChakraProvider>\n```\n\n### Responsive Design Issues\n\n**Problem:** Responsive breakpoints not working correctly.\n\n**Solution:** Use Chakra's responsive utilities properly:\n\n```tsx\n// ✅ Use breakpoint values correctly\nconst columns = useBreakpointValue({\n base: 1,\n md: 2,\n lg: 3,\n xl: 4,\n})\n\n// ✅ Or use responsive props\n\u003cBox\n display={{ base: 'block', md: 'flex' }}\n flexDirection={{ base: 'column', md: 'row' }}\n>\n```\n\n---\n\n## Production Checklist\n\nBefore deploying your Chakra UI + TanStack Router app:\n\n### Functionality\n\n- [ ] All router-compatible components work correctly\n- [ ] Navigation states properly reflected\n- [ ] Theme persists across route changes\n- [ ] TypeScript compilation successful\n\n### Accessibility\n\n- [ ] Keyboard navigation working\n- [ ] Screen reader compatibility tested\n- [ ] Color contrast meets WCAG standards\n- [ ] Focus management working properly\n\n### Performance\n\n- [ ] Bundle size optimized\n- [ ] Color mode switching performant\n- [ ] No unnecessary re-renders\n- [ ] Images and icons optimized\n\n### Responsiveness\n\n- [ ] Works on mobile devices\n- [ ] Tablet layouts functional\n- [ ] Desktop experience optimal\n- [ ] Breakpoints working correctly\n\n---\n\n## Related Resources\n\n- Chakra UI Documentation - Complete component library guide\n- Chakra UI Recipes - Common patterns and recipes\n- [TanStack Router createLink API](../api/router#createlink) - API documentation for component integration\n- Emotion CSS-in-JS - Styling library used by Chakra UI\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":19732,"content_sha256":"058df15fb4afa5c62d8d898fdc50173727cadce14350026685bcf0c97faaad72"},{"filename":"references/docs/router/how-to/integrate-framer-motion.md","content":"---\ntitle: How to Integrate TanStack Router with Framer Motion\n---\n\nThis guide covers setting up Framer Motion with TanStack Router for smooth route transitions and navigation animations.\n\n## Quick Start\n\n**Time Required:** 30-45 minutes \n**Difficulty:** Intermediate \n**Prerequisites:** Existing TanStack Router project\n\n### What You'll Accomplish\n\n- Install and configure Framer Motion with TanStack Router\n- Create smooth route transition animations\n- Implement animated navigation components\n- Set up layout animations and shared elements\n- Handle complex animation sequences\n\n---\n\n## Installation and Setup\n\n### Step 1: Install Framer Motion\n\n```bash\nnpm install framer-motion\n```\n\n### Step 2: Verify Version Compatibility\n\nEnsure you're using compatible versions:\n\n```json\n{\n \"dependencies\": {\n \"@tanstack/react-router\": \"^1.0.0\",\n \"framer-motion\": \"^11.0.0\",\n \"react\": \"^18.0.0\"\n }\n}\n```\n\n---\n\n## Route Transition Animations\n\n### Step 1: Create Animated Route Wrapper\n\n```tsx\n// src/components/animated-route.tsx\nimport { motion, type MotionProps, type Variants } from 'framer-motion'\nimport { ReactNode } from 'react'\n\ninterface AnimatedRouteProps extends MotionProps {\n children: ReactNode\n variant?: 'fade' | 'slide' | 'scale' | 'slideUp'\n}\n\nconst routeVariants: Record\u003cstring, Variants> = {\n fade: {\n initial: { opacity: 0 },\n in: { opacity: 1 },\n out: { opacity: 0 },\n },\n slide: {\n initial: { opacity: 0, x: -20 },\n in: { opacity: 1, x: 0 },\n out: { opacity: 0, x: 20 },\n },\n scale: {\n initial: { opacity: 0, scale: 0.95 },\n in: { opacity: 1, scale: 1 },\n out: { opacity: 0, scale: 1.05 },\n },\n slideUp: {\n initial: { opacity: 0, y: 20 },\n in: { opacity: 1, y: 0 },\n out: { opacity: 0, y: -20 },\n },\n}\n\nconst pageTransition = {\n type: 'tween',\n ease: 'anticipate',\n duration: 0.3,\n}\n\nexport function AnimatedRoute({\n children,\n variant = 'fade',\n ...motionProps\n}: AnimatedRouteProps) {\n return (\n \u003cmotion.div\n initial=\"initial\"\n animate=\"in\"\n exit=\"out\"\n variants={routeVariants[variant]}\n transition={pageTransition}\n {...motionProps}\n >\n {children}\n \u003c/motion.div>\n )\n}\n```\n\n### Step 2: Set Up Route Animation Container\n\n```tsx\n// src/components/route-animation-container.tsx\nimport { useRouter } from '@tanstack/react-router'\nimport { AnimatePresence } from 'framer-motion'\nimport { ReactNode } from 'react'\n\ninterface RouteAnimationContainerProps {\n children: ReactNode\n}\n\nexport function RouteAnimationContainer({\n children,\n}: RouteAnimationContainerProps) {\n const router = useRouter()\n\n return (\n \u003cAnimatePresence mode=\"wait\" initial={false}>\n \u003cdiv key={router.state.location.pathname}>{children}\u003c/div>\n \u003c/AnimatePresence>\n )\n}\n```\n\n### Step 3: Update Root Route for Animations\n\n```tsx\n// src/routes/__root.tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/router-devtools'\nimport { RouteAnimationContainer } from '@/components/route-animation-container'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003c>\n \u003cRouteAnimationContainer>\n \u003cOutlet />\n \u003c/RouteAnimationContainer>\n \u003cTanStackRouterDevtools />\n \u003c/>\n ),\n})\n```\n\n### Step 4: Use Animations in Routes\n\n```tsx\n// src/routes/posts/index.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { motion } from 'framer-motion'\nimport { AnimatedRoute } from '@/components/animated-route'\n\nexport const Route = createFileRoute('/posts/')({\n component: PostsPage,\n})\n\nfunction PostsPage() {\n return (\n \u003cAnimatedRoute variant=\"slide\">\n \u003cdiv className=\"container mx-auto p-4\">\n \u003cmotion.h1\n initial={{ opacity: 0, y: -20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ delay: 0.1 }}\n className=\"text-3xl font-bold mb-6\"\n >\n Posts\n \u003c/motion.h1>\n\n \u003cmotion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ delay: 0.2 }}\n className=\"grid gap-4\"\n >\n {/* Post cards with staggered animations */}\n {posts.map((post, index) => (\n \u003cmotion.div\n key={post.id}\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ delay: 0.3 + index * 0.1 }}\n className=\"border rounded-lg p-4\"\n >\n \u003ch2 className=\"text-xl font-semibold\">{post.title}\u003c/h2>\n \u003cp className=\"text-gray-600\">{post.excerpt}\u003c/p>\n \u003c/motion.div>\n ))}\n \u003c/motion.div>\n \u003c/div>\n \u003c/AnimatedRoute>\n )\n}\n```\n\n---\n\n## Animated Navigation Components\n\n### Step 1: Create Animated Tab Navigation\n\n```tsx\n// src/components/navigation/animated-tabs.tsx\nimport { Link, useMatchRoute } from '@tanstack/react-router'\nimport { motion } from 'framer-motion'\n\ninterface TabItem {\n to: string\n label: string\n exact?: boolean\n}\n\ninterface AnimatedTabsProps {\n items: TabItem[]\n className?: string\n}\n\nexport function AnimatedTabs({ items, className }: AnimatedTabsProps) {\n const matchRoute = useMatchRoute()\n\n return (\n \u003cnav className={`flex space-x-1 p-2 bg-gray-100 rounded-lg ${className}`}>\n {items.map((item) => {\n const isActive = matchRoute({ to: item.to, fuzzy: !item.exact })\n\n return (\n \u003cLink\n key={item.to}\n to={item.to}\n className={`relative px-3 py-2 rounded-md text-sm font-medium transition-colors ${\n isActive ? 'text-blue-600' : 'text-gray-600 hover:text-gray-900'\n }`}\n >\n {isActive && (\n \u003cmotion.div\n layoutId=\"activeTab\"\n className=\"absolute inset-0 bg-white rounded-md shadow-sm\"\n initial={false}\n transition={{\n type: 'spring',\n bounce: 0.2,\n duration: 0.6,\n }}\n />\n )}\n \u003cspan className=\"relative z-10\">{item.label}\u003c/span>\n \u003c/Link>\n )\n })}\n \u003c/nav>\n )\n}\n```\n\n### Step 2: Create Sliding Mobile Menu\n\n```tsx\n// src/components/navigation/animated-mobile-menu.tsx\nimport { useState } from 'react'\nimport { Link } from '@tanstack/react-router'\nimport { motion, AnimatePresence } from 'framer-motion'\n\ninterface MenuItem {\n to: string\n label: string\n icon?: React.ReactNode\n}\n\ninterface AnimatedMobileMenuProps {\n items: MenuItem[]\n trigger: React.ReactNode\n}\n\nexport function AnimatedMobileMenu({\n items,\n trigger,\n}: AnimatedMobileMenuProps) {\n const [isOpen, setIsOpen] = useState(false)\n\n const menuVariants = {\n closed: {\n opacity: 0,\n x: '-100%',\n transition: {\n type: 'spring',\n stiffness: 400,\n damping: 40,\n },\n },\n open: {\n opacity: 1,\n x: 0,\n transition: {\n type: 'spring',\n stiffness: 400,\n damping: 40,\n },\n },\n }\n\n const itemVariants = {\n closed: { opacity: 0, x: -20 },\n open: { opacity: 1, x: 0 },\n }\n\n return (\n \u003c>\n {/* Trigger */}\n \u003cbutton onClick={() => setIsOpen(!isOpen)}>{trigger}\u003c/button>\n\n {/* Overlay */}\n \u003cAnimatePresence>\n {isOpen && (\n \u003cmotion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 bg-black bg-opacity-50 z-40\"\n onClick={() => setIsOpen(false)}\n />\n )}\n \u003c/AnimatePresence>\n\n {/* Menu */}\n \u003cmotion.nav\n initial=\"closed\"\n animate={isOpen ? 'open' : 'closed'}\n variants={menuVariants}\n className=\"fixed top-0 left-0 h-full w-64 bg-white shadow-lg z-50\"\n >\n \u003cdiv className=\"p-4\">\n \u003cmotion.div\n initial=\"closed\"\n animate={isOpen ? 'open' : 'closed'}\n transition={{ staggerChildren: 0.1, delayChildren: 0.2 }}\n className=\"space-y-2\"\n >\n {items.map((item) => (\n \u003cmotion.div key={item.to} variants={itemVariants}>\n \u003cLink\n to={item.to}\n className=\"flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-100 transition-colors\"\n onClick={() => setIsOpen(false)}\n >\n {item.icon}\n \u003cspan className=\"text-gray-700\">{item.label}\u003c/span>\n \u003c/Link>\n \u003c/motion.div>\n ))}\n \u003c/motion.div>\n \u003c/div>\n \u003c/motion.nav>\n \u003c/>\n )\n}\n```\n\n### Step 3: Create Floating Action Button with Animations\n\n```tsx\n// src/components/navigation/animated-fab.tsx\nimport { Link } from '@tanstack/react-router'\nimport { motion } from 'framer-motion'\nimport { Plus } from 'lucide-react'\n\ninterface AnimatedFabProps {\n to: string\n label?: string\n icon?: React.ReactNode\n className?: string\n}\n\nexport function AnimatedFab({\n to,\n label = 'Add',\n icon = \u003cPlus className=\"w-6 h-6\" />,\n className = '',\n}: AnimatedFabProps) {\n return (\n \u003cmotion.div\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n className={`fixed bottom-6 right-6 ${className}`}\n >\n \u003cLink\n to={to}\n className=\"flex items-center space-x-2 bg-blue-600 text-white px-6 py-3 rounded-full shadow-lg hover:bg-blue-700 transition-colors\"\n >\n \u003cmotion.div\n initial={{ rotate: 0 }}\n whileHover={{ rotate: 90 }}\n transition={{ type: 'spring', stiffness: 300 }}\n >\n {icon}\n \u003c/motion.div>\n \u003cspan className=\"font-medium\">{label}\u003c/span>\n \u003c/Link>\n \u003c/motion.div>\n )\n}\n```\n\n---\n\n## Advanced Animation Patterns\n\n### Step 1: Shared Element Transitions\n\n```tsx\n// src/components/animations/shared-element.tsx\nimport { motion } from 'framer-motion'\nimport { ReactNode } from 'react'\n\ninterface SharedElementProps {\n layoutId: string\n children: ReactNode\n className?: string\n}\n\nexport function SharedElement({\n layoutId,\n children,\n className,\n}: SharedElementProps) {\n return (\n \u003cmotion.div\n layoutId={layoutId}\n className={className}\n transition={{\n type: 'spring',\n stiffness: 300,\n damping: 30,\n }}\n >\n {children}\n \u003c/motion.div>\n )\n}\n\n// Usage in post list\nfunction PostCard({ post }: { post: Post }) {\n return (\n \u003cLink to=\"/posts/$postId\" params={{ postId: post.id }}>\n \u003cSharedElement layoutId={`post-${post.id}`}>\n \u003cdiv className=\"border rounded-lg p-4\">\n \u003ch2 className=\"text-xl font-semibold\">{post.title}\u003c/h2>\n \u003cp className=\"text-gray-600\">{post.excerpt}\u003c/p>\n \u003c/div>\n \u003c/SharedElement>\n \u003c/Link>\n )\n}\n\n// Usage in post detail\nfunction PostDetail({ post }: { post: Post }) {\n return (\n \u003cSharedElement layoutId={`post-${post.id}`}>\n \u003cdiv className=\"border rounded-lg p-6\">\n \u003ch1 className=\"text-3xl font-bold\">{post.title}\u003c/h1>\n \u003cdiv className=\"prose mt-4\">{post.content}\u003c/div>\n \u003c/div>\n \u003c/SharedElement>\n )\n}\n```\n\n### Step 2: Route-Based Animation Variants\n\n```tsx\n// src/components/animations/route-variants.tsx\nimport { motion } from 'framer-motion'\nimport { useRouter } from '@tanstack/react-router'\nimport { ReactNode } from 'react'\n\ninterface RouteVariantsProps {\n children: ReactNode\n}\n\nexport function RouteVariants({ children }: RouteVariantsProps) {\n const router = useRouter()\n const currentPath = router.state.location.pathname\n\n // Different animations based on route depth\n const getVariants = (path: string) => {\n const depth = path.split('/').length - 1\n\n if (depth === 1) {\n // Top-level routes slide from right\n return {\n initial: { opacity: 0, x: 100 },\n in: { opacity: 1, x: 0 },\n out: { opacity: 0, x: -100 },\n }\n } else if (depth === 2) {\n // Sub-routes slide up\n return {\n initial: { opacity: 0, y: 50 },\n in: { opacity: 1, y: 0 },\n out: { opacity: 0, y: -50 },\n }\n } else {\n // Deep routes fade\n return {\n initial: { opacity: 0 },\n in: { opacity: 1 },\n out: { opacity: 0 },\n }\n }\n }\n\n return (\n \u003cmotion.div\n key={currentPath}\n initial=\"initial\"\n animate=\"in\"\n exit=\"out\"\n variants={getVariants(currentPath)}\n transition={{\n type: 'spring',\n stiffness: 300,\n damping: 30,\n }}\n >\n {children}\n \u003c/motion.div>\n )\n}\n```\n\n### Step 3: Loading Animations\n\n```tsx\n// src/components/animations/loading-animation.tsx\nimport { motion } from 'framer-motion'\n\nexport function LoadingAnimation() {\n return (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n \u003cmotion.div\n className=\"flex space-x-2\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n >\n {[0, 1, 2].map((index) => (\n \u003cmotion.div\n key={index}\n className=\"w-3 h-3 bg-blue-600 rounded-full\"\n animate={{\n scale: [1, 1.2, 1],\n opacity: [1, 0.8, 1],\n }}\n transition={{\n duration: 1,\n repeat: Infinity,\n delay: index * 0.2,\n }}\n />\n ))}\n \u003c/motion.div>\n \u003c/div>\n )\n}\n\n// Usage in routes with loading states\nexport const Route = createFileRoute('/posts/$postId')({\n component: PostPage,\n pendingComponent: LoadingAnimation,\n})\n```\n\n---\n\n## Complete Example\n\n### App with Full Animation Integration\n\n```tsx\n// src/routes/posts/index.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { motion } from 'framer-motion'\nimport { AnimatedRoute } from '@/components/animated-route'\nimport { AnimatedTabs } from '@/components/navigation/animated-tabs'\nimport { AnimatedFab } from '@/components/navigation/animated-fab'\nimport { SharedElement } from '@/components/animations/shared-element'\n\nexport const Route = createFileRoute('/posts/')({\n component: PostsPage,\n})\n\nconst tabItems = [\n { to: '/posts', label: 'All Posts', exact: true },\n { to: '/posts/published', label: 'Published' },\n { to: '/posts/drafts', label: 'Drafts' },\n]\n\nfunction PostsPage() {\n const posts = [\n { id: '1', title: 'First Post', excerpt: 'This is the first post' },\n { id: '2', title: 'Second Post', excerpt: 'This is the second post' },\n ]\n\n return (\n \u003cAnimatedRoute variant=\"slide\">\n \u003cdiv className=\"container mx-auto p-4\">\n {/* Animated header */}\n \u003cmotion.div\n initial={{ opacity: 0, y: -20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ delay: 0.1 }}\n className=\"mb-6\"\n >\n \u003ch1 className=\"text-3xl font-bold mb-4\">Posts\u003c/h1>\n \u003cAnimatedTabs items={tabItems} />\n \u003c/motion.div>\n\n {/* Animated post grid */}\n \u003cmotion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ delay: 0.2 }}\n className=\"grid gap-4\"\n >\n {posts.map((post, index) => (\n \u003cmotion.div\n key={post.id}\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ delay: 0.3 + index * 0.1 }}\n whileHover={{ y: -2 }}\n className=\"cursor-pointer\"\n >\n \u003cSharedElement layoutId={`post-${post.id}`}>\n \u003cdiv className=\"border rounded-lg p-4 hover:shadow-lg transition-shadow\">\n \u003ch2 className=\"text-xl font-semibold\">{post.title}\u003c/h2>\n \u003cp className=\"text-gray-600\">{post.excerpt}\u003c/p>\n \u003c/div>\n \u003c/SharedElement>\n \u003c/motion.div>\n ))}\n \u003c/motion.div>\n\n {/* Floating action button */}\n \u003cAnimatedFab to=\"/posts/new\" label=\"New Post\" />\n \u003c/div>\n \u003c/AnimatedRoute>\n )\n}\n```\n\n---\n\n## Common Problems\n\n### Animations Not Triggering\n\n**Problem:** Route animations don't work or appear choppy.\n\n**Solutions:**\n\n1. **Ensure proper key for AnimatePresence:**\n\n ```tsx\n \u003cAnimatePresence mode=\"wait\">\n \u003cmotion.div key={router.state.location.pathname}>\n \u003cOutlet />\n \u003c/motion.div>\n \u003c/AnimatePresence>\n ```\n\n2. **Use layout animations correctly:**\n\n ```tsx\n // ❌ This might cause layout shifts\n \u003cmotion.div animate={{ x: 100 }}>\n\n // ✅ Use layout for changing layouts\n \u003cmotion.div layout>\n ```\n\n### Performance Issues\n\n**Problem:** Animations cause performance problems or jank.\n\n**Solutions:**\n\n1. **Prefer transform and opacity animations:**\n\n ```tsx\n // ✅ GPU-accelerated properties\n const variants = {\n initial: { opacity: 0, scale: 0.95 },\n in: { opacity: 1, scale: 1 },\n }\n\n // ❌ Avoid animating layout properties\n const badVariants = {\n initial: { width: 0, height: 0 },\n in: { width: 'auto', height: 'auto' },\n }\n ```\n\n2. **Use will-change CSS property sparingly:**\n ```tsx\n \u003cmotion.div style={{ willChange: 'transform' }} animate={{ x: 100 }} />\n ```\n\n### Layout Shift Issues\n\n**Problem:** Shared element transitions cause layout shifts.\n\n**Solution:** Use layout animations and proper positioning:\n\n```tsx\n\u003cmotion.div\n layout\n layoutId=\"shared-element\"\n style={{ position: 'relative' }}\n transition={{\n layout: { duration: 0.3 },\n }}\n>\n {children}\n\u003c/motion.div>\n```\n\n---\n\n## Production Checklist\n\nBefore deploying your animated TanStack Router app:\n\n### Performance\n\n- [ ] Animations use GPU-accelerated properties (transform, opacity)\n- [ ] No unnecessary will-change CSS properties\n- [ ] Complex animations are conditional on user preferences\n- [ ] Frame rate stays above 60fps on target devices\n\n### User Experience\n\n- [ ] Animations respect user's motion preferences\n- [ ] Loading states have appropriate animations\n- [ ] Navigation feels responsive and smooth\n- [ ] Animations enhance rather than distract from content\n\n### Accessibility\n\n- [ ] Respect prefers-reduced-motion media query\n- [ ] Animations don't interfere with screen readers\n- [ ] Focus management works during transitions\n- [ ] Essential content isn't hidden behind animations\n\n### Technical\n\n- [ ] Bundle size impact acceptable\n- [ ] No animation-related console errors\n- [ ] Smooth transitions on slower devices\n- [ ] Proper cleanup of animation effects\n\n---\n\n## Related Resources\n\n- Framer Motion Documentation - Complete animation library guide\n- React Transition Group Migration - Migration guide from other animation libraries\n- Animation Performance - Web performance guide for animations\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18681,"content_sha256":"a99ef52f3ac8ffee475b960ab0e5af248a3ed2e02b360539b169e97c145770ac"},{"filename":"references/docs/router/how-to/integrate-material-ui.md","content":"---\ntitle: How to Integrate TanStack Router with Material-UI (MUI)\n---\n\nThis guide covers setting up Material-UI with TanStack Router, including proper TypeScript integration and component composition patterns.\n\n## Quick Start\n\n**Time Required:** 45-60 minutes \n**Difficulty:** Intermediate \n**Prerequisites:** Existing TanStack Router project\n\n### What You'll Accomplish\n\n- Install and configure Material-UI with TanStack Router\n- Set up proper theme provider integration\n- Create type-safe router-compatible MUI components\n- Implement navigation with active state indicators\n- Resolve common TypeScript and styling issues\n\n---\n\n## Installation and Setup\n\n### Step 1: Install Material-UI Dependencies\n\n```bash\nnpm install @mui/material @emotion/react @emotion/styled @mui/icons-material\n```\n\n**Optional: Add date picker support**\n\n```bash\nnpm install @mui/x-date-pickers dayjs\n```\n\n### Step 2: Set Up Theme Provider\n\nCreate a theme provider that works with TanStack Router:\n\n```tsx\n// src/components/theme-provider.tsx\nimport { ThemeProvider, createTheme } from '@mui/material/styles'\nimport CssBaseline from '@mui/material/CssBaseline'\nimport { ReactNode } from 'react'\n\nconst theme = createTheme({\n palette: {\n mode: 'light',\n primary: {\n main: '#1976d2',\n },\n secondary: {\n main: '#dc004e',\n },\n },\n typography: {\n fontFamily: '\"Roboto\", \"Helvetica\", \"Arial\", sans-serif',\n },\n components: {\n // Customize components for router integration\n MuiButton: {\n styleOverrides: {\n root: {\n textTransform: 'none', // More modern button styling\n },\n },\n },\n MuiLink: {\n styleOverrides: {\n root: {\n textDecoration: 'none',\n '&:hover': {\n textDecoration: 'underline',\n },\n },\n },\n },\n },\n})\n\ninterface MuiThemeProviderProps {\n children: ReactNode\n}\n\nexport function MuiThemeProvider({ children }: MuiThemeProviderProps) {\n return (\n \u003cThemeProvider theme={theme}>\n \u003cCssBaseline />\n {children}\n \u003c/ThemeProvider>\n )\n}\n```\n\n### Step 3: Update Root Route\n\nWrap your application with the MUI theme provider:\n\n```tsx\n// src/routes/__root.tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/router-devtools'\nimport { MuiThemeProvider } from '@/components/theme-provider'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003cMuiThemeProvider>\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/MuiThemeProvider>\n ),\n})\n```\n\n---\n\n## Creating Router-Compatible MUI Components\n\n### Step 1: Create Typed MUI Link Component\n\nMUI Link components require special handling for TanStack Router's type system:\n\n```tsx\n// src/components/ui/mui-router-link.tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Link as MuiLink, type LinkProps } from '@mui/material/Link'\nimport { forwardRef } from 'react'\n\n// Create a router-compatible MUI Link with full type safety\nexport const RouterLink = createLink(\n forwardRef\u003cHTMLAnchorElement, LinkProps>((props, ref) => {\n return \u003cMuiLink ref={ref} {...props} />\n }),\n)\n```\n\n### Step 2: Create Typed MUI Button Component\n\n```tsx\n// src/components/ui/mui-router-button.tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Button, type ButtonProps } from '@mui/material/Button'\nimport { forwardRef } from 'react'\n\n// Create a router-compatible MUI Button\nexport const RouterButton = createLink(\n forwardRef\u003cHTMLButtonElement, ButtonProps>((props, ref) => {\n return \u003cButton ref={ref} component=\"button\" {...props} />\n }),\n)\n```\n\n### Step 3: Create Advanced Navigation Components\n\n```tsx\n// src/components/ui/mui-router-fab.tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Fab, type FabProps } from '@mui/material/Fab'\nimport { forwardRef } from 'react'\n\n// Router-compatible Floating Action Button\nexport const RouterFab = createLink(\n forwardRef\u003cHTMLButtonElement, FabProps>((props, ref) => {\n return \u003cFab ref={ref} {...props} />\n }),\n)\n```\n\n---\n\n## Implementing Navigation with Active States\n\n### Step 1: Create Navigation Tabs with Active States\n\n```tsx\n// src/components/navigation/mui-nav-tabs.tsx\nimport { useMatchRoute } from '@tanstack/react-router'\nimport { Tabs, Tab, type TabsProps } from '@mui/material'\nimport { RouterLink } from '@/components/ui/mui-router-link'\n\ninterface NavTab {\n label: string\n to: string\n value: string\n icon?: React.ReactNode\n}\n\ninterface MuiNavTabsProps extends Omit\u003cTabsProps, 'value' | 'onChange'> {\n tabs: NavTab[]\n}\n\nexport function MuiNavTabs({ tabs, ...tabsProps }: MuiNavTabsProps) {\n const matchRoute = useMatchRoute()\n\n // Find active tab based on current route\n const activeTab =\n tabs.find((tab) => matchRoute({ to: tab.to, fuzzy: true }))?.value || false\n\n return (\n \u003cTabs value={activeTab} {...tabsProps}>\n {tabs.map((tab) => (\n \u003cTab\n key={tab.value}\n label={tab.label}\n value={tab.value}\n icon={tab.icon}\n component={RouterLink}\n to={tab.to}\n sx={{\n '&.Mui-selected': {\n fontWeight: 'bold',\n },\n }}\n />\n ))}\n \u003c/Tabs>\n )\n}\n```\n\n### Step 2: Create Navigation Drawer\n\n```tsx\n// src/components/navigation/mui-nav-drawer.tsx\nimport { useMatchRoute } from '@tanstack/react-router'\nimport {\n Drawer,\n List,\n ListItem,\n ListItemButton,\n ListItemIcon,\n ListItemText,\n Typography,\n Box,\n type DrawerProps,\n} from '@mui/material'\nimport { RouterLink } from '@/components/ui/mui-router-link'\n\ninterface DrawerItem {\n label: string\n to: string\n icon?: React.ReactNode\n}\n\ninterface MuiNavDrawerProps extends Omit\u003cDrawerProps, 'children'> {\n items: DrawerItem[]\n title?: string\n}\n\nexport function MuiNavDrawer({\n items,\n title,\n ...drawerProps\n}: MuiNavDrawerProps) {\n const matchRoute = useMatchRoute()\n\n return (\n \u003cDrawer {...drawerProps}>\n \u003cBox sx={{ width: 250 }} role=\"presentation\">\n {title && (\n \u003cTypography\n variant=\"h6\"\n sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}\n >\n {title}\n \u003c/Typography>\n )}\n\n \u003cList>\n {items.map((item) => {\n const isActive = matchRoute({ to: item.to, fuzzy: true })\n\n return (\n \u003cListItem key={item.to} disablePadding>\n \u003cListItemButton\n component={RouterLink}\n to={item.to}\n selected={isActive}\n sx={{\n '&.Mui-selected': {\n backgroundColor: 'primary.main',\n color: 'primary.contrastText',\n '&:hover': {\n backgroundColor: 'primary.dark',\n },\n },\n }}\n >\n {item.icon && \u003cListItemIcon>{item.icon}\u003c/ListItemIcon>}\n \u003cListItemText primary={item.label} />\n \u003c/ListItemButton>\n \u003c/ListItem>\n )\n })}\n \u003c/List>\n \u003c/Box>\n \u003c/Drawer>\n )\n}\n```\n\n### Step 3: Create App Bar with Navigation\n\n```tsx\n// src/components/navigation/mui-app-bar.tsx\nimport { useState } from 'react'\nimport {\n AppBar,\n Toolbar,\n Typography,\n IconButton,\n Menu,\n MenuItem,\n Box,\n} from '@mui/material'\nimport { Menu as MenuIcon, AccountCircle } from '@mui/icons-material'\nimport { RouterButton, RouterLink } from '@/components/ui/mui-router-link'\nimport { MuiNavDrawer } from './mui-nav-drawer'\n\ninterface AppBarItem {\n label: string\n to: string\n icon?: React.ReactNode\n}\n\ninterface MuiAppBarProps {\n title: string\n navigationItems: AppBarItem[]\n userMenuItems?: AppBarItem[]\n}\n\nexport function MuiAppBar({\n title,\n navigationItems,\n userMenuItems,\n}: MuiAppBarProps) {\n const [drawerOpen, setDrawerOpen] = useState(false)\n const [userMenuAnchor, setUserMenuAnchor] = useState\u003cnull | HTMLElement>(null)\n\n const handleUserMenuClick = (event: React.MouseEvent\u003cHTMLElement>) => {\n setUserMenuAnchor(event.currentTarget)\n }\n\n const handleUserMenuClose = () => {\n setUserMenuAnchor(null)\n }\n\n return (\n \u003c>\n \u003cAppBar position=\"static\">\n \u003cToolbar>\n \u003cIconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={() => setDrawerOpen(true)}\n sx={{ mr: 2 }}\n >\n \u003cMenuIcon />\n \u003c/IconButton>\n\n \u003cTypography variant=\"h6\" component=\"div\" sx={{ flexGrow: 1 }}>\n \u003cRouterLink to=\"/\" color=\"inherit\" underline=\"none\">\n {title}\n \u003c/RouterLink>\n \u003c/Typography>\n\n {/* Desktop Navigation */}\n \u003cBox sx={{ display: { xs: 'none', md: 'flex' }, mr: 2 }}>\n {navigationItems.map((item) => (\n \u003cRouterButton\n key={item.to}\n to={item.to}\n color=\"inherit\"\n startIcon={item.icon}\n sx={{ ml: 1 }}\n >\n {item.label}\n \u003c/RouterButton>\n ))}\n \u003c/Box>\n\n {/* User Menu */}\n {userMenuItems && (\n \u003c>\n \u003cIconButton color=\"inherit\" onClick={handleUserMenuClick}>\n \u003cAccountCircle />\n \u003c/IconButton>\n \u003cMenu\n anchorEl={userMenuAnchor}\n open={Boolean(userMenuAnchor)}\n onClose={handleUserMenuClose}\n >\n {userMenuItems.map((item) => (\n \u003cMenuItem\n key={item.to}\n component={RouterLink}\n to={item.to}\n onClick={handleUserMenuClose}\n >\n {item.label}\n \u003c/MenuItem>\n ))}\n \u003c/Menu>\n \u003c/>\n )}\n \u003c/Toolbar>\n \u003c/AppBar>\n\n {/* Mobile Navigation Drawer */}\n \u003cMuiNavDrawer\n items={navigationItems}\n title=\"Navigation\"\n open={drawerOpen}\n onClose={() => setDrawerOpen(false)}\n />\n \u003c/>\n )\n}\n```\n\n---\n\n## Usage Examples\n\n### Complete Page Example\n\n```tsx\n// src/routes/posts/$postId.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport {\n Container,\n Typography,\n Box,\n Card,\n CardContent,\n CardActions,\n Chip,\n Stack,\n} from '@mui/material'\nimport { Edit, Delete, ArrowBack } from '@mui/icons-material'\nimport { RouterButton, RouterLink } from '@/components/ui/mui-router-link'\n\nexport const Route = createFileRoute('/posts/$postId')({\n component: PostPage,\n})\n\nfunction PostPage() {\n const { postId } = Route.useParams()\n\n return (\n \u003cContainer maxWidth=\"md\" sx={{ py: 4 }}>\n {/* Breadcrumb Navigation */}\n \u003cBox sx={{ mb: 3 }}>\n \u003cRouterLink\n to=\"/posts\"\n color=\"primary\"\n sx={{ display: 'flex', alignItems: 'center', mb: 2 }}\n >\n \u003cArrowBack sx={{ mr: 1 }} />\n Back to Posts\n \u003c/RouterLink>\n \u003c/Box>\n\n {/* Post Content */}\n \u003cCard>\n \u003cCardContent>\n \u003cTypography variant=\"h4\" component=\"h1\" gutterBottom>\n Post {postId}\n \u003c/Typography>\n\n \u003cStack direction=\"row\" spacing={1} sx={{ mb: 2 }}>\n \u003cChip label=\"React\" color=\"primary\" size=\"small\" />\n \u003cChip label=\"TypeScript\" color=\"secondary\" size=\"small\" />\n \u003c/Stack>\n\n \u003cTypography variant=\"body1\" paragraph>\n This is the content of post {postId}. It demonstrates how\n Material-UI components work seamlessly with TanStack Router.\n \u003c/Typography>\n \u003c/CardContent>\n\n \u003cCardActions>\n \u003cRouterButton\n to=\"/posts/$postId/edit\"\n params={{ postId }}\n variant=\"contained\"\n startIcon={\u003cEdit />}\n size=\"small\"\n >\n Edit Post\n \u003c/RouterButton>\n\n \u003cRouterButton\n to=\"/posts/$postId/delete\"\n params={{ postId }}\n variant=\"outlined\"\n color=\"error\"\n startIcon={\u003cDelete />}\n size=\"small\"\n >\n Delete Post\n \u003c/RouterButton>\n \u003c/CardActions>\n \u003c/Card>\n \u003c/Container>\n )\n}\n```\n\n### Layout with Navigation\n\n```tsx\n// src/routes/_layout.tsx\nimport { createFileRoute, Outlet } from '@tanstack/react-router'\nimport { Box } from '@mui/material'\nimport { Home, Article, Info, Contact } from '@mui/icons-material'\nimport { MuiAppBar } from '@/components/navigation/mui-app-bar'\n\nexport const Route = createFileRoute('/_layout')({\n component: LayoutComponent,\n})\n\nconst navigationItems = [\n { label: 'Home', to: '/', icon: \u003cHome /> },\n { label: 'Posts', to: '/posts', icon: \u003cArticle /> },\n { label: 'About', to: '/about', icon: \u003cInfo /> },\n { label: 'Contact', to: '/contact', icon: \u003cContact /> },\n]\n\nconst userMenuItems = [\n { label: 'Profile', to: '/profile' },\n { label: 'Settings', to: '/settings' },\n { label: 'Logout', to: '/logout' },\n]\n\nfunction LayoutComponent() {\n return (\n \u003cBox sx={{ flexGrow: 1 }}>\n \u003cMuiAppBar\n title=\"My App\"\n navigationItems={navigationItems}\n userMenuItems={userMenuItems}\n />\n\n \u003cBox component=\"main\" sx={{ mt: 2 }}>\n \u003cOutlet />\n \u003c/Box>\n \u003c/Box>\n )\n}\n```\n\n---\n\n## Common Problems\n\n### TypeScript Errors with Component Props\n\n**Problem:** TypeScript errors when using MUI components with TanStack Router props.\n\n**Solution:** Always use `createLink` for proper typing:\n\n```tsx\n// ❌ This will cause TypeScript errors\nconst BadButton = (props: any) => \u003cButton {...props} />\n\n// ✅ This provides full type safety\nexport const RouterButton = createLink(\n forwardRef\u003cHTMLButtonElement, ButtonProps>((props, ref) => {\n return \u003cButton ref={ref} component=\"button\" {...props} />\n }),\n)\n```\n\n### Styling Conflicts\n\n**Problem:** MUI styles conflict with other libraries or custom styles.\n\n**Solutions:**\n\n1. **Use MUI's emotion cache:**\n\n ```tsx\n import { CacheProvider } from '@emotion/react'\n import createCache from '@emotion/cache'\n\n const cache = createCache({\n key: 'mui',\n prepend: true,\n })\n\n export function App() {\n return (\n \u003cCacheProvider value={cache}>\n \u003cMuiThemeProvider>{/* Your app */}\u003c/MuiThemeProvider>\n \u003c/CacheProvider>\n )\n }\n ```\n\n2. **Increase CSS specificity:**\n ```tsx\n const StyledButton = styled(Button)(({ theme }) => ({\n '&.router-active': {\n backgroundColor: theme.palette.primary.main,\n color: theme.palette.primary.contrastText,\n },\n }))\n ```\n\n### Theme Not Applied Correctly\n\n**Problem:** MUI theme changes don't apply to router-created components.\n\n**Solution:** Ensure theme provider wraps the entire app:\n\n```tsx\n// ❌ Theme provider inside routes won't work for navigation\nexport const Route = createFileRoute('/some-route')({\n component: () => (\n \u003cThemeProvider theme={theme}>\n \u003cSomeComponent />\n \u003c/ThemeProvider>\n ),\n})\n\n// ✅ Theme provider at root level\nexport const Route = createRootRoute({\n component: () => (\n \u003cThemeProvider theme={theme}>\n \u003cOutlet />\n \u003c/ThemeProvider>\n ),\n})\n```\n\n### Performance Issues with Large Apps\n\n**Problem:** Bundle size or runtime performance issues.\n\n**Solutions:**\n\n1. **Use tree shaking:**\n\n ```tsx\n // ✅ Import only what you need\n import Button from '@mui/material/Button'\n import TextField from '@mui/material/TextField'\n\n // ❌ Avoid importing everything\n import { Button, TextField } from '@mui/material'\n ```\n\n2. **Use dynamic imports for heavy components:**\n\n ```tsx\n import { lazy, Suspense } from 'react'\n import { CircularProgress } from '@mui/material'\n\n const DataGrid = lazy(() =>\n import('@mui/x-data-grid').then((module) => ({\n default: module.DataGrid,\n })),\n )\n\n function MyComponent() {\n return (\n \u003cSuspense fallback={\u003cCircularProgress />}>\n \u003cDataGrid {...props} />\n \u003c/Suspense>\n )\n }\n ```\n\n---\n\n## Production Checklist\n\nBefore deploying your MUI + TanStack Router app:\n\n### Functionality\n\n- [ ] All navigation components work with router state\n- [ ] Active states properly reflected in tabs and navigation\n- [ ] TypeScript compilation successful\n- [ ] All MUI components render correctly\n\n### Performance\n\n- [ ] Bundle size optimized with tree shaking\n- [ ] Emotion CSS-in-JS performance acceptable\n- [ ] No unnecessary re-renders on route changes\n- [ ] Large components code-split appropriately\n\n### Styling\n\n- [ ] Theme consistency across all routes\n- [ ] CSS conflicts resolved\n- [ ] Responsive design working properly\n- [ ] Dark mode integration (if applicable)\n\n### Accessibility\n\n- [ ] Keyboard navigation working\n- [ ] Screen reader compatibility maintained\n- [ ] Focus management across route transitions\n- [ ] ARIA labels and roles properly set\n\n---\n\n## Related Resources\n\n- Material-UI with TypeScript - Official MUI TypeScript guide\n- MUI Theming - Complete theming documentation\n- [TanStack Router createLink API](../api/router#createlink) - API documentation for component integration\n- Emotion CSS-in-JS - Styling library used by MUI\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":17264,"content_sha256":"0086197ae02d3fb01788603d20f809d7ae969a5acc852addb3e89dc2c261ebf1"},{"filename":"references/docs/router/how-to/integrate-shadcn-ui.md","content":"---\ntitle: How to Integrate TanStack Router with Shadcn/ui\n---\n\nThis guide covers setting up Shadcn/ui with TanStack Router, including solutions for common animation and compatibility issues.\n\n## Quick Start\n\n**Time Required:** 30-45 minutes \n**Difficulty:** Intermediate \n**Prerequisites:** Existing TanStack Router project\n\n### What You'll Accomplish\n\n- Install and configure Shadcn/ui with TanStack Router\n- Fix animation issues with modals, sheets, and dialogs\n- Create type-safe navigation components\n- Set up proper styling integration\n- Resolve common compatibility problems\n\n---\n\n## Installation and Setup\n\n### Step 1: Install Shadcn/ui\n\n**Option 1: New project with TanStack Router template**\n\n```bash\nnpx create-tsrouter-app@latest my-app --template file-router --tailwind --add-ons shadcn\n```\n\n**Option 2: Add to existing TanStack Router project**\n\n```bash\nnpx shadcn@latest init\n```\n\n### Step 2: Configure components.json\n\nCreate or update your `components.json` for TanStack Router compatibility:\n\n```json\n{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"default\",\n \"rsc\": false,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"tailwind.config.js\",\n \"css\": \"src/app/globals.css\",\n \"baseColor\": \"slate\",\n \"cssVariables\": true\n },\n \"aliases\": {\n \"components\": \"@/components\",\n \"utils\": \"@/lib/utils\"\n }\n}\n```\n\n### Step 3: Add Essential Components\n\nInstall the most commonly used components:\n\n```bash\nnpx shadcn@latest add button\nnpx shadcn@latest add navigation-menu\nnpx shadcn@latest add sheet\nnpx shadcn@latest add dialog\n```\n\n---\n\n## Fixing Animation Issues\n\n### Step 1: Set Up Proper DOM Structure\n\nUpdate your root route to support portals and animations:\n\n```tsx\n// src/routes/__root.tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003c>\n {/* Main content wrapper */}\n \u003cdiv id=\"root-content\">\n \u003cOutlet />\n \u003c/div>\n\n {/* Portal root for overlays */}\n \u003cdiv id=\"portal-root\">\u003c/div>\n\n \u003cTanStackRouterDevtools />\n \u003c/>\n ),\n})\n```\n\n### Step 2: Create Router-Compatible Sheet Component\n\nShadcn/ui Sheet components can have animation issues. Create a wrapper:\n\n```tsx\n// src/components/ui/router-sheet.tsx\nimport * as React from 'react'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from '@/components/ui/sheet'\n\ninterface RouterSheetProps {\n children: React.ReactNode\n trigger: React.ReactNode\n title: string\n description?: string\n onOpenChange?: (open: boolean) => void\n}\n\nexport function RouterSheet({\n children,\n trigger,\n title,\n description,\n onOpenChange,\n}: RouterSheetProps) {\n const [open, setOpen] = React.useState(false)\n\n const handleOpenChange = (newOpen: boolean) => {\n setOpen(newOpen)\n onOpenChange?.(newOpen)\n }\n\n return (\n \u003cSheet open={open} onOpenChange={handleOpenChange}>\n \u003cSheetTrigger asChild>{trigger}\u003c/SheetTrigger>\n \u003cSheetContent>\n \u003cSheetHeader>\n \u003cSheetTitle>{title}\u003c/SheetTitle>\n {description && \u003cSheetDescription>{description}\u003c/SheetDescription>}\n \u003c/SheetHeader>\n \u003cdiv className=\"mt-4\">{children}\u003c/div>\n \u003c/SheetContent>\n \u003c/Sheet>\n )\n}\n```\n\n### Step 3: Create Router-Compatible Dialog Component\n\n```tsx\n// src/components/ui/router-dialog.tsx\nimport * as React from 'react'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '@/components/ui/dialog'\n\ninterface RouterDialogProps {\n children: React.ReactNode\n trigger: React.ReactNode\n title: string\n description?: string\n open?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\nexport function RouterDialog({\n children,\n trigger,\n title,\n description,\n open: controlledOpen,\n onOpenChange,\n}: RouterDialogProps) {\n const [internalOpen, setInternalOpen] = React.useState(false)\n\n const open = controlledOpen ?? internalOpen\n const setOpen = onOpenChange ?? setInternalOpen\n\n return (\n \u003cDialog open={open} onOpenChange={setOpen}>\n \u003cDialogTrigger asChild>{trigger}\u003c/DialogTrigger>\n \u003cDialogContent>\n \u003cDialogHeader>\n \u003cDialogTitle>{title}\u003c/DialogTitle>\n {description && \u003cDialogDescription>{description}\u003c/DialogDescription>}\n \u003c/DialogHeader>\n \u003cdiv className=\"mt-4\">{children}\u003c/div>\n \u003c/DialogContent>\n \u003c/Dialog>\n )\n}\n```\n\n---\n\n## Creating Navigation Components\n\n### Step 1: Router-Compatible Navigation Menu\n\n```tsx\n// src/components/navigation/main-nav.tsx\nimport { Link, useMatchRoute } from '@tanstack/react-router'\nimport { cn } from '@/lib/utils'\nimport {\n NavigationMenu,\n NavigationMenuItem,\n NavigationMenuLink,\n NavigationMenuList,\n navigationMenuTriggerStyle,\n} from '@/components/ui/navigation-menu'\n\ninterface NavItem {\n to: string\n label: string\n exact?: boolean\n}\n\ninterface MainNavProps {\n items: NavItem[]\n className?: string\n}\n\nexport function MainNav({ items, className }: MainNavProps) {\n const matchRoute = useMatchRoute()\n\n return (\n \u003cNavigationMenu className={className}>\n \u003cNavigationMenuList>\n {items.map((item) => {\n const isActive = matchRoute({ to: item.to, fuzzy: !item.exact })\n\n return (\n \u003cNavigationMenuItem key={item.to}>\n \u003cLink\n to={item.to}\n className={cn(\n navigationMenuTriggerStyle(),\n isActive && 'bg-accent text-accent-foreground font-medium',\n )}\n >\n {item.label}\n \u003c/Link>\n \u003c/NavigationMenuItem>\n )\n })}\n \u003c/NavigationMenuList>\n \u003c/NavigationMenu>\n )\n}\n```\n\n### Step 2: Create Router-Compatible Button Links\n\n```tsx\n// src/components/ui/router-button.tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Button, type ButtonProps } from '@/components/ui/button'\nimport { forwardRef } from 'react'\n\n// Create a router-compatible Button\nexport const RouterButton = createLink(\n forwardRef\u003cHTMLButtonElement, ButtonProps>((props, ref) => {\n return \u003cButton ref={ref} {...props} />\n }),\n)\n```\n\n### Step 3: Usage Example\n\n```tsx\n// src/routes/posts/index.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { MainNav } from '@/components/navigation/main-nav'\nimport { RouterButton } from '@/components/ui/router-button'\nimport { RouterSheet } from '@/components/ui/router-sheet'\nimport { Button } from '@/components/ui/button'\n\nexport const Route = createFileRoute('/posts/')({\n component: PostsPage,\n})\n\nconst navItems = [\n { to: '/', label: 'Home' },\n { to: '/posts', label: 'Posts', exact: true },\n { to: '/about', label: 'About' },\n]\n\nfunction PostsPage() {\n return (\n \u003cdiv className=\"container mx-auto p-4\">\n {/* Navigation with active states */}\n \u003cMainNav items={navItems} className=\"mb-8\" />\n\n \u003cdiv className=\"flex items-center justify-between mb-6\">\n \u003ch1 className=\"text-3xl font-bold\">Posts\u003c/h1>\n\n {/* Router-compatible button */}\n \u003cRouterButton to=\"/posts/new\" variant=\"default\">\n Create Post\n \u003c/RouterButton>\n \u003c/div>\n\n {/* Sheet with proper animations */}\n \u003cRouterSheet\n trigger={\u003cButton variant=\"outline\">Open Menu\u003c/Button>}\n title=\"Navigation Menu\"\n description=\"Navigate through your posts\"\n >\n \u003cdiv className=\"space-y-4\">\n \u003cp>This sheet animates correctly with TanStack Router!\u003c/p>\n \u003cRouterButton to=\"/posts/new\" variant=\"default\" className=\"w-full\">\n Create New Post\n \u003c/RouterButton>\n \u003c/div>\n \u003c/RouterSheet>\n \u003c/div>\n )\n}\n```\n\n---\n\n## Common Problems\n\n### Animation Components Not Working\n\n**Problem:** Sheet, Dialog, or other animated components don't animate properly.\n\n**Solutions:**\n\n1. **Ensure proper portal setup:**\n\n ```tsx\n // Add to your index.html or root component\n \u003cdiv id=\"portal-root\">\u003c/div>\n ```\n\n2. **Check CSS imports order:**\n\n ```css\n /* Make sure this comes before your custom styles */\n @import 'tailwindcss/base';\n @import 'tailwindcss/components';\n @import 'tailwindcss/utilities';\n ```\n\n3. **Use controlled components for complex animations:**\n\n ```tsx\n const [open, setOpen] = useState(false)\n\n // Controlled instead of uncontrolled\n \u003cSheet open={open} onOpenChange={setOpen}>\n ```\n\n### TypeScript Errors with Router Integration\n\n**Problem:** TypeScript errors when using Shadcn/ui components with TanStack Router.\n\n**Solution:** Use `createLink` for proper typing:\n\n```tsx\nimport { createLink } from '@tanstack/react-router'\nimport { Button } from '@/components/ui/button'\n\n// This provides full type safety\nexport const RouterButton = createLink(Button)\n```\n\n### Styling Conflicts\n\n**Problem:** Shadcn/ui styles conflict with router or custom styles.\n\n**Solutions:**\n\n1. **Use CSS layers:**\n\n ```css\n @layer base, components, utilities;\n\n @layer base {\n /* Shadcn/ui base styles */\n }\n\n @layer components {\n /* Your component styles */\n }\n ```\n\n2. **Increase specificity for router-specific styles:**\n ```tsx\n \u003cButton className=\"router-active:bg-primary router-active:text-primary-foreground\">\n Active Button\n \u003c/Button>\n ```\n\n### Dark Mode Integration\n\n**Problem:** Dark mode doesn't work properly with route changes.\n\n**Solution:** Set up theme provider correctly:\n\n```tsx\n// src/components/theme-provider.tsx\nimport { createContext, useContext, useEffect, useState } from 'react'\n\ntype Theme = 'dark' | 'light' | 'system'\n\ninterface ThemeProviderProps {\n children: React.ReactNode\n defaultTheme?: Theme\n storageKey?: string\n}\n\nconst ThemeProviderContext = createContext\u003c{\n theme: Theme\n setTheme: (theme: Theme) => void\n}>({\n theme: 'system',\n setTheme: () => null,\n})\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'ui-theme',\n}: ThemeProviderProps) {\n const [theme, setTheme] = useState\u003cTheme>(\n () => (localStorage.getItem(storageKey) as Theme) || defaultTheme,\n )\n\n useEffect(() => {\n const root = window.document.documentElement\n root.classList.remove('light', 'dark')\n\n if (theme === 'system') {\n const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')\n .matches\n ? 'dark'\n : 'light'\n root.classList.add(systemTheme)\n return\n }\n\n root.classList.add(theme)\n }, [theme])\n\n const value = {\n theme,\n setTheme: (theme: Theme) => {\n localStorage.setItem(storageKey, theme)\n setTheme(theme)\n },\n }\n\n return (\n \u003cThemeProviderContext.Provider value={value}>\n {children}\n \u003c/ThemeProviderContext.Provider>\n )\n}\n\nexport const useTheme = () => {\n const context = useContext(ThemeProviderContext)\n if (context === undefined)\n throw new Error('useTheme must be used within a ThemeProvider')\n return context\n}\n```\n\n---\n\n## Production Checklist\n\nBefore deploying your Shadcn/ui + TanStack Router app:\n\n### Styling\n\n- [ ] All Shadcn/ui components render correctly\n- [ ] Animations work properly on route changes\n- [ ] Dark mode integration working (if applicable)\n- [ ] CSS conflicts resolved\n- [ ] Responsive design tested\n\n### Functionality\n\n- [ ] Navigation components work with router state\n- [ ] Active states properly reflected\n- [ ] TypeScript compilation successful\n- [ ] All sheets, dialogs, and modals animate correctly\n\n### Performance\n\n- [ ] Bundle size optimized (tree shaking working)\n- [ ] CSS-in-JS not causing performance issues\n- [ ] Animation performance acceptable on slower devices\n\n---\n\n## Related Resources\n\n- Shadcn/ui TanStack Router Installation - Official integration guide\n- [TanStack Router createLink API](../api/router#createlink) - API documentation for component integration\n- Shadcn/ui Components - Complete component documentation\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11955,"content_sha256":"0c34295070d3afc5f14b69ca27ee3e00b5a3f30bc35c34b3ddc42f3cfce52fec"},{"filename":"references/docs/router/how-to/navigate-with-search-params.md","content":"---\ntitle: How to Navigate with Search Parameters\n---\n\nThis guide covers updating and managing search parameters during navigation using TanStack Router's Link components and programmatic navigation methods.\n\n**Prerequisites:** [Set Up Basic Search Parameters](./setup-basic-search-params.md) - Foundation concepts for reading and validating search params.\n\n## Quick Start\n\nConfigure navigation that updates search parameters while preserving existing state:\n\n```tsx\nimport { Link, useNavigate } from '@tanstack/react-router'\n\n// Link with search parameter updates\n;\u003cLink to=\"/search\" search={(prev) => ({ ...prev, query: 'new search' })}>\n Search for \"new search\"\n\u003c/Link>\n\n// Programmatic navigation\nconst navigate = useNavigate()\nnavigate({\n to: '/search',\n search: (prev) => ({ ...prev, page: 1 }),\n})\n```\n\n## Navigation Methods\n\n### Using Link Components\n\n#### Basic Search Parameter Updates\n\nReplace all search parameters:\n\n```tsx\nimport { Link } from '@tanstack/router'\n\nfunction SearchForm() {\n return (\n \u003cdiv>\n {/* Replace all search params */}\n \u003cLink to=\"/products\" search={{ category: 'electronics', page: 1 }}>\n Electronics\n \u003c/Link>\n\n {/* Navigate to same route with new search */}\n \u003cLink search={{ sort: 'price-asc' }}>Sort by Price\u003c/Link>\n \u003c/div>\n )\n}\n```\n\n#### Functional Search Parameter Updates\n\nMerge with existing search parameters:\n\n```tsx\nimport { Link } from '@tanstack/react-router'\n\nfunction Pagination() {\n return (\n \u003cdiv>\n {/* Preserve existing search, update page */}\n \u003cLink search={(prev) => ({ ...prev, page: (prev.page || 1) + 1 })}>\n Next Page\n \u003c/Link>\n\n {/* Toggle filter while keeping other params */}\n \u003cLink\n search={(prev) => ({\n ...prev,\n inStock: !prev.inStock,\n })}\n >\n Toggle In Stock\n \u003c/Link>\n\n {/* Remove a search parameter */}\n \u003cLink\n search={(prev) => {\n const { category, ...rest } = prev\n return rest\n }}\n >\n Clear Category Filter\n \u003c/Link>\n \u003c/div>\n )\n}\n```\n\n#### Preserving All Search Parameters\n\nUse `search={true}` to keep all current search parameters when navigating:\n\n```tsx\nimport { Link } from '@tanstack/react-router'\n\nfunction Navigation() {\n return (\n \u003cnav>\n {/* Keep all search params when changing routes */}\n \u003cLink to=\"/products\" search={true}>\n View Products (Keep Filters)\n \u003c/Link>\n\n {/* Equivalent functional approach */}\n \u003cLink to=\"/products\" search={(prev) => prev}>\n View Products (Functional)\n \u003c/Link>\n \u003c/nav>\n )\n}\n```\n\n#### Navigation with Route Changes\n\nNavigate to different routes with search parameters:\n\n```tsx\nimport { Link } from '@tanstack/react-router'\n\nfunction Navigation() {\n return (\n \u003cnav>\n {/* Navigate to different route with search */}\n \u003cLink to=\"/search\" search={{ query: 'laptops', category: 'electronics' }}>\n Search Laptops\n \u003c/Link>\n\n {/* Inherit current search params to new route */}\n \u003cLink\n to=\"/products\"\n search={true} // Shorthand to carry over all search params\n >\n View Products\n \u003c/Link>\n\n {/* Transform search params for new route */}\n \u003cLink\n to=\"/advanced-search\"\n search={(prev) => ({\n q: prev.query, // Rename parameter\n filters: {\n category: prev.category,\n inStock: prev.inStock,\n },\n })}\n >\n Advanced Search\n \u003c/Link>\n \u003c/nav>\n )\n}\n```\n\n### Programmatic Navigation\n\n#### Using useNavigate Hook\n\nNavigate programmatically with search parameter updates:\n\n```tsx\nimport { useNavigate } from '@tanstack/react-router'\n\nfunction SearchControls() {\n const navigate = useNavigate()\n\n const handleSortChange = (sortBy: string) => {\n navigate({\n search: (prev) => ({ ...prev, sort: sortBy, page: 1 }),\n })\n }\n\n const handleClearFilters = () => {\n navigate({\n search: (prev) => {\n const { category, minPrice, maxPrice, ...rest } = prev\n return rest\n },\n })\n }\n\n const handleSearch = (query: string) => {\n navigate({\n to: '/search',\n search: { query, page: 1 },\n })\n }\n\n return (\n \u003cdiv>\n \u003cselect onChange={(e) => handleSortChange(e.target.value)}>\n \u003coption value=\"relevance\">Sort by Relevance\u003c/option>\n \u003coption value=\"price-asc\">Price: Low to High\u003c/option>\n \u003coption value=\"price-desc\">Price: High to Low\u003c/option>\n \u003c/select>\n\n \u003cbutton onClick={handleClearFilters}>Clear Filters\u003c/button>\n\n \u003cbutton onClick={() => handleSearch('latest products')}>\n Search Latest\n \u003c/button>\n \u003c/div>\n )\n}\n```\n\n#### Navigation with Router Instance\n\nUse the router directly only in non-React contexts where `useNavigate` or `Link` aren't available:\n\n```tsx\nimport { router } from './router' // Your router instance\n\n// ✅ Appropriate use case: Utility function outside React components\nexport function navigateFromUtility(searchParams: Record\u003cstring, any>) {\n router.navigate({\n search: (prev) => ({ ...prev, ...searchParams }),\n })\n}\n\n// ✅ Appropriate use case: Event handlers in non-React code\nclass ApiService {\n onAuthError() {\n // Navigate to login when auth fails\n router.navigate({\n to: '/login',\n search: { redirect: window.location.pathname },\n })\n }\n}\n\n// ✅ Appropriate use case: Global error handler\nwindow.addEventListener('unhandledrejection', (event) => {\n if (event.reason.status === 401) {\n router.navigate({\n to: '/login',\n search: { error: 'session-expired' },\n })\n }\n})\n```\n\n** In React components, prefer `useNavigate` instead:**\n\n```tsx\n// ❌ Avoid in React components\nfunction Component() {\n const router = useRouter()\n\n const handleClick = () => {\n router.navigate({ search: { filter: 'active' } })\n }\n\n return \u003cbutton onClick={handleClick}>Filter\u003c/button>\n}\n\n// ✅ Use useNavigate in React components\nfunction Component() {\n const navigate = useNavigate()\n\n const handleClick = () => {\n navigate({ search: { filter: 'active' } })\n }\n\n return \u003cbutton onClick={handleClick}>Filter\u003c/button>\n}\n```\n\n## Advanced Navigation Patterns\n\n### Conditional Navigation\n\nNavigate automatically when certain conditions are met:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useNavigate, useSearch } from '@tanstack/react-router'\n\nfunction ConditionalNavigation() {\n const navigate = useNavigate()\n const search = useSearch({ from: '/products' })\n\n // Auto-reset page when search query changes\n useEffect(() => {\n if (search.query && search.page > 1) {\n navigate({\n search: (prev) => ({ ...prev, page: 1 }),\n })\n }\n }, [search.query, search.page, navigate])\n\n return \u003cdiv>Page resets automatically when search changes\u003c/div>\n}\n```\n\n## Common Patterns\n\n## Common Problems\n\n### Search Parameters Not Updating\n\n**Problem:** Link navigation doesn't update search parameters.\n\n```tsx\n// ❌ Wrong - no search prop\n\u003cLink to=\"/products\">Electronics\u003c/Link>\n\n// ✅ Correct - with search parameters\n\u003cLink to=\"/products\" search={{ category: 'electronics' }}>\n Electronics\n\u003c/Link>\n```\n\n### Losing Existing Search Parameters\n\n**Problem:** New navigation replaces all search parameters instead of updating specific ones.\n\n```tsx\n// ❌ Wrong - replaces all search params\n\u003cLink search={{ page: 2 }}>Next Page\u003c/Link>\n\n// ✅ Correct - preserves existing search params\n\u003cLink search={(prev) => ({ ...prev, page: 2 })}>\n Next Page\n\u003c/Link>\n```\n\n## Common Next Steps\n\nAfter mastering navigation with search parameters, you might want to:\n\n- [Validate Search Parameters with Schemas](./validate-search-params.md) - Add robust validation with Zod, Valibot, or ArkType\n- [Work with Arrays, Objects, and Dates](./arrays-objects-dates-search-params.md) - Handle arrays, objects, dates, and nested data structures\n\n\n\n## Related Resources\n\n- TanStack Router Search Params Guide - Official documentation\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7983,"content_sha256":"dbbe6e9dedeaea30059fdfe150b64a4332de99ab9f47bbea1111aaaa6142225d"},{"filename":"references/docs/router/how-to/README.md","content":"# How-To Guides\n\nThis directory contains focused, step-by-step instructions for common TanStack Router tasks. Each guide is designed to be:\n\n- **Task-focused**: Covers one specific goal from start to finish\n- **Self-contained**: All necessary steps included, no prerequisites assumed\n- **Copy-paste ready**: Working code examples you can use immediately\n- **Problem-solving oriented**: Addresses real-world scenarios and common issues\n\n## Available Guides\n\n- [Install TanStack Router](./install.md) - Basic installation steps\n- [Use Environment Variables](./use-environment-variables.md) - Configure and use environment variables across different bundlers\n- [Deploy to Production](./deploy-to-production.md) - Deploy your app to hosting platforms\n- [Set Up Server-Side Rendering (SSR)](./setup-ssr.md) - Implement SSR with TanStack Router\n- [Migrate from React Router v7](./migrate-from-react-router.md) - Complete migration guide from React Router v7\n\n### Authentication\n\n- [Set Up Basic Authentication](./setup-authentication.md) - Implement basic auth with React Context\n- [Integrate Authentication Providers](./setup-auth-providers.md) - Use Auth0, Clerk, or Supabase\n- [Set Up Role-Based Access Control](./setup-rbac.md) - Add permission-based routing\n\n### Testing & Debugging\n\n- [Set Up Testing with Code-Based Routing](./setup-testing.md) - Comprehensive testing setup for manually defined routes\n- [Test File-Based Routing](./test-file-based-routing.md) - Specific patterns for testing file-based routing applications\n- [Debug Router Issues](./debug-router-issues.md) - Troubleshoot common routing problems and performance issues\n\n### UI Library Integration\n\n- [Integrate with Shadcn/ui](./integrate-shadcn-ui.md) - Set up Shadcn/ui components with animations and styling\n- [Integrate with Material-UI (MUI)](./integrate-material-ui.md) - Configure MUI components with proper theming and TypeScript\n- [Integrate with Framer Motion](./integrate-framer-motion.md) - Add smooth animations and transitions to your routes\n- [Integrate with Chakra UI](./integrate-chakra-ui.md) - Build responsive, accessible UIs with Chakra UI components\n\n### Search Parameters & URL State (Progressive Series)\n\n**Foundation Level (Start Here):**\n\n- [x] [Set Up Basic Search Parameters](./setup-basic-search-params.md) - Create type-safe search validation and reading\n- [x] [Navigate with Search Parameters](./navigate-with-search-params.md) - Update and manage search params with Links and navigation\n\n**Intermediate Level (Common Patterns):**\n\n- [x] [Validate Search Parameters with Schemas](./validate-search-params.md) - Use schema validation libraries for robust validation and type safety\n- [x] [Work with Arrays, Objects, and Dates](./arrays-objects-dates-search-params.md) - Handle arrays, objects, dates, and nested data structures\n- [x] [Share Search Parameters Across Routes](./share-search-params-across-routes.md) - Inherit and manage search params across route hierarchies\n\n**Advanced Level (Power User Patterns):**\n\n- [ ] Build Advanced Search Parameter Middleware - Create custom middleware for search param processing\n- [ ] Optimize Search Parameter Performance - Minimize re-renders and improve performance with selectors\n- [ ] Customize Search Parameter Serialization - Implement custom serialization for compression and compatibility\n\n**Specialized Use Cases:**\n\n- [ ] Build Search-Based Filtering Systems - Create complex filtering UIs with URL state\n- [ ] Handle Search Parameters in Forms - Synchronize form state with URL search parameters\n- [ ] Debug Search Parameter Issues - Troubleshoot common search param problems and performance issues\n- [ ] Use Search Parameters with Data Loading - Integrate search params with loaders and data fetching\n\n## Using These Guides\n\nEach guide follows a consistent structure:\n\n1. **Quick Start** - Brief overview of what you'll accomplish\n2. **Step-by-step instructions** - Platform-specific or scenario-specific guidance\n3. **Production checklist** - Verification steps (where applicable)\n4. **Common problems** - Troubleshooting for typical issues\n5. **Common next steps** - Optional related tasks you might want to tackle\n\n## Contributing\n\nWhen adding new how-to guides:\n\n1. Focus on a single, well-defined task\n2. Use clear headings and numbered steps\n3. Include complete, working code examples\n4. Address the most frequent problems at the end\n5. Comment out \"Common next steps\" links until those guides are created\n6. In \"Related Resources\", link to specific relevant resources, not generic documentation\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4557,"content_sha256":"0b3245cd18ff2bdbea54291ef8baad2f97a1f5c2307ff9a43ea161f9e58059ad"},{"filename":"references/docs/router/how-to/setup-auth-providers.md","content":"---\ntitle: How to Set Up Authentication Providers\n---\n\nThis guide covers integrating popular authentication services (Auth0, Clerk, Supabase) with TanStack Router.\n\n## Quick Start\n\nChoose an authentication provider, install their SDK, wrap your router with their context, and adapt their auth state to work with TanStack Router's context system.\n\n---\n\n## Auth0 Integration\n\n### 1. Install Auth0\n\n```bash\nnpm install @auth0/auth0-react\n```\n\n### 2. Set Up Environment Variables\n\nAdd to your `.env` file:\n\n```env\nVITE_AUTH0_DOMAIN=your-auth0-domain.auth0.com\nVITE_AUTH0_CLIENT_ID=your_auth0_client_id\n```\n\n### 3. Create Auth0 Wrapper\n\nCreate `src/auth/auth0.tsx`:\n\n```tsx\nimport { Auth0Provider, useAuth0 } from '@auth0/auth0-react'\nimport { createContext, useContext } from 'react'\n\ninterface Auth0ContextType {\n isAuthenticated: boolean\n user: any\n login: () => void\n logout: () => void\n isLoading: boolean\n}\n\nconst Auth0Context = createContext\u003cAuth0ContextType | undefined>(undefined)\n\nexport function Auth0Wrapper({ children }: { children: React.ReactNode }) {\n return (\n \u003cAuth0Provider\n domain={import.meta.env.VITE_AUTH0_DOMAIN}\n clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}\n authorizationParams={{\n redirect_uri: window.location.origin,\n }}\n >\n \u003cAuth0ContextProvider>{children}\u003c/Auth0ContextProvider>\n \u003c/Auth0Provider>\n )\n}\n\nfunction Auth0ContextProvider({ children }: { children: React.ReactNode }) {\n const { isAuthenticated, user, loginWithRedirect, logout, isLoading } =\n useAuth0()\n\n const contextValue = {\n isAuthenticated,\n user,\n login: loginWithRedirect,\n logout: () =>\n logout({ logoutParams: { returnTo: window.location.origin } }),\n isLoading,\n }\n\n return (\n \u003cAuth0Context.Provider value={contextValue}>\n {children}\n \u003c/Auth0Context.Provider>\n )\n}\n\nexport function useAuth0Context() {\n const context = useContext(Auth0Context)\n if (context === undefined) {\n throw new Error('useAuth0Context must be used within Auth0Wrapper')\n }\n return context\n}\n```\n\n### 4. Update App Configuration\n\nUpdate `src/App.tsx`:\n\n```tsx\nimport { RouterProvider } from '@tanstack/react-router'\nimport { Auth0Wrapper, useAuth0Context } from './auth/auth0'\nimport { router } from './router'\n\nfunction InnerApp() {\n const auth = useAuth0Context()\n\n if (auth.isLoading) {\n return (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n Loading...\n \u003c/div>\n )\n }\n\n return \u003cRouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n \u003cAuth0Wrapper>\n \u003cInnerApp />\n \u003c/Auth0Wrapper>\n )\n}\n\nexport default App\n```\n\n### 5. Create Protected Routes\n\nCreate `src/routes/_authenticated.tsx`:\n\n```tsx\nimport { createFileRoute, redirect, Outlet } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: ({ context, location }) => {\n if (!context.auth.isAuthenticated) {\n // Auth0 handles login redirects, so just trigger login\n context.auth.login()\n return\n }\n },\n component: () => \u003cOutlet />,\n})\n```\n\n---\n\n## Clerk Integration\n\n### 1. Install Clerk\n\n```bash\nnpm install @clerk/clerk-react\n```\n\n### 2. Set Up Environment Variables\n\nAdd to your `.env` file:\n\n```env\nVITE_CLERK_PUBLISHABLE_KEY=pk_test_your_clerk_key\n```\n\n### 3. Create Clerk Wrapper\n\nCreate `src/auth/clerk.tsx`:\n\n```tsx\nimport { ClerkProvider, useUser, useAuth } from '@clerk/clerk-react'\n\nexport function ClerkWrapper({ children }: { children: React.ReactNode }) {\n return (\n \u003cClerkProvider publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}>\n {children}\n \u003c/ClerkProvider>\n )\n}\n\nexport function useClerkAuth() {\n const { isSignedIn, isLoaded } = useAuth()\n const { user } = useUser()\n\n return {\n isAuthenticated: isSignedIn,\n user: user\n ? {\n id: user.id,\n username:\n user.username || user.primaryEmailAddress?.emailAddress || '',\n email: user.primaryEmailAddress?.emailAddress || '',\n }\n : null,\n isLoading: !isLoaded,\n login: () => {\n // Clerk handles login through components\n window.location.href = '/sign-in'\n },\n logout: () => {\n // Clerk handles logout through components\n window.location.href = '/sign-out'\n },\n }\n}\n```\n\n### 4. Create Clerk Auth Routes\n\nCreate `src/routes/sign-in.tsx`:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { SignIn } from '@clerk/clerk-react'\n\nexport const Route = createFileRoute('/sign-in')({\n component: () => (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n \u003cSignIn redirectUrl=\"/dashboard\" signUpUrl=\"/sign-up\" />\n \u003c/div>\n ),\n})\n```\n\nCreate `src/routes/sign-up.tsx`:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { SignUp } from '@clerk/clerk-react'\n\nexport const Route = createFileRoute('/sign-up')({\n component: () => (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n \u003cSignUp redirectUrl=\"/dashboard\" signInUrl=\"/sign-in\" />\n \u003c/div>\n ),\n})\n```\n\n### 5. Update App Configuration\n\nUpdate `src/App.tsx`:\n\n```tsx\nimport { RouterProvider } from '@tanstack/react-router'\nimport { ClerkWrapper, useClerkAuth } from './auth/clerk'\nimport { router } from './router'\n\nfunction InnerApp() {\n const auth = useClerkAuth()\n\n if (auth.isLoading) {\n return (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n Loading...\n \u003c/div>\n )\n }\n\n return \u003cRouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n \u003cClerkWrapper>\n \u003cInnerApp />\n \u003c/ClerkWrapper>\n )\n}\n\nexport default App\n```\n\n### 6. Create Protected Routes\n\nCreate `src/routes/_authenticated.tsx`:\n\n```tsx\nimport { createFileRoute, redirect, Outlet } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: ({ context, location }) => {\n if (!context.auth.isAuthenticated) {\n throw redirect({\n to: '/sign-in',\n search: {\n redirect: location.href,\n },\n })\n }\n },\n component: () => \u003cOutlet />,\n})\n```\n\n---\n\n## Supabase Integration\n\n### 1. Install Supabase\n\n```bash\nnpm install @supabase/supabase-js\n```\n\n### 2. Set Up Environment Variables\n\nAdd to your `.env` file:\n\n```env\nVITE_SUPABASE_URL=https://your-project.supabase.co\nVITE_SUPABASE_ANON_KEY=your_supabase_anon_key\n```\n\n### 3. Create Supabase Client\n\nCreate `src/auth/supabase.tsx`:\n\n```tsx\nimport { createClient } from '@supabase/supabase-js'\nimport { createContext, useContext, useEffect, useState } from 'react'\n\nconst supabase = createClient(\n import.meta.env.VITE_SUPABASE_URL,\n import.meta.env.VITE_SUPABASE_ANON_KEY,\n)\n\ninterface SupabaseAuthState {\n isAuthenticated: boolean\n user: any\n login: (email: string, password: string) => Promise\u003cvoid>\n logout: () => Promise\u003cvoid>\n isLoading: boolean\n}\n\nconst SupabaseAuthContext = createContext\u003cSupabaseAuthState | undefined>(\n undefined,\n)\n\nexport function SupabaseAuthProvider({\n children,\n}: {\n children: React.ReactNode\n}) {\n const [user, setUser] = useState(null)\n const [isAuthenticated, setIsAuthenticated] = useState(false)\n const [isLoading, setIsLoading] = useState(true)\n\n useEffect(() => {\n // Get initial session\n supabase.auth.getSession().then(({ data: { session } }) => {\n setUser(session?.user ?? null)\n setIsAuthenticated(!!session?.user)\n setIsLoading(false)\n })\n\n // Listen for auth changes\n const {\n data: { subscription },\n } = supabase.auth.onAuthStateChange((_event, session) => {\n setUser(session?.user ?? null)\n setIsAuthenticated(!!session?.user)\n setIsLoading(false)\n })\n\n return () => subscription.unsubscribe()\n }, [])\n\n const login = async (email: string, password: string) => {\n const { error } = await supabase.auth.signInWithPassword({\n email,\n password,\n })\n if (error) throw error\n }\n\n const logout = async () => {\n const { error } = await supabase.auth.signOut()\n if (error) throw error\n }\n\n return (\n \u003cSupabaseAuthContext.Provider\n value={{\n isAuthenticated,\n user,\n login,\n logout,\n isLoading,\n }}\n >\n {children}\n \u003c/SupabaseAuthContext.Provider>\n )\n}\n\nexport function useSupabaseAuth() {\n const context = useContext(SupabaseAuthContext)\n if (context === undefined) {\n throw new Error('useSupabaseAuth must be used within SupabaseAuthProvider')\n }\n return context\n}\n```\n\n### 4. Create Login Component\n\nCreate `src/routes/login.tsx`:\n\n```tsx\nimport { createFileRoute, redirect } from '@tanstack/react-router'\nimport { useState } from 'react'\n\nexport const Route = createFileRoute('/login')({\n validateSearch: (search) => ({\n redirect: (search.redirect as string) || '/dashboard',\n }),\n beforeLoad: ({ context, search }) => {\n if (context.auth.isAuthenticated) {\n throw redirect({ to: search.redirect })\n }\n },\n component: LoginComponent,\n})\n\nfunction LoginComponent() {\n const { auth } = Route.useRouteContext()\n const { redirect } = Route.useSearch()\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState('')\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n setIsLoading(true)\n setError('')\n\n try {\n await auth.login(email, password)\n // Supabase auth will automatically update context\n window.location.href = redirect\n } catch (err: any) {\n setError(err.message || 'Login failed')\n } finally {\n setIsLoading(false)\n }\n }\n\n return (\n \u003cdiv className=\"min-h-screen flex items-center justify-center\">\n \u003cform\n onSubmit={handleSubmit}\n className=\"max-w-md w-full space-y-4 p-6 border rounded-lg\"\n >\n \u003ch1 className=\"text-2xl font-bold text-center\">Sign In\u003c/h1>\n\n {error && (\n \u003cdiv className=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded\">\n {error}\n \u003c/div>\n )}\n\n \u003cdiv>\n \u003clabel htmlFor=\"email\" className=\"block text-sm font-medium mb-1\">\n Email\n \u003c/label>\n \u003cinput\n id=\"email\"\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\"\n required\n />\n \u003c/div>\n\n \u003cdiv>\n \u003clabel htmlFor=\"password\" className=\"block text-sm font-medium mb-1\">\n Password\n \u003c/label>\n \u003cinput\n id=\"password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\"\n required\n />\n \u003c/div>\n\n \u003cbutton\n type=\"submit\"\n disabled={isLoading}\n className=\"w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50\"\n >\n {isLoading ? 'Signing in...' : 'Sign In'}\n \u003c/button>\n \u003c/form>\n \u003c/div>\n )\n}\n```\n\n### 5. Update App Configuration\n\nUpdate `src/App.tsx`:\n\n```tsx\nimport { RouterProvider } from '@tanstack/react-router'\nimport { SupabaseAuthProvider, useSupabaseAuth } from './auth/supabase'\nimport { router } from './router'\n\nfunction InnerApp() {\n const auth = useSupabaseAuth()\n\n if (auth.isLoading) {\n return (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n Loading...\n \u003c/div>\n )\n }\n\n return \u003cRouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n \u003cSupabaseAuthProvider>\n \u003cInnerApp />\n \u003c/SupabaseAuthProvider>\n )\n}\n\nexport default App\n```\n\n---\n\n## Provider Comparison\n\n| Feature | Auth0 | Clerk | Supabase |\n| ----------------------- | -------- | --------- | -------- |\n| **Setup Complexity** | Medium | Low | Medium |\n| **UI Components** | Basic | Excellent | None |\n| **Customization** | High | Medium | High |\n| **Pricing** | Freemium | Freemium | Freemium |\n| **Social Login** | | | |\n| **Enterprise Features** | | | |\n| **Database Included** | | | |\n\n### When to Choose Each:\n\n- **Auth0**: Complex enterprise requirements, extensive customization\n- **Clerk**: Quick setup with beautiful UI components\n- **Supabase**: Full-stack solution with database and real-time features\n\n---\n\n## Common Problems\n\n### Provider Context Not Available\n\n**Problem:** Auth context is undefined in components.\n\n**Solution:** Ensure the provider wrapper is above `RouterProvider`:\n\n```tsx\n// ✅ Correct order\n\u003cAuthProvider>\n \u003cInnerApp>\n \u003cRouterProvider />\n \u003c/InnerApp>\n\u003c/AuthProvider>\n\n// ❌ Wrong order\n\u003cRouterProvider>\n \u003cAuthProvider />\n\u003c/RouterProvider>\n```\n\n### Infinite Loading States\n\n**Problem:** App stuck on loading screen.\n\n**Solution:** Check if auth provider properly sets `isLoading` to `false`:\n\n```tsx\n// Add timeout fallback\nuseEffect(() => {\n const timeout = setTimeout(() => {\n if (isLoading) {\n setIsLoading(false)\n }\n }, 5000)\n return () => clearTimeout(timeout)\n}, [isLoading])\n```\n\n### Redirect Loops with Auth0\n\n**Problem:** Continuous redirects between login and protected routes.\n\n**Solution:** Handle Auth0's automatic redirects properly:\n\n```tsx\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: ({ context }) => {\n if (!context.auth.isAuthenticated && !context.auth.isLoading) {\n context.auth.login()\n // Don't throw redirect, let Auth0 handle it\n return\n }\n },\n component: () => \u003cOutlet />,\n})\n```\n\n---\n\n## Common Next Steps\n\nAfter integrating authentication providers, you might want to:\n\n- [How to Set Up Role-Based Access Control](./setup-rbac.md) - Add permission-based routing\n- [How to Set Up Basic Authentication](./setup-authentication.md) - Custom auth implementation\n\n\n\n## Related Resources\n\n- Auth0 React SDK - Official Auth0 documentation\n- Clerk React SDK - Official Clerk documentation\n- Supabase Auth - Official Supabase auth guide\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14449,"content_sha256":"1084df7335171e6fb90bbd898c8564c47403d9b2b7bee0499d3bdcf5495f03e9"},{"filename":"references/docs/router/how-to/setup-authentication.md","content":"---\ntitle: How to Set Up Basic Authentication and Protected Routes\n---\n\nThis guide covers implementing basic authentication patterns and protecting routes in TanStack Router applications.\n\n## Quick Start\n\nSet up authentication by creating a context-aware router, implementing auth state management, and using `beforeLoad` for route protection. This guide focuses on the core authentication setup using React Context.\n\n---\n\n## Create Authentication Context\n\nCreate `src/auth.tsx`:\n\n```tsx\nimport React, { createContext, useContext, useState, useEffect } from 'react'\n\ninterface User {\n id: string\n username: string\n email: string\n}\n\ninterface AuthState {\n isAuthenticated: boolean\n user: User | null\n login: (username: string, password: string) => Promise\u003cvoid>\n logout: () => void\n}\n\nconst AuthContext = createContext\u003cAuthState | undefined>(undefined)\n\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n const [user, setUser] = useState\u003cUser | null>(null)\n const [isAuthenticated, setIsAuthenticated] = useState(false)\n const [isLoading, setIsLoading] = useState(true)\n\n // Restore auth state on app load\n useEffect(() => {\n const token = localStorage.getItem('auth-token')\n if (token) {\n // Validate token with your API\n fetch('/api/validate-token', {\n headers: { Authorization: `Bearer ${token}` },\n })\n .then((response) => response.json())\n .then((userData) => {\n if (userData.valid) {\n setUser(userData.user)\n setIsAuthenticated(true)\n } else {\n localStorage.removeItem('auth-token')\n }\n })\n .catch(() => {\n localStorage.removeItem('auth-token')\n })\n .finally(() => {\n setIsLoading(false)\n })\n } else {\n setIsLoading(false)\n }\n }, [])\n\n // Show loading state while checking auth\n if (isLoading) {\n return (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n Loading...\n \u003c/div>\n )\n }\n\n const login = async (username: string, password: string) => {\n // Replace with your authentication logic\n const response = await fetch('/api/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ username, password }),\n })\n\n if (response.ok) {\n const userData = await response.json()\n setUser(userData)\n setIsAuthenticated(true)\n // Store token for persistence\n localStorage.setItem('auth-token', userData.token)\n } else {\n throw new Error('Authentication failed')\n }\n }\n\n const logout = () => {\n setUser(null)\n setIsAuthenticated(false)\n localStorage.removeItem('auth-token')\n }\n\n return (\n \u003cAuthContext.Provider value={{ isAuthenticated, user, login, logout }}>\n {children}\n \u003c/AuthContext.Provider>\n )\n}\n\nexport function useAuth() {\n const context = useContext(AuthContext)\n if (context === undefined) {\n throw new Error('useAuth must be used within an AuthProvider')\n }\n return context\n}\n```\n\n---\n\n## Configure Router Context\n\n### 1. Set Up Router Context\n\nUpdate `src/routes/__root.tsx`:\n\n```tsx\nimport { createRootRouteWithContext, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\ninterface AuthState {\n isAuthenticated: boolean\n user: { id: string; username: string; email: string } | null\n login: (username: string, password: string) => Promise\u003cvoid>\n logout: () => void\n}\n\ninterface MyRouterContext {\n auth: AuthState\n}\n\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()({\n component: () => (\n \u003cdiv>\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/div>\n ),\n})\n```\n\n### 2. Configure Router\n\nUpdate `src/router.tsx`:\n\n```tsx\nimport { createRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n\nexport const router = createRouter({\n routeTree,\n context: {\n // auth will be passed down from App component\n auth: undefined!,\n },\n})\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n```\n\n### 3. Connect App with Authentication\n\nUpdate `src/App.tsx`:\n\n```tsx\nimport { RouterProvider } from '@tanstack/react-router'\nimport { AuthProvider, useAuth } from './auth'\nimport { router } from './router'\n\nfunction InnerApp() {\n const auth = useAuth()\n return \u003cRouterProvider router={router} context={{ auth }} />\n}\n\nfunction App() {\n return (\n \u003cAuthProvider>\n \u003cInnerApp />\n \u003c/AuthProvider>\n )\n}\n\nexport default App\n```\n\n---\n\n## Create Protected Routes\n\n### 1. Create Authentication Layout Route\n\nCreate `src/routes/_authenticated.tsx`:\n\n```tsx\nimport { createFileRoute, redirect, Outlet } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: ({ context, location }) => {\n if (!context.auth.isAuthenticated) {\n throw redirect({\n to: '/login',\n search: {\n // Save current location for redirect after login\n redirect: location.href,\n },\n })\n }\n },\n component: () => \u003cOutlet />,\n})\n```\n\n### 2. Create Login Route\n\nCreate `src/routes/login.tsx`:\n\n```tsx\nimport { createFileRoute, redirect } from '@tanstack/react-router'\nimport { useState } from 'react'\n\nexport const Route = createFileRoute('/login')({\n validateSearch: (search) => ({\n redirect: (search.redirect as string) || '/',\n }),\n beforeLoad: ({ context, search }) => {\n // Redirect if already authenticated\n if (context.auth.isAuthenticated) {\n throw redirect({ to: search.redirect })\n }\n },\n component: LoginComponent,\n})\n\nfunction LoginComponent() {\n const { auth } = Route.useRouteContext()\n const { redirect } = Route.useSearch()\n const navigate = Route.useNavigate()\n const [username, setUsername] = useState('')\n const [password, setPassword] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState('')\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n setIsLoading(true)\n setError('')\n\n try {\n await auth.login(username, password)\n // Navigate to the redirect URL using router navigation\n navigate({ to: redirect })\n } catch (err) {\n setError('Invalid username or password')\n } finally {\n setIsLoading(false)\n }\n }\n\n return (\n \u003cdiv className=\"min-h-screen flex items-center justify-center\">\n \u003cform\n onSubmit={handleSubmit}\n className=\"max-w-md w-full space-y-4 p-6 border rounded-lg\"\n >\n \u003ch1 className=\"text-2xl font-bold text-center\">Sign In\u003c/h1>\n\n {error && (\n \u003cdiv className=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded\">\n {error}\n \u003c/div>\n )}\n\n \u003cdiv>\n \u003clabel htmlFor=\"username\" className=\"block text-sm font-medium mb-1\">\n Username\n \u003c/label>\n \u003cinput\n id=\"username\"\n type=\"text\"\n value={username}\n onChange={(e) => setUsername(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\"\n required\n />\n \u003c/div>\n\n \u003cdiv>\n \u003clabel htmlFor=\"password\" className=\"block text-sm font-medium mb-1\">\n Password\n \u003c/label>\n \u003cinput\n id=\"password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\"\n required\n />\n \u003c/div>\n\n \u003cbutton\n type=\"submit\"\n disabled={isLoading}\n className=\"w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isLoading ? 'Signing in...' : 'Sign In'}\n \u003c/button>\n \u003c/form>\n \u003c/div>\n )\n}\n```\n\n### 3. Create Protected Dashboard\n\nCreate `src/routes/_authenticated/dashboard.tsx`:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/dashboard')({\n component: DashboardComponent,\n})\n\nfunction DashboardComponent() {\n const { auth } = Route.useRouteContext()\n\n return (\n \u003cdiv className=\"p-6\">\n \u003cdiv className=\"flex justify-between items-center mb-6\">\n \u003ch1 className=\"text-3xl font-bold\">Dashboard\u003c/h1>\n \u003cbutton\n onClick={auth.logout}\n className=\"bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700\"\n >\n Sign Out\n \u003c/button>\n \u003c/div>\n\n \u003cdiv className=\"bg-white p-6 rounded-lg shadow\">\n \u003ch2 className=\"text-xl font-semibold mb-2\">Welcome back!\u003c/h2>\n \u003cp className=\"text-gray-600\">\n Hello, \u003cstrong>{auth.user?.username}\u003c/strong>! You are successfully\n authenticated.\n \u003c/p>\n \u003cp className=\"text-sm text-gray-500 mt-2\">Email: {auth.user?.email}\u003c/p>\n \u003c/div>\n \u003c/div>\n )\n}\n```\n\n---\n\n## Add Authentication Persistence\n\nUpdate your `AuthProvider` to restore authentication state on page refresh:\n\n```tsx\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n const [user, setUser] = useState\u003cUser | null>(null)\n const [isAuthenticated, setIsAuthenticated] = useState(false)\n const [isLoading, setIsLoading] = useState(true)\n\n // Restore auth state on app load\n useEffect(() => {\n const token = localStorage.getItem('auth-token')\n if (token) {\n // Validate token with your API\n fetch('/api/validate-token', {\n headers: { Authorization: `Bearer ${token}` },\n })\n .then((response) => response.json())\n .then((userData) => {\n if (userData.valid) {\n setUser(userData.user)\n setIsAuthenticated(true)\n } else {\n localStorage.removeItem('auth-token')\n }\n })\n .catch(() => {\n localStorage.removeItem('auth-token')\n })\n .finally(() => {\n setIsLoading(false)\n })\n } else {\n setIsLoading(false)\n }\n }, [])\n\n // Show loading state while checking auth\n if (isLoading) {\n return (\n \u003cdiv className=\"flex items-center justify-center min-h-screen\">\n Loading...\n \u003c/div>\n )\n }\n\n // ... rest of the provider logic\n}\n```\n\n---\n\n## Production Checklist\n\nBefore deploying authentication, ensure you have:\n\n- [ ] Secured API endpoints with proper authentication middleware\n- [ ] Set up HTTPS in production (required for secure cookies)\n- [ ] Configured environment variables for API endpoints\n- [ ] Implemented proper token validation and refresh\n- [ ] Added CSRF protection for form-based authentication\n- [ ] Tested authentication flows (login, logout, persistence)\n- [ ] Added proper error handling for network failures\n- [ ] Implemented loading states for auth operations\n\n---\n\n## Common Problems\n\n### Authentication Context Not Available\n\n**Problem:** `useAuth must be used within an AuthProvider` error.\n\n**Solution:** Ensure `AuthProvider` wraps your entire app and `RouterProvider` is inside it.\n\n### User Logged Out on Page Refresh\n\n**Problem:** Authentication state resets when page refreshes.\n\n**Solution:** Add token persistence as shown in the persistence section above.\n\n### Protected Route Flashing Before Redirect\n\n**Problem:** Protected content briefly shows before redirecting to login.\n\n**Solution:** Use `beforeLoad` instead of component-level auth checks:\n\n```tsx\nexport const Route = createFileRoute('/_authenticated/dashboard')({\n beforeLoad: ({ context }) => {\n if (!context.auth.isAuthenticated) {\n throw redirect({ to: '/login' })\n }\n },\n component: DashboardComponent,\n})\n```\n\n---\n\n## Common Next Steps\n\nAfter setting up basic authentication, you might want to:\n\n- [How to Integrate Authentication Providers](./setup-auth-providers.md) - Use Auth0, Clerk, or Supabase\n- [How to Set Up Role-Based Access Control](./setup-rbac.md) - Add permission-based routing\n\n\n\n## Related Resources\n\n- [Authenticated Routes Guide](../guide/authenticated-routes.md) - Detailed conceptual guide\n- [Router Context Guide](../guide/router-context.md) - Understanding context in TanStack Router\n- Authentication Examples - Complete working examples\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12449,"content_sha256":"9ed0593fbff2e0ab84472a7af7f8fd51e2f72ca21713ab88a8beeaf2ba5da7c4"},{"filename":"references/docs/router/how-to/setup-basic-search-params.md","content":"---\ntitle: How to Set Up Basic Search Parameters\n---\n\nLearn how to add type-safe, production-ready search parameters to your TanStack Router routes using schema validation. This guide covers the fundamentals of search parameter validation, reading values, and handling different data types with any standard schema-compliant validation library.\n\n## Quick Start\n\nSet up search parameters with schema validation (recommended for production):\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { zodValidator, fallback } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n page: fallback(z.number(), 1).default(1),\n category: fallback(z.string(), 'all').default('all'),\n showSale: fallback(z.boolean(), false).default(false),\n})\n\nexport const Route = createFileRoute('/products')({\n validateSearch: zodValidator(productSearchSchema),\n component: ProductsPage,\n})\n\nfunction ProductsPage() {\n const { page, category, showSale } = Route.useSearch()\n\n return (\n \u003cdiv>\n \u003ch1>Products\u003c/h1>\n \u003cp>Page: {page}\u003c/p>\n \u003cp>Category: {category}\u003c/p>\n \u003cp>Show Sale Items: {showSale ? 'Yes' : 'No'}\u003c/p>\n \u003c/div>\n )\n}\n```\n\n## Why Use Schema Validation for Search Parameters?\n\n**Production Benefits:**\n\n- **Type Safety**: Automatic TypeScript inference\n- **Runtime Validation**: Catches invalid URL parameters gracefully\n- **Default Values**: Fallback handling for missing parameters\n- **Error Handling**: Built-in validation error management\n- **Maintainability**: Clear, declarative schema definitions\n\n## Validation Library Setup\n\nTanStack Router supports any standard schema-compliant validation library. This guide focuses on Zod for examples, but you can use any validation library:\n\n```bash\nnpm install zod @tanstack/zod-adapter\n```\n\n```tsx\nimport { zodValidator, fallback } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst searchSchema = z.object({\n page: fallback(z.number(), 1).default(1),\n category: fallback(z.string(), 'all').default('all'),\n})\n\nexport const Route = createFileRoute('/products')({\n validateSearch: zodValidator(searchSchema),\n component: ProductsPage,\n})\n```\n\n**For detailed validation library comparisons and advanced validation patterns, see:** [Validate Search Parameters with Schemas](./validate-search-params.md)\n\n## Step-by-Step Setup with Zod\n\nThe rest of this guide uses Zod for examples, but the patterns apply to any validation library.\n\n### Step 1: Install Dependencies\n\n```bash\nnpm install zod @tanstack/zod-adapter\n```\n\n### Step 2: Define Your Search Schema\n\nStart by identifying what search parameters your route needs:\n\n```tsx\nimport { z } from 'zod'\nimport { fallback } from '@tanstack/zod-adapter'\n\nconst shopSearchSchema = z.object({\n // Pagination\n page: fallback(z.number(), 1).default(1),\n limit: fallback(z.number(), 20).default(20),\n\n // Filtering\n category: fallback(z.string(), 'all').default('all'),\n minPrice: fallback(z.number(), 0).default(0),\n maxPrice: fallback(z.number(), 1000).default(1000),\n\n // Settings\n sort: fallback(z.enum(['name', 'price', 'date']), 'name').default('name'),\n ascending: fallback(z.boolean(), true).default(true),\n\n // Optional parameters\n searchTerm: z.string().optional(),\n showOnlyInStock: fallback(z.boolean(), false).default(false),\n})\n\ntype ShopSearch = z.infer\u003ctypeof shopSearchSchema>\n```\n\n### Step 3: Add Schema Validation to Route\n\nUse the validation adapter to connect your schema to the route:\n\n```tsx\nimport { zodValidator } from '@tanstack/zod-adapter'\n\nexport const Route = createFileRoute('/shop')({\n validateSearch: zodValidator(shopSearchSchema),\n component: ShopPage,\n})\n```\n\n### Step 4: Read Search Parameters in Components\n\nUse the route's `useSearch()` hook to access validated and typed search parameters:\n\n```tsx\nfunction ShopPage() {\n const searchParams = Route.useSearch()\n\n // All properties are fully type-safe and validated\n const {\n page,\n limit,\n category,\n sort,\n ascending,\n searchTerm,\n showOnlyInStock,\n } = searchParams\n\n return (\n \u003cdiv>\n \u003ch1>Shop - Page {page}\u003c/h1>\n \u003cdiv>Category: {category}\u003c/div>\n \u003cdiv>\n Sort: {sort} ({ascending ? 'ascending' : 'descending'})\n \u003c/div>\n \u003cdiv>Items per page: {limit}\u003c/div>\n \u003cdiv>In stock only: {showOnlyInStock ? 'Yes' : 'No'}\u003c/div>\n {searchTerm && \u003cdiv>Search: \"{searchTerm}\"\u003c/div>}\n \u003c/div>\n )\n}\n```\n\n## Common Search Parameter Patterns\n\n### Pagination with Constraints\n\n```tsx\nconst paginationSchema = z.object({\n page: fallback(z.number().min(1), 1).default(1),\n limit: fallback(z.number().min(10).max(100), 20).default(20),\n})\n\nexport const Route = createFileRoute('/posts')({\n validateSearch: zodValidator(paginationSchema),\n component: PostsPage,\n})\n\nfunction PostsPage() {\n const { page, limit } = Route.useSearch()\n\n // Calculate offset for API calls\n const offset = (page - 1) * limit\n\n return (\n \u003cdiv>\n \u003ch1>Posts (Page {page})\u003c/h1>\n \u003cp>Showing {limit} posts per page\u003c/p>\n \u003cp>Offset: {offset}\u003c/p>\n {/* Render posts... */}\n \u003c/div>\n )\n}\n```\n\n### Enum Validation with Defaults\n\n```tsx\nconst catalogSchema = z.object({\n sort: fallback(z.enum(['name', 'date', 'price']), 'name').default('name'),\n category: fallback(\n z.enum(['electronics', 'clothing', 'books', 'all']),\n 'all',\n ).default('all'),\n ascending: fallback(z.boolean(), true).default(true),\n})\n\nexport const Route = createFileRoute('/catalog')({\n validateSearch: zodValidator(catalogSchema),\n component: CatalogPage,\n})\n```\n\n### Complex Data Types\n\n```tsx\nconst dashboardSchema = z.object({\n // Numbers with validation\n userId: fallback(z.number().positive(), 1).default(1),\n refreshInterval: fallback(z.number().min(1000).max(60000), 5000).default(\n 5000,\n ),\n\n // Strings with validation\n theme: fallback(z.enum(['light', 'dark']), 'light').default('light'),\n timezone: z.string().optional(),\n\n // Arrays with validation\n selectedIds: fallback(z.number().array(), []).default([]),\n tags: fallback(z.string().array(), []).default([]),\n\n // Objects with validation\n filters: fallback(\n z.object({\n status: z.enum(['active', 'inactive']).optional(),\n type: z.string().optional(),\n }),\n {},\n ).default({}),\n})\n```\n\n### Date and Advanced Types\n\n```tsx\nconst reportSchema = z.object({\n startDate: z.string().pipe(z.coerce.date()).optional(),\n endDate: z.string().pipe(z.coerce.date()).optional(),\n format: fallback(z.enum(['pdf', 'csv', 'excel']), 'pdf').default('pdf'),\n includeCharts: fallback(z.boolean(), true).default(true),\n})\n```\n\n## Reading Search Parameters Outside Components\n\n### Using getRouteApi\n\nFor code-split components or separate files:\n\n```tsx\n// components/ProductFilters.tsx\nimport { getRouteApi } from '@tanstack/react-router'\n\nconst routeApi = getRouteApi('/products')\n\nexport function ProductFilters() {\n const { category, sort, showSale } = routeApi.useSearch()\n\n return (\n \u003cdiv>\n \u003cselect value={category}>\n \u003coption value=\"all\">All Categories\u003c/option>\n \u003coption value=\"electronics\">Electronics\u003c/option>\n \u003coption value=\"clothing\">Clothing\u003c/option>\n \u003c/select>\n {/* More filters... */}\n \u003c/div>\n )\n}\n```\n\n### Using useSearch with from\n\n```tsx\nimport { useSearch } from '@tanstack/react-router'\n\nfunction GenericSearchDisplay() {\n const search = useSearch({ from: '/products' })\n\n return \u003cdiv>Current filters: {JSON.stringify(search, null, 2)}\u003c/div>\n}\n```\n\n## Manual Validation (Understanding the Primitives)\n\nWhile schema validation is recommended for production, understanding manual validation helps you understand how search parameters work under the hood:\n\n```tsx\n// Educational example - use schema validation for production\nexport const Route = createFileRoute('/example')({\n validateSearch: (search: Record\u003cstring, unknown>) => ({\n // Numbers need coercion from URL strings\n page: Number(search.page) || 1,\n\n // Strings can be cast with defaults\n category: (search.category as string) || 'all',\n\n // Booleans: TanStack Router auto-converts \"true\"/\"false\" to booleans\n showSale: Boolean(search.showSale),\n\n // Arrays need JSON parsing validation\n selectedIds: Array.isArray(search.selectedIds)\n ? search.selectedIds.map(Number).filter(Boolean)\n : [],\n }),\n component: ExamplePage,\n})\n```\n\n## Production Checklist\n\n- [x] **Use schema validation** with a validation library for type safety and runtime validation\n- [x] **Add fallback values** for graceful error handling\n- [x] **Set default values** for optional parameters\n- [x] **Validate constraints** using your validation library's built-in validators\n- [x] **Handle optional parameters** appropriately\n- [x] **Type inference** works automatically with proper schema setup\n- [x] **Error boundaries** are configured to handle validation failures\n\n## Common Problems\n\n### Problem: Search Parameters Cause TypeScript Errors\n\n**Cause:** Missing or incorrect schema definition.\n\n**Solution:** Ensure your schema covers all search parameters and use proper types:\n\n```tsx\n// ❌ Missing schema or incorrect types\nexport const Route = createFileRoute('/page')({\n component: MyPage,\n})\n\n// ✅ Complete schema with proper validation\nconst searchSchema = z.object({\n page: fallback(z.number(), 1).default(1),\n category: fallback(z.string(), 'all').default('all'),\n})\n\nexport const Route = createFileRoute('/page')({\n validateSearch: zodValidator(searchSchema),\n component: MyPage,\n})\n```\n\n### Problem: Invalid URL Parameters Break the App\n\n**Cause:** Not using fallback handling for error cases.\n\n**Solution:** Use fallback values to provide safe defaults:\n\n```tsx\n// ❌ No fallback handling\nconst schema = z.object({\n page: z.number().default(1), // Will throw on invalid input\n})\n\n// ✅ Graceful fallback handling\nconst schema = z.object({\n page: fallback(z.number(), 1).default(1), // Safe fallback to 1\n})\n```\n\n### Problem: Optional Parameters Are Required by TypeScript\n\n**Cause:** Using `.default()` makes parameters required in navigation.\n\n**Solution:** Use `.optional()` for truly optional parameters:\n\n```tsx\nconst schema = z.object({\n // Required with default (navigation can omit, but always present in component)\n page: fallback(z.number(), 1).default(1),\n\n // Truly optional (can be undefined in component)\n searchTerm: z.string().optional(),\n})\n```\n\n### Problem: Complex Objects Not Validating\n\n**Cause:** Nested objects need explicit schema definition.\n\n**Solution:** Define complete nested schemas:\n\n```tsx\nconst schema = z.object({\n filters: fallback(\n z.object({\n status: z.enum(['active', 'inactive']).optional(),\n tags: z.string().array().optional(),\n dateRange: z\n .object({\n start: z.string().pipe(z.coerce.date()),\n end: z.string().pipe(z.coerce.date()),\n })\n .optional(),\n }),\n {},\n ).default({}),\n})\n```\n\n## Common Next Steps\n\nAfter setting up basic search parameters, you might want to:\n\n- [Validate Search Parameters with Schemas](./validate-search-params.md) - Add robust validation with Zod, Valibot, or ArkType\n- [Navigate with Search Parameters](./navigate-with-search-params.md) - Learn to update search params with Links and navigation\n- [Work with Arrays, Objects, and Dates](./arrays-objects-dates-search-params.md) - Handle arrays, objects, dates, and nested data structures\n\n## Related Resources\n\n- **Validation Libraries:**\n - Zod Documentation - Complete validation library reference\n - Valibot Documentation - Lightweight validation library\n - Yup Documentation - Object schema validation\n- **TanStack Router:**\n - TanStack Zod Adapter - Official Zod adapter\n - TanStack Valibot Adapter - Official Valibot adapter\n - [Search Parameters Guide](../guide/search-params.md) - Comprehensive search parameters documentation\n - [Type Safety Guide](../guide/type-safety.md) - Understanding TanStack Router's type safety\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11975,"content_sha256":"fea1f60ed38069db5b1305e93c74dca2620901481e088d6af5ed01e70b559b13"},{"filename":"references/docs/router/how-to/setup-rbac.md","content":"---\ntitle: How to Set Up Role-Based Access Control\n---\n\nThis guide covers implementing role-based access control (RBAC) and permission-based routing in TanStack Router applications.\n\n## Quick Start\n\nExtend your authentication context to include roles and permissions, create role-protected layout routes, and use `beforeLoad` to check user permissions before rendering routes.\n\n---\n\n## Extend Authentication Context\n\n### 1. Add Roles to User Type\n\nUpdate your authentication context to include roles:\n\n```tsx\n// src/auth.tsx\nimport React, { createContext, useContext, useState } from 'react'\n\ninterface User {\n id: string\n username: string\n email: string\n roles: string[]\n permissions: string[]\n}\n\ninterface AuthState {\n isAuthenticated: boolean\n user: User | null\n hasRole: (role: string) => boolean\n hasAnyRole: (roles: string[]) => boolean\n hasPermission: (permission: string) => boolean\n hasAnyPermission: (permissions: string[]) => boolean\n login: (username: string, password: string) => Promise\u003cvoid>\n logout: () => void\n}\n\nconst AuthContext = createContext\u003cAuthState | undefined>(undefined)\n\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n const [user, setUser] = useState\u003cUser | null>(null)\n const [isAuthenticated, setIsAuthenticated] = useState(false)\n\n const hasRole = (role: string) => {\n return user?.roles.includes(role) ?? false\n }\n\n const hasAnyRole = (roles: string[]) => {\n return roles.some((role) => user?.roles.includes(role)) ?? false\n }\n\n const hasPermission = (permission: string) => {\n return user?.permissions.includes(permission) ?? false\n }\n\n const hasAnyPermission = (permissions: string[]) => {\n return (\n permissions.some((permission) =>\n user?.permissions.includes(permission),\n ) ?? false\n )\n }\n\n const login = async (username: string, password: string) => {\n const response = await fetch('/api/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ username, password }),\n })\n\n if (response.ok) {\n const userData = await response.json()\n setUser(userData)\n setIsAuthenticated(true)\n } else {\n throw new Error('Authentication failed')\n }\n }\n\n const logout = () => {\n setUser(null)\n setIsAuthenticated(false)\n }\n\n return (\n \u003cAuthContext.Provider\n value={{\n isAuthenticated,\n user,\n hasRole,\n hasAnyRole,\n hasPermission,\n hasAnyPermission,\n login,\n logout,\n }}\n >\n {children}\n \u003c/AuthContext.Provider>\n )\n}\n\nexport function useAuth() {\n const context = useContext(AuthContext)\n if (context === undefined) {\n throw new Error('useAuth must be used within an AuthProvider')\n }\n return context\n}\n```\n\n### 2. Update Router Context Types\n\nUpdate `src/routes/__root.tsx`:\n\n```tsx\nimport { createRootRouteWithContext, Outlet } from '@tanstack/react-router'\n\ninterface AuthState {\n isAuthenticated: boolean\n user: {\n id: string\n username: string\n email: string\n roles: string[]\n permissions: string[]\n } | null\n hasRole: (role: string) => boolean\n hasAnyRole: (roles: string[]) => boolean\n hasPermission: (permission: string) => boolean\n hasAnyPermission: (permissions: string[]) => boolean\n login: (username: string, password: string) => Promise\u003cvoid>\n logout: () => void\n}\n\ninterface MyRouterContext {\n auth: AuthState\n}\n\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()({\n component: () => (\n \u003cdiv>\n \u003cOutlet />\n \u003c/div>\n ),\n})\n```\n\n---\n\n## Create Role-Protected Routes\n\n### 1. Admin-Only Routes\n\nCreate `src/routes/_authenticated/_admin.tsx`:\n\n```tsx\nimport { createFileRoute, redirect, Outlet } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/_admin')({\n beforeLoad: ({ context, location }) => {\n if (!context.auth.hasRole('admin')) {\n throw redirect({\n to: '/unauthorized',\n search: {\n redirect: location.href,\n },\n })\n }\n },\n component: AdminLayout,\n})\n\nfunction AdminLayout() {\n return (\n \u003cdiv>\n \u003cdiv className=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4\">\n \u003cstrong>Admin Area:\u003c/strong> You have administrative privileges.\n \u003c/div>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n### 2. Multiple Role Access\n\nCreate `src/routes/_authenticated/_moderator.tsx`:\n\n```tsx\nimport { createFileRoute, redirect, Outlet } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/_moderator')({\n beforeLoad: ({ context, location }) => {\n const allowedRoles = ['admin', 'moderator']\n if (!context.auth.hasAnyRole(allowedRoles)) {\n throw redirect({\n to: '/unauthorized',\n search: {\n redirect: location.href,\n reason: 'insufficient_role',\n },\n })\n }\n },\n component: ModeratorLayout,\n})\n\nfunction ModeratorLayout() {\n const { auth } = Route.useRouteContext()\n\n return (\n \u003cdiv>\n \u003cdiv className=\"bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded mb-4\">\n \u003cstrong>Moderator Area:\u003c/strong> Role: {auth.user?.roles.join(', ')}\n \u003c/div>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n### 3. Permission-Based Routes\n\nCreate `src/routes/_authenticated/_users.tsx`:\n\n```tsx\nimport { createFileRoute, redirect, Outlet } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/_users')({\n beforeLoad: ({ context, location }) => {\n const requiredPermissions = ['users:read', 'users:write']\n if (!context.auth.hasAnyPermission(requiredPermissions)) {\n throw redirect({\n to: '/unauthorized',\n search: {\n redirect: location.href,\n reason: 'insufficient_permissions',\n },\n })\n }\n },\n component: () => \u003cOutlet />,\n})\n```\n\n---\n\n## Create Specific Protected Pages\n\n### 1. Admin Dashboard\n\nCreate `src/routes/_authenticated/_admin/dashboard.tsx`:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/_admin/dashboard')({\n component: AdminDashboard,\n})\n\nfunction AdminDashboard() {\n const { auth } = Route.useRouteContext()\n\n return (\n \u003cdiv className=\"p-6\">\n \u003ch1 className=\"text-3xl font-bold mb-6\">Admin Dashboard\u003c/h1>\n\n \u003cdiv className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n \u003cdiv className=\"bg-white p-6 rounded-lg shadow\">\n \u003ch2 className=\"text-xl font-semibold mb-2\">User Management\u003c/h2>\n \u003cp className=\"text-gray-600\">Manage all users in the system\u003c/p>\n \u003cbutton className=\"mt-4 bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700\">\n View Users\n \u003c/button>\n \u003c/div>\n\n \u003cdiv className=\"bg-white p-6 rounded-lg shadow\">\n \u003ch2 className=\"text-xl font-semibold mb-2\">System Settings\u003c/h2>\n \u003cp className=\"text-gray-600\">Configure system-wide settings\u003c/p>\n \u003cbutton className=\"mt-4 bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700\">\n Open Settings\n \u003c/button>\n \u003c/div>\n\n \u003cdiv className=\"bg-white p-6 rounded-lg shadow\">\n \u003ch2 className=\"text-xl font-semibold mb-2\">Reports\u003c/h2>\n \u003cp className=\"text-gray-600\">View system reports and analytics\u003c/p>\n \u003cbutton className=\"mt-4 bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700\">\n View Reports\n \u003c/button>\n \u003c/div>\n \u003c/div>\n\n \u003cdiv className=\"mt-8 bg-gray-100 p-4 rounded\">\n \u003ch3 className=\"font-semibold\">Your Info:\u003c/h3>\n \u003cp>Username: {auth.user?.username}\u003c/p>\n \u003cp>Roles: {auth.user?.roles.join(', ')}\u003c/p>\n \u003cp>Permissions: {auth.user?.permissions.join(', ')}\u003c/p>\n \u003c/div>\n \u003c/div>\n )\n}\n```\n\n### 2. User Management Page\n\nCreate `src/routes/_authenticated/_users/manage.tsx`:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/_users/manage')({\n beforeLoad: ({ context }) => {\n // Additional permission check at the page level\n if (!context.auth.hasPermission('users:write')) {\n throw new Error('You need write permissions to manage users')\n }\n },\n component: UserManagement,\n})\n\nfunction UserManagement() {\n const { auth } = Route.useRouteContext()\n\n const canEdit = auth.hasPermission('users:write')\n const canDelete = auth.hasPermission('users:delete')\n\n return (\n \u003cdiv className=\"p-6\">\n \u003ch1 className=\"text-3xl font-bold mb-6\">User Management\u003c/h1>\n\n \u003cdiv className=\"bg-white rounded-lg shadow overflow-hidden\">\n \u003ctable className=\"min-w-full\">\n \u003cthead className=\"bg-gray-50\">\n \u003ctr>\n \u003cth className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">\n Name\n \u003c/th>\n \u003cth className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">\n Email\n \u003c/th>\n \u003cth className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">\n Role\n \u003c/th>\n \u003cth className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">\n Actions\n \u003c/th>\n \u003c/tr>\n \u003c/thead>\n \u003ctbody className=\"divide-y divide-gray-200\">\n \u003ctr>\n \u003ctd className=\"px-6 py-4 whitespace-nowrap\">John Doe\u003c/td>\n \u003ctd className=\"px-6 py-4 whitespace-nowrap\">[email protected]\u003c/td>\n \u003ctd className=\"px-6 py-4 whitespace-nowrap\">\n \u003cspan className=\"inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800\">\n User\n \u003c/span>\n \u003c/td>\n \u003ctd className=\"px-6 py-4 whitespace-nowrap text-sm\">\n {canEdit && (\n \u003cbutton className=\"text-blue-600 hover:text-blue-900 mr-4\">\n Edit\n \u003c/button>\n )}\n {canDelete && (\n \u003cbutton className=\"text-red-600 hover:text-red-900\">\n Delete\n \u003c/button>\n )}\n \u003c/td>\n \u003c/tr>\n \u003c/tbody>\n \u003c/table>\n \u003c/div>\n\n \u003cdiv className=\"mt-6 p-4 bg-blue-50 rounded\">\n \u003ch3 className=\"font-semibold text-blue-800\">Your Permissions:\u003c/h3>\n \u003cul className=\"text-blue-700 text-sm\">\n {auth.user?.permissions.map((permission) => (\n \u003cli key={permission}>✓ {permission}\u003c/li>\n ))}\n \u003c/ul>\n \u003c/div>\n \u003c/div>\n )\n}\n```\n\n---\n\n## Create Unauthorized Page\n\nCreate `src/routes/unauthorized.tsx`:\n\n```tsx\nimport { createFileRoute, Link } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/unauthorized')({\n validateSearch: (search) => ({\n redirect: (search.redirect as string) || '/dashboard',\n reason: (search.reason as string) || 'insufficient_permissions',\n }),\n component: UnauthorizedPage,\n})\n\nfunction UnauthorizedPage() {\n const { redirect, reason } = Route.useSearch()\n const { auth } = Route.useRouteContext()\n\n const reasonMessages = {\n insufficient_role: 'You do not have the required role to access this page.',\n insufficient_permissions:\n 'You do not have the required permissions to access this page.',\n default: 'You are not authorized to access this page.',\n }\n\n const message =\n reasonMessages[reason as keyof typeof reasonMessages] ||\n reasonMessages.default\n\n return (\n \u003cdiv className=\"min-h-screen flex items-center justify-center bg-gray-50\">\n \u003cdiv className=\"max-w-md w-full bg-white shadow-lg rounded-lg p-8 text-center\">\n \u003cdiv className=\"mb-6\">\n \u003cdiv className=\"mx-auto w-16 h-16 bg-red-100 rounded-full flex items-center justify-center\">\n \u003csvg\n className=\"w-8 h-8 text-red-600\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n \u003cpath\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n \u003c/svg>\n \u003c/div>\n \u003c/div>\n\n \u003ch1 className=\"text-2xl font-bold text-gray-900 mb-4\">Access Denied\u003c/h1>\n \u003cp className=\"text-gray-600 mb-6\">{message}\u003c/p>\n\n \u003cdiv className=\"mb-6 text-sm text-gray-500\">\n \u003cp>\n \u003cstrong>Your roles:\u003c/strong> {auth.user?.roles.join(', ') || 'None'}\n \u003c/p>\n \u003cp>\n \u003cstrong>Your permissions:\u003c/strong>{' '}\n {auth.user?.permissions.join(', ') || 'None'}\n \u003c/p>\n \u003c/div>\n\n \u003cdiv className=\"space-y-3\">\n \u003cLink\n to=\"/dashboard\"\n className=\"block w-full bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700 transition-colors\"\n >\n Go to Dashboard\n \u003c/Link>\n\n \u003cLink\n to={redirect}\n className=\"block w-full bg-gray-200 text-gray-800 py-2 px-4 rounded hover:bg-gray-300 transition-colors\"\n >\n Try Again\n \u003c/Link>\n \u003c/div>\n \u003c/div>\n \u003c/div>\n )\n}\n```\n\n---\n\n## Component-Level Permission Checks\n\n### 1. Conditional Rendering Hook\n\nCreate `src/hooks/usePermissions.ts`:\n\n```tsx\nimport { useRouter } from '@tanstack/react-router'\n\nexport function usePermissions() {\n const router = useRouter()\n const auth = router.options.context.auth\n\n return {\n hasRole: auth.hasRole,\n hasAnyRole: auth.hasAnyRole,\n hasPermission: auth.hasPermission,\n hasAnyPermission: auth.hasAnyPermission,\n user: auth.user,\n }\n}\n```\n\n### 2. Permission Guard Component\n\nCreate `src/components/PermissionGuard.tsx`:\n\n```tsx\ninterface PermissionGuardProps {\n children: React.ReactNode\n roles?: string[]\n permissions?: string[]\n requireAll?: boolean\n fallback?: React.ReactNode\n}\n\nexport function PermissionGuard({\n children,\n roles = [],\n permissions = [],\n requireAll = false,\n fallback = null,\n}: PermissionGuardProps) {\n const { hasAnyRole, hasAnyPermission, hasRole, hasPermission } =\n usePermissions()\n\n const hasRequiredRoles =\n roles.length === 0 ||\n (requireAll ? roles.every((role) => hasRole(role)) : hasAnyRole(roles))\n\n const hasRequiredPermissions =\n permissions.length === 0 ||\n (requireAll\n ? permissions.every((permission) => hasPermission(permission))\n : hasAnyPermission(permissions))\n\n if (hasRequiredRoles && hasRequiredPermissions) {\n return \u003c>{children}\u003c/>\n }\n\n return \u003c>{fallback}\u003c/>\n}\n```\n\n### 3. Using Permission Guards\n\n```tsx\nimport { PermissionGuard } from '../components/PermissionGuard'\n\nfunction SomeComponent() {\n return (\n \u003cdiv>\n \u003ch1>Dashboard\u003c/h1>\n\n \u003cPermissionGuard roles={['admin']}>\n \u003cbutton className=\"bg-red-600 text-white px-4 py-2 rounded\">\n Admin Only Button\n \u003c/button>\n \u003c/PermissionGuard>\n\n \u003cPermissionGuard\n permissions={['users:write']}\n fallback={\u003cp className=\"text-gray-500\">You cannot edit users\u003c/p>}\n >\n \u003cbutton className=\"bg-blue-600 text-white px-4 py-2 rounded\">\n Edit Users\n \u003c/button>\n \u003c/PermissionGuard>\n\n \u003cPermissionGuard\n roles={['admin', 'moderator']}\n permissions={['content:moderate']}\n requireAll={true}\n >\n \u003cbutton className=\"bg-yellow-600 text-white px-4 py-2 rounded\">\n Moderate Content (Admin/Mod + Permission)\n \u003c/button>\n \u003c/PermissionGuard>\n \u003c/div>\n )\n}\n```\n\n---\n\n## Advanced Permission Patterns\n\n### 1. Resource-Based Permissions\n\n```tsx\n// Check if user can edit a specific resource\nfunction canEditResource(auth: AuthState, resourceId: string, ownerId: string) {\n // Admin can edit anything\n if (auth.hasRole('admin')) return true\n\n // Owner can edit their own resources\n if (auth.user?.id === ownerId && auth.hasPermission('resource:edit:own'))\n return true\n\n // Moderators can edit with permission\n if (auth.hasRole('moderator') && auth.hasPermission('resource:edit:any'))\n return true\n\n return false\n}\n\n// Usage in component\nfunction ResourceEditor({ resource }) {\n const { auth } = Route.useRouteContext()\n\n if (!canEditResource(auth, resource.id, resource.ownerId)) {\n return \u003cdiv>You cannot edit this resource\u003c/div>\n }\n\n return \u003cEditForm resource={resource} />\n}\n```\n\n### 2. Time-Based Permissions\n\n```tsx\nfunction hasTimeBasedPermission(auth: AuthState, permission: string) {\n const userPermissions = auth.user?.permissions || []\n const hasPermission = userPermissions.includes(permission)\n\n // Check if permission has time restrictions\n const timeRestricted = userPermissions.find((p) =>\n p.startsWith(`${permission}:time:`),\n )\n\n if (timeRestricted) {\n const [, , startHour, endHour] = timeRestricted.split(':')\n const currentHour = new Date().getHours()\n return (\n currentHour >= parseInt(startHour) && currentHour \u003c= parseInt(endHour)\n )\n }\n\n return hasPermission\n}\n```\n\n---\n\n## Common Problems\n\n### Role/Permission Data Not Loading\n\n**Problem:** User roles/permissions are undefined in routes.\n\n**Solution:** Ensure your authentication API returns complete user data:\n\n```tsx\nconst login = async (username: string, password: string) => {\n const response = await fetch('/api/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ username, password }),\n })\n\n if (response.ok) {\n const userData = await response.json()\n // Ensure userData includes roles and permissions\n console.log('User data:', userData) // Debug log\n setUser(userData)\n setIsAuthenticated(true)\n }\n}\n```\n\n### Permission Checks Too Restrictive\n\n**Problem:** Users locked out of areas they should access.\n\n**Solution:** Use hierarchical permissions and role inheritance:\n\n```tsx\nconst roleHierarchy = {\n admin: ['admin', 'moderator', 'user'],\n moderator: ['moderator', 'user'],\n user: ['user'],\n}\n\nconst hasRole = (requiredRole: string) => {\n const userRoles = user?.roles || []\n return userRoles.some((userRole) =>\n roleHierarchy[userRole]?.includes(requiredRole),\n )\n}\n```\n\n### Performance Issues with Many Permission Checks\n\n**Problem:** Too many permission checks slowing down renders.\n\n**Solution:** Memoize permission computations:\n\n```tsx\nimport { useMemo } from 'react'\n\nfunction usePermissions() {\n const { auth } = Route.useRouteContext()\n\n const permissions = useMemo(\n () => ({\n canEditUsers: auth.hasPermission('users:write'),\n canDeleteUsers: auth.hasPermission('users:delete'),\n isAdmin: auth.hasRole('admin'),\n isModerator: auth.hasAnyRole(['admin', 'moderator']),\n }),\n [auth.user?.roles, auth.user?.permissions],\n )\n\n return permissions\n}\n```\n\n---\n\n## Common Next Steps\n\nAfter setting up RBAC, you might want to:\n\n- [How to Set Up Basic Authentication](./setup-authentication.md) - Core auth implementation\n- [How to Integrate Authentication Providers](./setup-auth-providers.md) - Use external auth services\n\n\n\n## Related Resources\n\n- [Authenticated Routes Guide](../guide/authenticated-routes.md) - Core authentication concepts\n- [Router Context Guide](../guide/router-context.md) - Understanding router context\n- RBAC Best Practices - General RBAC principles\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":19513,"content_sha256":"5d1a6de1863d22ba7353c6a6c265740f8b87c4f4500b9afb1752f8c9ee0c59e5"},{"filename":"references/docs/router/how-to/setup-ssr.md","content":"---\ntitle: How to Set Up Server-Side Rendering (SSR)\n---\n\n> [!IMPORTANT]\n> **[TanStack Start](/start/latest/docs/framework/react/overview) is the recommended way to set up SSR** - it provides SSR, streaming, and deployment with zero configuration.\n\n> Use the manual setup below only if you need to integrate with an existing server.\n\n## Quick Start with TanStack Start\n\n```bash\nnpx create-tsrouter-app@latest my-app --template start\ncd my-app\nnpm run dev\n```\n\n## Manual SSR Setup\n\n### 1. Install Dependencies\n\n```bash\nnpm install express compression\nnpm install --save-dev @types/express\n```\n\n### 2. Create Shared Router Configuration\n\n```tsx\n// src/router.tsx\nimport { createRouter as createTanstackRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n\nexport function createRouter() {\n return createTanstackRouter({\n routeTree,\n context: {\n head: '', // For server-side head injection\n },\n defaultPreload: 'intent',\n })\n}\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: ReturnType\u003ctypeof createRouter>\n }\n}\n```\n\n### 3. Set Up Server Entry Point\n\n```tsx\n// src/entry-server.tsx\nimport { pipeline } from 'node:stream/promises'\nimport {\n RouterServer,\n createRequestHandler,\n renderRouterToString,\n} from '@tanstack/react-router/ssr/server'\nimport { createRouter } from './router'\nimport type express from 'express'\n\nexport async function render({\n req,\n res,\n head = '',\n}: {\n head?: string\n req: express.Request\n res: express.Response\n}) {\n // Convert Express request to Web API Request\n const url = new URL(req.originalUrl || req.url, 'https://localhost:3000').href\n\n const request = new Request(url, {\n method: req.method,\n headers: (() => {\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n headers.set(key, value as any)\n }\n return headers\n })(),\n })\n\n // Create request handler\n const handler = createRequestHandler({\n request,\n createRouter: () => {\n const router = createRouter()\n\n // Inject server context (like head tags from Vite)\n router.update({\n context: {\n ...router.options.context,\n head: head,\n },\n })\n return router\n },\n })\n\n // Render to string (non-streaming)\n const response = await handler(({ responseHeaders, router }) =>\n renderRouterToString({\n responseHeaders,\n router,\n children: \u003cRouterServer router={router} />,\n }),\n )\n\n // Convert Web API Response back to Express response\n res.statusMessage = response.statusText\n res.status(response.status)\n\n response.headers.forEach((value, name) => {\n res.setHeader(name, value)\n })\n\n // Stream response body\n return pipeline(response.body as any, res)\n}\n```\n\n### 4. Set Up Client Entry Point\n\n```tsx\n// src/entry-client.tsx\nimport { hydrateRoot } from 'react-dom/client'\nimport { RouterClient } from '@tanstack/react-router/ssr/client'\nimport { createRouter } from './router'\n\nconst router = createRouter()\n\nhydrateRoot(document, \u003cRouterClient router={router} />)\n```\n\n### 5. Configure Vite for SSR\n\n```ts\n// vite.config.ts\nimport path from 'node:path'\nimport url from 'node:url'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nconst __filename = url.fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\nexport default defineConfig(({ isSsrBuild }) => ({\n plugins: [\n tanstackRouter({\n autoCodeSplitting: true,\n }),\n react(),\n ],\n build: isSsrBuild\n ? {\n // SSR build configuration\n ssr: true,\n outDir: 'dist/server',\n emitAssets: true,\n copyPublicDir: false,\n rollupOptions: {\n input: path.resolve(__dirname, 'src/entry-server.tsx'),\n output: {\n entryFileNames: '[name].js',\n chunkFileNames: 'assets/[name]-[hash].js',\n assetFileNames: 'assets/[name]-[hash][extname]',\n },\n },\n }\n : {\n // Client build configuration\n outDir: 'dist/client',\n emitAssets: true,\n copyPublicDir: true,\n rollupOptions: {\n input: path.resolve(__dirname, 'src/entry-client.tsx'),\n output: {\n entryFileNames: '[name].js',\n chunkFileNames: 'assets/[name]-[hash].js',\n assetFileNames: 'assets/[name]-[hash][extname]',\n },\n },\n },\n}))\n```\n\n### 6. Update Root Route for HTML Structure\n\n```tsx\n// src/routes/__root.tsx\nimport {\n HeadContent,\n Outlet,\n createRootRouteWithContext,\n} from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\ninterface RouterContext {\n head: string\n}\n\nexport const Route = createRootRouteWithContext\u003cRouterContext>()({\n head: () => ({\n links: [\n { rel: 'icon', href: '/favicon.ico' },\n { rel: 'apple-touch-icon', href: '/logo192.png' },\n { rel: 'manifest', href: '/manifest.json' },\n ],\n meta: [\n {\n charSet: 'UTF-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1.0',\n },\n {\n title: 'TanStack Router SSR App',\n },\n ],\n scripts: [\n // Development scripts\n ...(!import.meta.env.PROD\n ? [\n {\n type: 'module',\n children: `import RefreshRuntime from \"/@react-refresh\"\n RefreshRuntime.injectIntoGlobalHook(window)\n window.$RefreshReg$ = () => {}\n window.$RefreshSig$ = () => (type) => type\n window.__vite_plugin_react_preamble_installed__ = true`,\n },\n {\n type: 'module',\n src: '/@vite/client',\n },\n ]\n : []),\n // Entry script\n {\n type: 'module',\n src: import.meta.env.PROD\n ? '/entry-client.js'\n : '/src/entry-client.tsx',\n },\n ],\n }),\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \u003chtml lang=\"en\">\n \u003chead>\n \u003cHeadContent />\n \u003c/head>\n \u003cbody>\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/body>\n \u003c/html>\n )\n}\n```\n\n### 7. Create Express Server\n\n```js\n// server.js\nimport path from 'node:path'\nimport express from 'express'\nimport compression from 'compression'\n\nconst isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD\n\nexport async function createServer(\n root = process.cwd(),\n isProd = process.env.NODE_ENV === 'production',\n hmrPort = process.env.VITE_DEV_SERVER_PORT,\n) {\n const app = express()\n\n let vite\n if (!isProd) {\n // Development mode with Vite middleware\n vite = await (\n await import('vite')\n ).createServer({\n root,\n logLevel: isTest ? 'error' : 'info',\n server: {\n middlewareMode: true,\n watch: {\n usePolling: true,\n interval: 100,\n },\n hmr: {\n port: hmrPort,\n },\n },\n appType: 'custom',\n })\n app.use(vite.middlewares)\n } else {\n // Production mode\n app.use(compression())\n app.use(express.static('./dist/client'))\n }\n\n app.use('*', async (req, res) => {\n try {\n const url = req.originalUrl\n\n // Check for static assets\n if (path.extname(url) !== '') {\n console.warn(`${url} is not a valid router path`)\n res.status(404).end(`${url} is not a valid router path`)\n return\n }\n\n // Extract head content from Vite in development\n let viteHead = ''\n if (!isProd) {\n const transformedHtml = await vite.transformIndexHtml(\n url,\n `\u003chtml>\u003chead>\u003c/head>\u003cbody>\u003c/body>\u003c/html>`,\n )\n viteHead = transformedHtml.substring(\n transformedHtml.indexOf('\u003chead>') + 6,\n transformedHtml.indexOf('\u003c/head>'),\n )\n }\n\n // Load server entry\n const entry = await (async () => {\n if (!isProd) {\n return vite.ssrLoadModule('/src/entry-server.tsx')\n } else {\n return import('./dist/server/entry-server.js')\n }\n })()\n\n console.info('Rendering:', url)\n await entry.render({ req, res, head: viteHead })\n } catch (e) {\n !isProd && vite.ssrFixStacktrace(e)\n console.error(e.stack)\n res.status(500).end(e.stack)\n }\n })\n\n return { app, vite }\n}\n\nif (!isTest) {\n createServer().then(({ app }) =>\n app.listen(3000, () => {\n console.info('Server running at http://localhost:3000')\n }),\n )\n}\n```\n\n### 8. Update Package Scripts\n\n```json\n{\n \"scripts\": {\n \"dev\": \"node server.js\",\n \"build\": \"npm run build:client && npm run build:server\",\n \"build:client\": \"vite build\",\n \"build:server\": \"vite build --ssr\",\n \"start\": \"NODE_ENV=production node server.js\"\n }\n}\n```\n\n## Streaming SSR\n\nFor better performance, enable streaming SSR by replacing `renderRouterToString` with `renderRouterToStream`:\n\n```tsx\n// src/entry-server.tsx\nimport { renderRouterToStream } from '@tanstack/react-router/ssr/server'\n\n// Replace renderRouterToString with:\nconst response = await handler(({ request, responseHeaders, router }) =>\n renderRouterToStream({\n request,\n responseHeaders,\n router,\n children: \u003cRouterServer router={router} />,\n }),\n)\n```\n\n### Streaming Vite Configuration\n\nFor streaming SSR, update your Vite config:\n\n```ts\n// vite.config.ts\nexport default defineConfig(({ isSsrBuild }) => ({\n plugins: [\n tanstackRouter({\n autoCodeSplitting: true,\n enableStreaming: true, // Enable streaming support\n }),\n react(),\n ],\n // ... rest of config\n ssr: {\n optimizeDeps: {\n include: ['@tanstack/react-router/ssr/server'],\n },\n },\n}))\n```\n\n## Common Problems\n\n> [!TIP]\n> **Most of these problems are automatically solved by [TanStack Start](/start/latest/docs/framework/react/overview).** The issues below are primarily relevant for manual SSR setups.\n\n### React Import Errors\n\n**Problem:** `ReferenceError: React is not defined` during SSR\n\n**Solution:** Ensure React is properly imported in components:\n\n```tsx\n// In your route components\nimport React from 'react' // Add explicit import\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/')({\n component: () => \u003cdiv>Hello World\u003c/div>, // React is now available\n})\n```\n\n### Hydration Mismatches\n\n**Problem:** Client HTML doesn't match server HTML\n\n**Solution:** Ensure consistent rendering between server and client:\n\n```tsx\n// Use useIsomorphicLayoutEffect for browser-only effects\nimport { useLayoutEffect, useEffect } from 'react'\n\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nfunction MyComponent() {\n useIsomorphicLayoutEffect(() => {\n // Browser-only code that won't cause hydration mismatches\n }, [])\n}\n```\n\n### Bun Runtime Issues\n\n**Problem:** `Cannot find module \"react-dom/server\"` with Bun\n\n**Solution:** Use Node.js compatibility or create Bun-specific builds:\n\n```json\n{\n \"scripts\": {\n \"build:bun\": \"bun build --target=bun --outdir=dist/bun src/entry-server.tsx\"\n }\n}\n```\n\n### Module Resolution Errors\n\n**Problem:** SSR modules not resolving correctly\n\n**Solution:** Configure Vite SSR externals:\n\n```ts\n// vite.config.ts\nexport default defineConfig({\n ssr: {\n noExternal: [\n // Packages that need to be bundled for SSR\n '@tanstack/react-router',\n ],\n external: [\n // Packages that should remain external\n 'express',\n ],\n },\n})\n```\n\n### Streaming Configuration Issues\n\n**Problem:** Streaming SSR not working with existing Vite setup\n\n**Solution:** Ensure proper streaming configuration:\n\n```ts\n// vite.config.ts - Additional streaming config\nexport default defineConfig({\n define: {\n 'process.env.STREAMING_SSR': JSON.stringify(true),\n },\n optimizeDeps: {\n include: ['@tanstack/react-router/ssr/server'],\n },\n})\n```\n\n### Build Output Issues\n\n**Problem:** Server build missing assets or incorrect paths\n\n**Solution:** Verify build configuration:\n\n```ts\n// vite.config.ts\nconst ssrConfig = {\n ssr: true,\n outDir: 'dist/server',\n ssrEmitAssets: true, // Important for asset handling\n copyPublicDir: false,\n rollupOptions: {\n input: path.resolve(__dirname, 'src/entry-server.tsx'),\n external: ['express', 'compression'], // External deps\n },\n}\n```\n\n## Related Resources\n\n- [TanStack Start](/start/latest/docs/framework/react/overview) - **Recommended full-stack React framework with SSR**\n- [SSR Guide (Detailed)](../guide/ssr.md) - Comprehensive SSR concepts, utilities, and theory\n- [Data Loading](../guide/data-loading.md) - SSR-compatible data loading patterns\n\n## Common Next Steps\n\n- [Deploy to Production](./deploy-to-production.md) - Deploy your SSR app\n- [Setup Authentication](./setup-authentication.md) - Add auth to SSR routes\n- [Debug Router Issues](./debug-router-issues.md) - Troubleshoot SSR-specific routing problems\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13029,"content_sha256":"dc1f74da54043541cf552f93a455c5d219ce1231a7cae8801bf76ea6cae6da5e"},{"filename":"references/docs/router/how-to/setup-testing.md","content":"# How to Set Up Testing with Code-Based Routing\n\nThis guide covers setting up comprehensive testing for TanStack Router applications that use code-based routing, including unit tests, integration tests, and end-to-end testing strategies.\n\n## Quick Start\n\nSet up testing by configuring your test framework (Vitest/Jest), creating router test utilities, and implementing patterns for testing navigation, route components, and data loading with manually defined routes.\n\n> **Using File-Based Routing?** See [How to Test File-Based Routing](./test-file-based-routing.md) for patterns specific to file-based routing applications.\n\n---\n\n## Configure Test Framework\n\n### 1. Install Dependencies\n\nFor **Vitest** (recommended):\n\n```bash\nnpm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom\n```\n\nFor **Jest**:\n\n```bash\nnpm install -D jest @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom\n```\n\n### 2. Configure Vitest\n\nCreate `vitest.config.ts`:\n\n```ts\nimport { defineConfig } from 'vitest/config'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n plugins: [react()],\n test: {\n environment: 'jsdom',\n setupFiles: ['./src/test/setup.ts'],\n typecheck: { enabled: true },\n watch: false,\n },\n})\n```\n\n### 3. Create Test Setup\n\nCreate `src/test/setup.ts`:\n\n```ts\nimport '@testing-library/jest-dom/vitest'\n\n// @ts-expect-error\nglobal.IS_REACT_ACT_ENVIRONMENT = true\n```\n\n---\n\n## Code-Based Router Testing Patterns\n\nThe following patterns are specifically designed for applications using code-based routing where you manually create routes with `createRoute()` and build route trees programmatically.\n\n### 1. TanStack Router Internal Pattern (Recommended)\n\nThe TanStack Router team uses this pattern internally for testing router components:\n\n```tsx\nimport { beforeEach, afterEach, describe, expect, test, vi } from 'vitest'\nimport { cleanup, render, screen } from '@testing-library/react'\nimport {\n RouterProvider,\n createBrowserHistory,\n createRootRoute,\n createRoute,\n createRouter,\n} from '@tanstack/react-router'\nimport type { RouterHistory } from '@tanstack/react-router'\n\nlet history: RouterHistory\n\nbeforeEach(() => {\n history = createBrowserHistory()\n expect(window.location.pathname).toBe('/')\n})\n\nafterEach(() => {\n history.destroy()\n window.history.replaceState(null, 'root', '/')\n vi.clearAllMocks()\n vi.resetAllMocks()\n cleanup()\n})\n\ndescribe('Router Component Testing', () => {\n test('should render route component', async () => {\n const rootRoute = createRootRoute()\n const indexRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n component: () => \u003ch1>IndexTitle\u003c/h1>,\n })\n\n const routeTree = rootRoute.addChildren([indexRoute])\n const router = createRouter({ routeTree, history })\n\n render(\u003cRouterProvider router={router} />)\n\n expect(await screen.findByText('IndexTitle')).toBeInTheDocument()\n })\n})\n```\n\n### 2. Alternative: Router Test Utilities (For Simpler Cases)\n\nCreate `src/test/router-utils.tsx`:\n\n```tsx\nimport React from 'react'\nimport { render, RenderOptions } from '@testing-library/react'\nimport {\n createRouter,\n createRootRoute,\n createRoute,\n RouterProvider,\n Outlet,\n} from '@tanstack/react-router'\nimport { createMemoryHistory } from '@tanstack/react-router'\n\n// Create a root route for testing\nconst rootRoute = createRootRoute({\n component: () => \u003cOutlet />,\n})\n\n// Test router factory\nexport function createTestRouter(routes: any[], initialLocation = '/') {\n const routeTree = rootRoute.addChildren(routes)\n\n const router = createRouter({\n routeTree,\n history: createMemoryHistory({\n initialEntries: [initialLocation],\n }),\n })\n\n return router\n}\n\n// Wrapper component for testing\ninterface RouterWrapperProps {\n children: React.ReactNode\n router: any\n}\n\nfunction RouterWrapper({ children, router }: RouterWrapperProps) {\n return \u003cRouterProvider router={router}>{children}\u003c/RouterProvider>\n}\n\n// Custom render function with router\ninterface RenderWithRouterOptions extends Omit\u003cRenderOptions, 'wrapper'> {\n router?: any\n initialLocation?: string\n routes?: any[]\n}\n\nexport function renderWithRouter(\n ui: React.ReactElement,\n {\n router,\n initialLocation = '/',\n routes = [],\n ...renderOptions\n }: RenderWithRouterOptions = {},\n) {\n if (!router && routes.length > 0) {\n router = createTestRouter(routes, initialLocation)\n }\n\n if (!router) {\n throw new Error(\n 'Router is required. Provide either a router or routes array.',\n )\n }\n\n function Wrapper({ children }: { children: React.ReactNode }) {\n return \u003cRouterWrapper router={router}>{children}\u003c/RouterWrapper>\n }\n\n return {\n ...render(ui, { wrapper: Wrapper, ...renderOptions }),\n router,\n }\n}\n```\n\n### 2. Mock Route Factory\n\nCreate `src/test/mock-routes.tsx`:\n\n```tsx\nimport { createRoute } from '@tanstack/react-router'\nimport { rootRoute } from './router-utils'\n\nexport const createMockRoute = (\n path: string,\n component: React.ComponentType,\n options: any = {},\n) => {\n return createRoute({\n getParentRoute: () => rootRoute,\n path,\n component,\n ...options,\n })\n}\n\n// Common test components\nexport function TestComponent({ title = 'Test' }: { title?: string }) {\n return \u003cdiv data-testid=\"test-component\">{title}\u003c/div>\n}\n\nexport function LoadingComponent() {\n return \u003cdiv data-testid=\"loading\">Loading...\u003c/div>\n}\n\nexport function ErrorComponent({ error }: { error: Error }) {\n return \u003cdiv data-testid=\"error\">Error: {error.message}\u003c/div>\n}\n```\n\n---\n\n## Test Code-Based Route Components\n\n### 1. Basic Component Testing\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { createRoute } from '@tanstack/react-router'\nimport {\n renderWithRouter,\n rootRoute,\n TestComponent,\n} from '../test/router-utils'\n\ndescribe('Code-Based Route Component Testing', () => {\n it('should render route component', () => {\n const testRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n component: TestComponent,\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [testRoute],\n initialLocation: '/',\n })\n\n expect(screen.getByTestId('test-component')).toBeInTheDocument()\n })\n\n it('should render component with props from route context', () => {\n function ComponentWithContext() {\n const { title } = Route.useLoaderData()\n return \u003cdiv data-testid=\"context-component\">{title}\u003c/div>\n }\n\n const contextRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/context',\n component: ComponentWithContext,\n loader: () => ({ title: 'From Context' }),\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [contextRoute],\n initialLocation: '/context',\n })\n\n expect(screen.getByText('From Context')).toBeInTheDocument()\n })\n})\n```\n\n### 2. Testing Route Parameters\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { createRoute } from '@tanstack/react-router'\nimport { renderWithRouter, rootRoute } from '../test/router-utils'\n\ndescribe('Route Parameters', () => {\n it('should handle route params correctly', () => {\n function UserProfile() {\n const { userId } = Route.useParams()\n return \u003cdiv data-testid=\"user-profile\">User: {userId}\u003c/div>\n }\n\n const userRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/users/$userId',\n component: UserProfile,\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [userRoute],\n initialLocation: '/users/123',\n })\n\n expect(screen.getByText('User: 123')).toBeInTheDocument()\n })\n\n it('should handle search params correctly', () => {\n function SearchPage() {\n const { q, page } = Route.useSearch()\n return (\n \u003cdiv data-testid=\"search-results\">\n Query: {q}, Page: {page}\n \u003c/div>\n )\n }\n\n const searchRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/search',\n component: SearchPage,\n validateSearch: (search) => ({\n q: (search.q as string) || '',\n page: Number(search.page) || 1,\n }),\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [searchRoute],\n initialLocation: '/search?q=react&page=2',\n })\n\n expect(screen.getByText('Query: react, Page: 2')).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## Test Navigation\n\n### 1. Testing Link Components\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen, fireEvent } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { Link, createRoute } from '@tanstack/react-router'\nimport {\n renderWithRouter,\n rootRoute,\n TestComponent,\n} from '../test/router-utils'\n\ndescribe('Code-Based Route Navigation', () => {\n it('should navigate when link is clicked', async () => {\n const user = userEvent.setup()\n\n function HomePage() {\n return (\n \u003cdiv>\n \u003ch1>Home\u003c/h1>\n \u003cLink to=\"/about\" data-testid=\"about-link\">\n About\n \u003c/Link>\n \u003c/div>\n )\n }\n\n function AboutPage() {\n return \u003ch1>About Page\u003c/h1>\n }\n\n const homeRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n component: HomePage,\n })\n\n const aboutRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/about',\n component: AboutPage,\n })\n\n const { router } = renderWithRouter(\u003cdiv />, {\n routes: [homeRoute, aboutRoute],\n initialLocation: '/',\n })\n\n // Initial state\n expect(screen.getByText('Home')).toBeInTheDocument()\n expect(router.state.location.pathname).toBe('/')\n\n // Click link\n await user.click(screen.getByTestId('about-link'))\n\n // Check navigation\n expect(screen.getByText('About Page')).toBeInTheDocument()\n expect(router.state.location.pathname).toBe('/about')\n })\n\n it('should navigate programmatically', async () => {\n function NavigationTest() {\n const navigate = Route.useNavigate()\n\n const handleNavigate = () => {\n navigate({ to: '/dashboard', search: { tab: 'settings' } })\n }\n\n return (\n \u003cdiv>\n \u003ch1>Navigation Test\u003c/h1>\n \u003cbutton onClick={handleNavigate} data-testid=\"navigate-btn\">\n Go to Dashboard\n \u003c/button>\n \u003c/div>\n )\n }\n\n const testRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n component: NavigationTest,\n })\n\n const dashboardRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/dashboard',\n component: () => \u003ch1>Dashboard\u003c/h1>,\n validateSearch: (search) => ({\n tab: (search.tab as string) || 'general',\n }),\n })\n\n const { router } = renderWithRouter(\u003cdiv />, {\n routes: [testRoute, dashboardRoute],\n initialLocation: '/',\n })\n\n await userEvent.click(screen.getByTestId('navigate-btn'))\n\n expect(router.state.location.pathname).toBe('/dashboard')\n expect(router.state.location.search).toEqual({ tab: 'settings' })\n })\n})\n```\n\n### 2. Testing Route Guards\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { createRoute, redirect } from '@tanstack/react-router'\nimport { renderWithRouter, rootRoute } from '../test/router-utils'\n\ndescribe('Code-Based Route Guards', () => {\n it('should redirect unauthenticated users', () => {\n const mockAuth = { isAuthenticated: false }\n\n function ProtectedPage() {\n return \u003ch1>Protected Content\u003c/h1>\n }\n\n function LoginPage() {\n return \u003ch1>Login Required\u003c/h1>\n }\n\n const protectedRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/protected',\n component: ProtectedPage,\n beforeLoad: ({ context }) => {\n if (!mockAuth.isAuthenticated) {\n throw redirect({ to: '/login' })\n }\n },\n })\n\n const loginRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/login',\n component: LoginPage,\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [protectedRoute, loginRoute],\n initialLocation: '/protected',\n })\n\n // Should redirect to login\n expect(screen.getByText('Login Required')).toBeInTheDocument()\n })\n\n it('should allow authenticated users', () => {\n const mockAuth = { isAuthenticated: true }\n\n function ProtectedPage() {\n return \u003ch1>Protected Content\u003c/h1>\n }\n\n const protectedRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/protected',\n component: ProtectedPage,\n beforeLoad: ({ context }) => {\n if (!mockAuth.isAuthenticated) {\n throw redirect({ to: '/login' })\n }\n },\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [protectedRoute],\n initialLocation: '/protected',\n })\n\n expect(screen.getByText('Protected Content')).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## Test Data Loading\n\n### 1. Testing Loaders\n\n```tsx\nimport { describe, it, expect, vi } from 'vitest'\nimport { screen, waitFor } from '@testing-library/react'\nimport { createRoute } from '@tanstack/react-router'\nimport { renderWithRouter, rootRoute } from '../test/router-utils'\n\ndescribe('Code-Based Route Data Loading', () => {\n it('should load and display data from loader', async () => {\n const mockFetchUser = vi.fn().mockResolvedValue({\n id: 1,\n name: 'John Doe',\n email: '[email protected]',\n })\n\n function UserProfile() {\n const user = Route.useLoaderData()\n return (\n \u003cdiv data-testid=\"user-profile\">\n \u003ch1>{user.name}\u003c/h1>\n \u003cp>{user.email}\u003c/p>\n \u003c/div>\n )\n }\n\n const userRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/users/$userId',\n component: UserProfile,\n loader: ({ params }) => mockFetchUser(params.userId),\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [userRoute],\n initialLocation: '/users/1',\n })\n\n await waitFor(() => {\n expect(screen.getByText('John Doe')).toBeInTheDocument()\n expect(screen.getByText('[email protected]')).toBeInTheDocument()\n })\n\n expect(mockFetchUser).toHaveBeenCalledWith('1')\n })\n\n it('should handle loader errors', async () => {\n const mockFetchUser = vi.fn().mockRejectedValue(new Error('User not found'))\n\n function UserProfile() {\n const user = Route.useLoaderData()\n return \u003cdiv>{user.name}\u003c/div>\n }\n\n function ErrorComponent({ error }: { error: Error }) {\n return \u003cdiv data-testid=\"error\">Error: {error.message}\u003c/div>\n }\n\n const userRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/users/$userId',\n component: UserProfile,\n loader: ({ params }) => mockFetchUser(params.userId),\n errorComponent: ErrorComponent,\n })\n\n renderWithRouter(\u003cdiv />, {\n routes: [userRoute],\n initialLocation: '/users/1',\n })\n\n await waitFor(() => {\n expect(screen.getByTestId('error')).toBeInTheDocument()\n expect(screen.getByText('Error: User not found')).toBeInTheDocument()\n })\n })\n})\n```\n\n### 2. Testing with React Query\n\n```tsx\nimport { describe, it, expect, vi } from 'vitest'\nimport { screen, waitFor } from '@testing-library/react'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { createRoute } from '@tanstack/react-router'\nimport { renderWithRouter, rootRoute } from '../test/router-utils'\n\ndescribe('React Query Integration', () => {\n it('should work with React Query', async () => {\n const queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n retry: false,\n },\n },\n })\n\n const mockFetchPosts = vi.fn().mockResolvedValue([\n { id: 1, title: 'Post 1' },\n { id: 2, title: 'Post 2' },\n ])\n\n function PostsList() {\n const posts = Route.useLoaderData()\n return (\n \u003cdiv data-testid=\"posts-list\">\n {posts.map((post: any) => (\n \u003cdiv key={post.id}>{post.title}\u003c/div>\n ))}\n \u003c/div>\n )\n }\n\n const postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/posts',\n component: PostsList,\n loader: ({ context: { queryClient } }) =>\n queryClient.ensureQueryData({\n queryKey: ['posts'],\n queryFn: mockFetchPosts,\n }),\n })\n\n function TestWrapper({ children }: { children: React.ReactNode }) {\n return (\n \u003cQueryClientProvider client={queryClient}>\n {children}\n \u003c/QueryClientProvider>\n )\n }\n\n renderWithRouter(\u003cdiv />, {\n routes: [postsRoute],\n initialLocation: '/posts',\n wrapper: TestWrapper,\n })\n\n await waitFor(() => {\n expect(screen.getByText('Post 1')).toBeInTheDocument()\n expect(screen.getByText('Post 2')).toBeInTheDocument()\n })\n })\n})\n```\n\n---\n\n## Test with Context\n\n### 1. Testing Router Context\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport {\n createRootRouteWithContext,\n createRoute,\n Outlet,\n} from '@tanstack/react-router'\n\ninterface RouterContext {\n auth: {\n user: { id: string; name: string } | null\n isAuthenticated: boolean\n }\n}\n\ndescribe('Code-Based Router Context', () => {\n it('should provide context to routes', () => {\n const rootRouteWithContext = createRootRouteWithContext\u003cRouterContext>()({\n component: () => \u003cOutlet />,\n })\n\n function UserDashboard() {\n const { auth } = Route.useRouteContext()\n return (\n \u003cdiv data-testid=\"dashboard\">\n Welcome, {auth.user?.name || 'Guest'}!\n \u003c/div>\n )\n }\n\n const dashboardRoute = createRoute({\n getParentRoute: () => rootRouteWithContext,\n path: '/dashboard',\n component: UserDashboard,\n })\n\n const mockContext = {\n auth: {\n user: { id: '1', name: 'John Doe' },\n isAuthenticated: true,\n },\n }\n\n const router = createRouter({\n routeTree: rootRouteWithContext.addChildren([dashboardRoute]),\n context: mockContext,\n history: createMemoryHistory({\n initialEntries: ['/dashboard'],\n }),\n })\n\n render(\u003cRouterProvider router={router} />)\n\n expect(screen.getByText('Welcome, John Doe!')).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## E2E Testing with Playwright\n\n### 1. Playwright Configuration\n\nCreate `playwright.config.ts`:\n\n```ts\nimport { defineConfig, devices } from '@playwright/test'\n\nexport default defineConfig({\n testDir: './e2e',\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n workers: process.env.CI ? 1 : undefined,\n reporter: 'html',\n use: {\n baseURL: 'http://localhost:3000',\n trace: 'on-first-retry',\n },\n projects: [\n {\n name: 'chromium',\n use: { ...devices['Desktop Chrome'] },\n },\n ],\n webServer: {\n command: 'npm run dev',\n url: 'http://localhost:3000',\n reuseExistingServer: !process.env.CI,\n },\n})\n```\n\n### 2. E2E Test Example\n\nCreate `e2e/navigation.spec.ts`:\n\n```ts\nimport { test, expect } from '@playwright/test'\n\ntest.describe('Code-Based Router Navigation', () => {\n test('should navigate between pages', async ({ page }) => {\n await page.goto('/')\n\n // Check home page\n await expect(page.locator('h1')).toContainText('Home')\n\n // Navigate to about page\n await page.click('text=About')\n await expect(page).toHaveURL('/about')\n await expect(page.locator('h1')).toContainText('About')\n\n // Use browser back button\n await page.goBack()\n await expect(page).toHaveURL('/')\n await expect(page.locator('h1')).toContainText('Home')\n })\n\n test('should handle search parameters', async ({ page }) => {\n await page.goto('/search?q=react')\n\n await expect(page.locator('[data-testid=\"search-input\"]')).toHaveValue(\n 'react',\n )\n await expect(page).toHaveURL('/search?q=react')\n\n // Update search\n await page.fill('[data-testid=\"search-input\"]', 'vue')\n await page.press('[data-testid=\"search-input\"]', 'Enter')\n\n await expect(page).toHaveURL('/search?q=vue')\n })\n\n test('should handle authentication flow', async ({ page }) => {\n // Try to access protected route\n await page.goto('/dashboard')\n\n // Should redirect to login\n await expect(page).toHaveURL('/login')\n\n // Login\n await page.fill('[data-testid=\"username\"]', 'testuser')\n await page.fill('[data-testid=\"password\"]', 'password')\n await page.click('[data-testid=\"login-btn\"]')\n\n // Should redirect back to dashboard\n await expect(page).toHaveURL('/dashboard')\n await expect(page.locator('h1')).toContainText('Dashboard')\n })\n})\n```\n\n---\n\n## Code-Based Routing Testing Best Practices\n\n### 1. Test Organization\n\n```\nsrc/\n├── components/\n│ ├── Header.tsx\n│ └── Header.test.tsx\n├── routes/\n│ ├── posts.tsx # Code-based route definitions\n│ ├── posts.test.tsx\n│ └── index.tsx\n├── test/\n│ ├── setup.ts\n│ ├── router-utils.tsx # Code-based router utilities\n│ └── mock-routes.tsx # Manual route factories\n└── __tests__/\n ├── integration/\n └── e2e/\n```\n\n### 2. Common Patterns\n\n```tsx\n// Mock external dependencies for code-based routes\nvi.mock('../api/users', () => ({\n fetchUser: vi.fn(),\n updateUser: vi.fn(),\n}))\n\n// Test utility for common code-based route setups\nexport function createAuthenticatedRouter(user = mockUser) {\n // Manually create routes for testing\n const protectedRoutes = [\n createRoute({\n getParentRoute: () => rootRoute,\n path: '/dashboard',\n component: DashboardComponent,\n }),\n ]\n\n return createTestRouter(protectedRoutes, {\n context: {\n auth: { user, isAuthenticated: true },\n },\n })\n}\n\n// Group related tests\ndescribe('User Management', () => {\n describe('when authenticated', () => {\n it('should show user dashboard', () => {\n // Test implementation\n })\n })\n\n describe('when not authenticated', () => {\n it('should redirect to login', () => {\n // Test implementation\n })\n })\n})\n```\n\n---\n\n## Common Problems\n\n### Test Environment Issues\n\n**Problem:** Tests fail with \"window is not defined\" errors.\n\n**Solution:** Ensure jsdom environment is configured:\n\n```ts\n// vitest.config.ts\nexport default defineConfig({\n test: {\n environment: 'jsdom',\n },\n})\n```\n\n### Router Context Missing\n\n**Problem:** Components can't access router context in tests.\n\n**Solution:** Use the custom render function with router:\n\n```tsx\n// ✅ Correct\nrenderWithRouter(\u003cComponent />, { routes, initialLocation })\n\n// ❌ Wrong\nrender(\u003cComponent />)\n```\n\n### Async Data Loading\n\n**Problem:** Tests fail because they don't wait for data loading.\n\n**Solution:** Use proper async testing patterns:\n\n```tsx\nawait waitFor(() => {\n expect(screen.getByText('Loaded Data')).toBeInTheDocument()\n})\n```\n\n---\n\n## Common Next Steps\n\nAfter setting up code-based routing testing, you might want to:\n\n- [How to Test File-Based Routing](./test-file-based-routing.md) - Specific patterns for file-based routing apps\n- [How to Set Up Basic Authentication](./setup-authentication.md) - Test authentication flows\n- [How to Debug Common Router Issues](./debug-router-issues.md) - Debug test failures\n\n\n\n## Related Resources\n\n- [Code-Based Routing Guide](../routing/code-based-routing.md) - Understanding code-based routing\n- Vitest Documentation - Testing framework\n- Testing Library React - Component testing utilities\n- Playwright Documentation - E2E testing framework\n- TanStack Router Examples - Example test setups\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":23745,"content_sha256":"a0597ee018147384bbe1d19c307204433520cdc72ec039666ddd8ca918b55832"},{"filename":"references/docs/router/how-to/share-search-params-across-routes.md","content":"---\ntitle: Share Search Parameters Across Routes\n---\n\n# How to Share Search Parameters Across Routes\n\nSearch parameters automatically inherit from parent routes in TanStack Router. When a parent route validates search parameters, child routes can access them via `Route.useSearch()` alongside their own parameters.\n\n## How Parameter Inheritance Works\n\nTanStack Router automatically merges search parameters from parent routes with child route parameters. This happens through the route hierarchy:\n\n1. **Parent route** validates shared parameters with `validateSearch`\n2. **Child routes** automatically inherit those validated parameters\n3. **`Route.useSearch()`** returns both local and inherited parameters\n\n## Global Parameters via Root Route\n\nShare parameters across your entire application by validating them in the root route:\n\n```tsx\n// routes/__root.tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst globalSearchSchema = z.object({\n theme: z.enum(['light', 'dark']).default('light'),\n lang: z.enum(['en', 'es', 'fr']).default('en'),\n debug: z.boolean().default(false),\n})\n\nexport const Route = createRootRoute({\n validateSearch: zodValidator(globalSearchSchema),\n component: RootComponent,\n})\n\nfunction RootComponent() {\n const { theme, lang, debug } = Route.useSearch()\n\n return (\n \u003cdiv className={`app theme-${theme} lang-${lang}`}>\n {debug && \u003cDebugPanel />}\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n```tsx\n// routes/products/index.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n page: z.number().default(1),\n category: z.string().default('all'),\n})\n\nexport const Route = createFileRoute('/products/')({\n validateSearch: zodValidator(productSearchSchema),\n component: ProductsPage,\n})\n\nfunction ProductsPage() {\n // Contains both local (page, category) AND inherited (theme, lang, debug) parameters\n const search = Route.useSearch()\n\n return (\n \u003cdiv>\n \u003ch1>Products (Theme: {search.theme})\u003c/h1>\n \u003cp>Page: {search.page}\u003c/p>\n \u003cp>Category: {search.category}\u003c/p>\n \u003c/div>\n )\n}\n```\n\n## Section-Specific Parameters via Layout Routes\n\nShare parameters within a section of your app using layout routes:\n\n```tsx\n// routes/_authenticated.tsx\nimport { createFileRoute, Outlet } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst authSearchSchema = z.object({\n impersonate: z.string().optional(),\n sidebar: z.boolean().default(true),\n notifications: z.boolean().default(true),\n})\n\nexport const Route = createFileRoute('/_authenticated')({\n validateSearch: zodValidator(authSearchSchema),\n component: AuthenticatedLayout,\n})\n\nfunction AuthenticatedLayout() {\n const search = Route.useSearch()\n\n return (\n \u003cdiv className=\"authenticated-layout\">\n {search.sidebar && \u003cSidebar />}\n \u003cmain className=\"main-content\">\n {search.notifications && \u003cNotificationBar />}\n \u003cOutlet />\n \u003c/main>\n {search.impersonate && \u003cImpersonationBanner user={search.impersonate} />}\n \u003c/div>\n )\n}\n```\n\n```tsx\n// routes/_authenticated/dashboard.tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_authenticated/dashboard')({\n component: DashboardPage,\n})\n\nfunction DashboardPage() {\n // Contains inherited auth parameters (impersonate, sidebar, notifications)\n const search = Route.useSearch()\n\n return (\n \u003cdiv>\n \u003ch1>Dashboard\u003c/h1>\n {search.impersonate && (\n \u003cAlert>Currently impersonating: {search.impersonate}\u003c/Alert>\n )}\n \u003cDashboardContent />\n \u003c/div>\n )\n}\n```\n\n## Common Use Cases\n\n**Global Application Settings:**\n\n- Theme, language, timezone\n- Debug flags, feature toggles\n- Analytics tracking (UTM parameters)\n\n**Section-Specific State:**\n\n- Authentication context (user role, impersonation)\n- Layout preferences (sidebar, density)\n- Workspace or organization context\n\n**Persistent UI State:**\n\n- Modal visibility, drawer state\n- Filter presets, view modes\n- Accessibility preferences\n\n## Common Problems\n\n### Problem: Parameters Not Inheriting\n\n**Cause**: Parent route not validating the shared parameters.\n\n```tsx\n// ❌ Root route missing validateSearch\nexport const Route = createRootRoute({\n component: RootComponent, // No validateSearch\n})\n\n// Child route can't access theme parameter\nfunction ProductsPage() {\n const search = Route.useSearch() // No theme available\n}\n```\n\n**Solution**: Add `validateSearch` to the parent route:\n\n```tsx\n// ✅ Root route validates shared parameters\nexport const Route = createRootRoute({\n validateSearch: zodValidator(globalSearchSchema),\n component: RootComponent,\n})\n```\n\n### Problem: Navigation Loses Shared Parameters\n\n**Cause**: Not preserving inherited parameters during navigation.\n\n```tsx\n// ❌ Navigation overwrites all search parameters\nrouter.navigate({\n to: '/products',\n search: { page: 1 }, // Loses theme, lang, etc.\n})\n```\n\n**Solution**: Preserve existing parameters with function syntax:\n\n```tsx\n// ✅ Preserve existing parameters\nrouter.navigate({\n to: '/products',\n search: (prev) => ({ ...prev, page: 1 }),\n})\n```\n\n### Problem: Type Errors with Inherited Parameters\n\n**Cause**: Child route schema doesn't account for inherited parameters.\n\n```tsx\n// ❌ TypeScript error: Property 'theme' doesn't exist\nconst search = Route.useSearch()\nconsole.log(search.theme) // Type error\n```\n\n**Solution**: TypeScript automatically infers inherited types when using `validateSearch`. No additional typing needed - the inheritance works automatically.\n\n## Production Checklist\n\n- [ ] **Clear ownership**: Document which route validates which shared parameters\n- [ ] **Avoid conflicts**: Use distinct parameter names across route levels\n- [ ] **Preserve on navigation**: Use function syntax to maintain inherited parameters\n- [ ] **Minimal URLs**: Only include essential shared parameters\n- [ ] **Graceful defaults**: Provide fallback values for all shared parameters\n\n\n\n## Related Resources\n\n- [Set Up Basic Search Parameters](./setup-basic-search-params.md) - Learn search parameter fundamentals\n- [Navigate with Search Parameters](./navigate-with-search-params.md) - Navigate while preserving search state\n- [Validate Search Parameters with Schemas](./validate-search-params.md) - Add type safety to shared parameters\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6523,"content_sha256":"4f6841af67c6037892cd17eedbff35901a02a88965fd258a7521d80371a1fb4d"},{"filename":"references/docs/router/how-to/test-file-based-routing.md","content":"---\ntitle: How to Test Router with File-Based Routing\n---\n\nThis guide covers testing TanStack Router applications that use file-based routing, including testing route generation, file-based route components, and file-based routing patterns.\n\n## Quick Start\n\nTest file-based routing by setting up route mocking utilities, testing generated route trees, and implementing patterns specific to file-based route structures and conventions.\n\n---\n\n## Understanding File-Based Routing Testing\n\nFile-based routing testing differs from code-based routing testing in several key ways:\n\n- **Generated Route Trees**: Routes are automatically generated from filesystem structure\n- **File Conventions**: Routes follow specific file naming conventions (`index.tsx`, `route.tsx`, `$param.tsx`)\n- **Route Discovery**: Routes are discovered through filesystem scanning rather than explicit imports\n- **Type Generation**: Route types are automatically generated and need special testing considerations\n\n---\n\n## Setting Up File-Based Route Testing\n\n### 1. Install Test Dependencies\n\nFor file-based routing testing, you'll need the same base dependencies as regular router testing:\n\n```bash\nnpm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom\n```\n\n### 2. Configure Test Environment\n\nCreate `vitest.config.ts` with file-based routing support:\n\n```ts\nimport { defineConfig } from 'vitest/config'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n // Configure for test environment\n routesDirectory: './src/routes',\n generatedRouteTree: './src/routeTree.gen.ts',\n disableLogging: true,\n }),\n react(),\n ],\n test: {\n environment: 'jsdom',\n setupFiles: ['./src/test/setup.ts'],\n typecheck: { enabled: true },\n watch: false,\n // Ensure route tree is generated before tests\n globals: true,\n },\n})\n```\n\n### 3. Create Route Testing Utilities\n\nCreate `src/test/file-route-utils.tsx`:\n\n```tsx\nimport React from 'react'\nimport { render, RenderOptions } from '@testing-library/react'\nimport {\n createRouter,\n RouterProvider,\n createMemoryHistory,\n} from '@tanstack/react-router'\n\n// Import the generated route tree\nimport { routeTree } from '../routeTree.gen'\n\n// Create test router with generated route tree\nexport function createTestRouterFromFiles(initialLocation = '/') {\n const router = createRouter({\n routeTree,\n history: createMemoryHistory({\n initialEntries: [initialLocation],\n }),\n context: {\n // Add any required context for your routes\n },\n })\n\n return router\n}\n\n// Custom render function for file-based routes\ninterface RenderWithFileRoutesOptions extends Omit\u003cRenderOptions, 'wrapper'> {\n initialLocation?: string\n routerContext?: any\n}\n\nexport function renderWithFileRoutes(\n ui: React.ReactElement,\n {\n initialLocation = '/',\n routerContext = {},\n ...renderOptions\n }: RenderWithFileRoutesOptions = {},\n) {\n const router = createRouter({\n routeTree,\n history: createMemoryHistory({\n initialEntries: [initialLocation],\n }),\n context: routerContext,\n })\n\n function Wrapper({ children }: { children: React.ReactNode }) {\n return \u003cRouterProvider router={router}>{children}\u003c/RouterProvider>\n }\n\n return {\n ...render(ui, { wrapper: Wrapper, ...renderOptions }),\n router,\n }\n}\n\n// Helper to test specific file routes\nexport function createMockFileRoute(\n path: string,\n component: React.ComponentType,\n) {\n // This is useful for isolated testing when you don't want to use the full route tree\n return {\n path,\n component,\n // Add other common route properties as needed\n }\n}\n```\n\n---\n\n## Testing File-Based Route Structure\n\n### 1. Test Route Tree Generation\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { routeTree } from '../routeTree.gen'\n\ndescribe('Generated Route Tree', () => {\n it('should generate route tree from file structure', () => {\n // Test that route tree exists and has expected structure\n expect(routeTree).toBeDefined()\n expect(routeTree.children).toBeDefined()\n })\n\n it('should include all expected routes', () => {\n // Get all route paths from the generated tree\n const getAllRoutePaths = (tree: any, paths: string[] = []): string[] => {\n if (tree.path) {\n paths.push(tree.path)\n }\n if (tree.children) {\n tree.children.forEach((child: any) => {\n getAllRoutePaths(child, paths)\n })\n }\n return paths\n }\n\n const routePaths = getAllRoutePaths(routeTree)\n\n // Test that expected routes are present\n expect(routePaths).toContain('/')\n expect(routePaths).toContain('/about')\n // Add assertions for your specific routes\n })\n\n it('should have correct route hierarchy', () => {\n // Test parent-child relationships\n const homeRoute = routeTree.children?.find(\n (child: any) => child.path === '/',\n )\n expect(homeRoute).toBeDefined()\n\n // Test for specific route structure based on your file organization\n // For example, if you have /posts/$postId routes:\n // const postsRoute = routeTree.children?.find((child: any) => child.path === '/posts')\n // expect(postsRoute?.children).toBeDefined()\n })\n})\n```\n\n### 2. Test File Route Conventions\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('File Route Conventions', () => {\n it('should render index route at root path', () => {\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/',\n })\n\n // Test that the index route component renders\n // This depends on what your src/routes/index.tsx exports\n expect(screen.getByText('Welcome Home!')).toBeInTheDocument()\n })\n\n it('should handle route parameters from filename', () => {\n // If you have a route like src/routes/posts/$postId.tsx\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts/123',\n })\n\n // Test that parameter is correctly parsed from file-based route\n expect(screen.getByText(/Post.*123/)).toBeInTheDocument()\n })\n\n it('should handle nested routes from directory structure', () => {\n // If you have src/routes/dashboard/settings.tsx\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/dashboard/settings',\n })\n\n expect(screen.getByText(/Settings/)).toBeInTheDocument()\n })\n\n it('should handle layout routes', () => {\n // If you have src/routes/_layout.tsx\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/some-nested-route',\n })\n\n // Test that layout is rendered for nested routes\n expect(screen.getByTestId('layout-header')).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## Testing File-Based Route Components\n\n### 1. Test Individual Route Files\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { createFileRoute } from '@tanstack/react-router'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('Individual Route Components', () => {\n it('should test about route component', () => {\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/about',\n })\n\n expect(screen.getByText('About')).toBeInTheDocument()\n })\n\n it('should test route with loader data', () => {\n // For a route like src/routes/posts/index.tsx with loader\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts',\n })\n\n // Wait for loader data to load\n expect(screen.getByText(/Posts List/)).toBeInTheDocument()\n })\n\n it('should test route with search params validation', () => {\n // For a route with validateSearch in src/routes/search.tsx\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/search?q=react&page=1',\n })\n\n expect(screen.getByDisplayValue('react')).toBeInTheDocument()\n })\n})\n```\n\n### 2. Test Route-Specific Hooks\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('Route-Specific Hooks', () => {\n it('should test useParams in parameterized route', () => {\n // Create a test component that uses Route.useParams()\n function TestComponent() {\n // This would be available in the actual route component\n const params = Route.useParams()\n return \u003cdiv data-testid=\"param-value\">{params.postId}\u003c/div>\n }\n\n renderWithFileRoutes(\u003cTestComponent />, {\n initialLocation: '/posts/abc123',\n })\n\n expect(screen.getByTestId('param-value')).toHaveTextContent('abc123')\n })\n\n it('should test useLoaderData in route with loader', () => {\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts/123',\n })\n\n // Test that loader data is available in the component\n expect(screen.getByText(/Post Title/)).toBeInTheDocument()\n })\n\n it('should test useSearch in route with search validation', () => {\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/search?q=typescript&sort=date',\n })\n\n // Test that search params are correctly parsed\n expect(screen.getByDisplayValue('typescript')).toBeInTheDocument()\n expect(screen.getByText(/sorted by date/)).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## Testing Route Navigation with File-Based Routes\n\n### 1. Test Link Navigation\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('File-Based Route Navigation', () => {\n it('should navigate between file-based routes', async () => {\n const user = userEvent.setup()\n\n const { router } = renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/',\n })\n\n // Initial state - should be on home route\n expect(screen.getByText('Welcome Home!')).toBeInTheDocument()\n expect(router.state.location.pathname).toBe('/')\n\n // Click navigation link\n await user.click(screen.getByRole('link', { name: /about/i }))\n\n // Should navigate to about route\n expect(screen.getByText('About')).toBeInTheDocument()\n expect(router.state.location.pathname).toBe('/about')\n })\n\n it('should handle dynamic route navigation', async () => {\n const user = userEvent.setup()\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts',\n })\n\n // Click on a post link (assuming your posts route renders links)\n await user.click(screen.getByRole('link', { name: /View Post 1/i }))\n\n // Should navigate to dynamic post route\n expect(screen.getByText(/Post 1 Details/)).toBeInTheDocument()\n })\n\n it('should handle nested route navigation', async () => {\n const user = userEvent.setup()\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/dashboard',\n })\n\n // Navigate to nested route\n await user.click(screen.getByRole('link', { name: /settings/i }))\n\n expect(screen.getByText(/Dashboard Settings/)).toBeInTheDocument()\n })\n})\n```\n\n### 2. Test Programmatic Navigation\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('Programmatic Navigation', () => {\n it('should programmatically navigate between file routes', async () => {\n const user = userEvent.setup()\n\n const { router } = renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/',\n })\n\n // Trigger programmatic navigation (button in your component)\n await user.click(screen.getByRole('button', { name: /Go to Posts/i }))\n\n expect(router.state.location.pathname).toBe('/posts')\n })\n\n it('should navigate with search params', async () => {\n const user = userEvent.setup()\n\n const { router } = renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/search',\n })\n\n // Trigger search with params\n await user.type(screen.getByRole('textbox'), 'test query')\n await user.click(screen.getByRole('button', { name: /search/i }))\n\n expect(router.state.location.search).toMatchObject({\n q: 'test query',\n })\n })\n})\n```\n\n---\n\n## Testing File-Based Route Guards and Loaders\n\n### 1. Test Route Guards\n\n```tsx\nimport { describe, it, expect, vi } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('File-Based Route Guards', () => {\n it('should redirect unauthenticated users from protected routes', () => {\n // Mock unauthenticated state\n const mockAuth = { isAuthenticated: false, user: null }\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/dashboard',\n routerContext: { auth: mockAuth },\n })\n\n // Should redirect to login (based on your beforeLoad implementation)\n expect(screen.getByText(/Please log in/)).toBeInTheDocument()\n })\n\n it('should allow authenticated users to access protected routes', () => {\n const mockAuth = {\n isAuthenticated: true,\n user: { id: '1', name: 'John' },\n }\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/dashboard',\n routerContext: { auth: mockAuth },\n })\n\n expect(screen.getByText(/Welcome to Dashboard/)).toBeInTheDocument()\n })\n})\n```\n\n### 2. Test Route Loaders\n\n```tsx\nimport { describe, it, expect, vi } from 'vitest'\nimport { screen, waitFor } from '@testing-library/react'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('File-Based Route Loaders', () => {\n it('should load data for route with loader', async () => {\n // Mock the API function used in your route loader\n const mockFetchPost = vi.fn().mockResolvedValue({\n id: '123',\n title: 'Test Post',\n content: 'Test content',\n })\n\n // If your route loader uses a global API function, mock it\n vi.mock('../api/posts', () => ({\n fetchPost: mockFetchPost,\n }))\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts/123',\n })\n\n await waitFor(() => {\n expect(screen.getByText('Test Post')).toBeInTheDocument()\n })\n\n expect(mockFetchPost).toHaveBeenCalledWith('123')\n })\n\n it('should handle loader errors', async () => {\n const mockFetchPost = vi.fn().mockRejectedValue(new Error('Post not found'))\n\n vi.mock('../api/posts', () => ({\n fetchPost: mockFetchPost,\n }))\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts/invalid',\n })\n\n await waitFor(() => {\n expect(screen.getByText(/Error.*Post not found/)).toBeInTheDocument()\n })\n })\n})\n```\n\n---\n\n## Testing File Route Validation\n\n### 1. Test Search Parameter Validation\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('File Route Validation', () => {\n it('should validate search parameters', () => {\n // Test with valid search params\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/search?q=react&page=1&sort=date',\n })\n\n expect(screen.getByDisplayValue('react')).toBeInTheDocument()\n expect(screen.getByText(/Page 1/)).toBeInTheDocument()\n })\n\n it('should handle invalid search parameters', () => {\n // Test with invalid search params (e.g., invalid page number)\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/search?page=invalid&sort=unknown',\n })\n\n // Should fall back to defaults based on your validation schema\n expect(screen.getByText(/Page 1/)).toBeInTheDocument() // default page\n })\n\n it('should validate route parameters', () => {\n // Test with valid route param\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts/123',\n })\n\n expect(screen.getByText(/Post 123/)).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## Testing File Route Error Boundaries\n\n### 1. Test Route-Level Error Handling\n\n```tsx\nimport { describe, it, expect, vi } from 'vitest'\nimport { screen } from '@testing-library/react'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('File Route Error Handling', () => {\n it('should handle component errors with error boundary', () => {\n // Mock console.error to avoid noise in test output\n const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n // Force an error in a route component\n vi.mock('../routes/error-prone.tsx', () => ({\n Route: {\n component: () => {\n throw new Error('Test error')\n },\n },\n }))\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/error-prone',\n })\n\n expect(screen.getByText(/Something went wrong/)).toBeInTheDocument()\n\n consoleSpy.mockRestore()\n })\n\n it('should handle loader errors with error component', async () => {\n const mockFailingLoader = vi\n .fn()\n .mockRejectedValue(new Error('Load failed'))\n\n vi.mock('../api/data', () => ({\n loadData: mockFailingLoader,\n }))\n\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/data-route',\n })\n\n expect(screen.getByText(/Failed to load data/)).toBeInTheDocument()\n })\n})\n```\n\n---\n\n## Testing with Generated Route Types\n\n### 1. Test Type Safety\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { useNavigate } from '@tanstack/react-router'\nimport { renderWithFileRoutes } from '../test/file-route-utils'\n\ndescribe('Generated Route Types', () => {\n it('should provide type-safe navigation', () => {\n function TestComponent() {\n const navigate = useNavigate()\n\n const handleNavigate = () => {\n // This should be type-safe based on your generated routes\n navigate({\n to: '/posts/$postId',\n params: { postId: '123' },\n search: { tab: 'comments' },\n })\n }\n\n return (\n \u003cbutton onClick={handleNavigate} data-testid=\"navigate-btn\">\n Navigate\n \u003c/button>\n )\n }\n\n const { router } = renderWithFileRoutes(\u003cTestComponent />, {\n initialLocation: '/',\n })\n\n // Test the navigation works correctly\n const button = screen.getByTestId('navigate-btn')\n fireEvent.click(button)\n\n expect(router.state.location.pathname).toBe('/posts/123')\n expect(router.state.location.search).toEqual({ tab: 'comments' })\n })\n})\n```\n\n---\n\n## Testing Route Tree Changes\n\n### 1. Test Route Generation During Development\n\n```tsx\nimport { describe, it, expect } from 'vitest'\nimport { routeTree } from '../routeTree.gen'\n\ndescribe('Route Tree Development', () => {\n it('should regenerate routes when files change', () => {\n // This test ensures your route tree is properly generated\n // You can add specific assertions based on your file structure\n\n expect(routeTree).toBeDefined()\n expect(typeof routeTree.children).toBe('object')\n\n // Test specific routes exist\n const routes = getAllRouteIds(routeTree)\n expect(routes).toContain('/')\n expect(routes).toContain('/about')\n // Add assertions for your specific routes\n })\n\n // Helper function to get all route IDs from tree\n function getAllRouteIds(tree: any, ids: string[] = []): string[] {\n if (tree.id) {\n ids.push(tree.id)\n }\n if (tree.children) {\n Object.values(tree.children).forEach((child: any) => {\n getAllRouteIds(child, ids)\n })\n }\n return ids\n }\n})\n```\n\n---\n\n## E2E Testing for File-Based Routes\n\n### 1. Playwright Configuration for File-Based Routes\n\nCreate `e2e/file-routing.spec.ts`:\n\n```ts\nimport { test, expect } from '@playwright/test'\n\ntest.describe('File-Based Route E2E', () => {\n test('should navigate through file-based route structure', async ({\n page,\n }) => {\n await page.goto('/')\n\n // Test home route (from src/routes/index.tsx)\n await expect(page.locator('h3')).toContainText('Welcome Home!')\n\n // Navigate to about route (from src/routes/about.tsx)\n await page.click('text=About')\n await expect(page).toHaveURL('/about')\n await expect(page.locator('h3')).toContainText('About')\n\n // Test browser navigation\n await page.goBack()\n await expect(page).toHaveURL('/')\n })\n\n test('should handle dynamic routes from file structure', async ({ page }) => {\n await page.goto('/posts')\n\n // Click on a dynamic post link (from src/routes/posts/$postId.tsx)\n await page.click('[data-testid=\"post-link-1\"]')\n await expect(page).toHaveURL('/posts/1')\n await expect(page.locator('h1')).toContainText('Post 1')\n })\n\n test('should handle nested routes', async ({ page }) => {\n await page.goto('/dashboard')\n\n // Navigate to nested route (from src/routes/dashboard/settings.tsx)\n await page.click('text=Settings')\n await expect(page).toHaveURL('/dashboard/settings')\n await expect(page.locator('h2')).toContainText('Settings')\n })\n})\n```\n\n---\n\n## Common File-Based Routing Testing Patterns\n\n### 1. Mock Route Files for Testing\n\n```tsx\n// src/test/mock-file-routes.tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\n// Mock individual route for isolated testing\nexport const createMockFileRoute = (\n path: string,\n component: React.ComponentType,\n options: any = {},\n) => {\n return createFileRoute(path)({\n component,\n ...options,\n })\n}\n\n// Common test route components\nexport const TestHomeRoute = createMockFileRoute('/', () => (\n \u003cdiv data-testid=\"home\">Home Page\u003c/div>\n))\n\nexport const TestAboutRoute = createMockFileRoute('/about', () => (\n \u003cdiv data-testid=\"about\">About Page\u003c/div>\n))\n\nexport const TestDynamicRoute = createMockFileRoute('/posts/$postId', () => {\n const { postId } = Route.useParams()\n return \u003cdiv data-testid=\"post\">Post {postId}\u003c/div>\n})\n```\n\n### 2. Test Route Discovery\n\n```tsx\nimport { describe, it, expect } from 'vitest'\n\ndescribe('Route Discovery', () => {\n it('should discover all routes from file structure', () => {\n // Test that your route tree includes all expected routes\n // This helps catch when routes are accidentally not being generated\n\n const expectedRoutes = [\n '/',\n '/about',\n '/posts',\n '/posts/$postId',\n '/dashboard',\n '/dashboard/settings',\n ]\n\n expectedRoutes.forEach((routePath) => {\n const routeExists = checkRouteExists(routeTree, routePath)\n expect(routeExists).toBe(true)\n })\n })\n})\n\nfunction checkRouteExists(tree: any, path: string): boolean {\n // Implementation to check if route exists in tree\n // This depends on your route tree structure\n return true // Simplified\n}\n```\n\n---\n\n## Best Practices for File-Based Route Testing\n\n### 1. Test Organization\n\n```\nsrc/\n├── routes/\n│ ├── __root.tsx\n│ ├── index.tsx\n│ ├── about.tsx\n│ ├── posts/\n│ │ ├── index.tsx\n│ │ └── $postId.tsx\n├── test/\n│ ├── setup.ts\n│ ├── file-route-utils.tsx\n│ └── routes/\n│ ├── index.test.tsx\n│ ├── about.test.tsx\n│ └── posts/\n│ ├── index.test.tsx\n│ └── $postId.test.tsx\n```\n\n### 2. Common Test Patterns\n\n```tsx\n// Test file for each route file\ndescribe('Posts Route (/posts)', () => {\n it('should render posts list', () => {\n renderWithFileRoutes(\u003cdiv />, {\n initialLocation: '/posts',\n })\n\n expect(screen.getByText(/Posts/)).toBeInTheDocument()\n })\n\n it('should handle loading state', () => {\n // Test pending state for route with loader\n })\n\n it('should handle error state', () => {\n // Test error handling for route\n })\n})\n\n// Test route groups\ndescribe('Dashboard Routes', () => {\n describe('/dashboard', () => {\n // Dashboard index tests\n })\n\n describe('/dashboard/settings', () => {\n // Settings route tests\n })\n})\n```\n\n---\n\n## Troubleshooting File-Based Route Testing\n\n### Common Issues\n\n**Problem**: Route tree not found in tests\n\n```bash\nError: Cannot find module '../routeTree.gen'\n```\n\n**Solution**: Ensure route tree generation in test setup:\n\n```ts\n// vitest.config.ts\nexport default defineConfig({\n plugins: [\n tanstackRouter(), // Ensure this runs before tests\n react(),\n ],\n test: {\n setupFiles: ['./src/test/setup.ts'],\n },\n})\n```\n\n**Problem**: Routes not updating in tests after file changes\n\n**Solution**: Clear module cache in test setup:\n\n```ts\n// src/test/setup.ts\nbeforeEach(() => {\n vi.clearAllMocks()\n // Clear route tree cache if needed\n delete require.cache[require.resolve('../routeTree.gen')]\n})\n```\n\n**Problem**: Type errors in tests with generated routes\n\n**Solution**: Ensure proper TypeScript configuration:\n\n```json\n{\n \"compilerOptions\": {\n \"types\": [\"vitest/globals\", \"@testing-library/jest-dom\"],\n \"moduleResolution\": \"bundler\"\n },\n \"include\": [\"src/**/*\", \"src/routeTree.gen.ts\"]\n}\n```\n\n---\n\n## Next Steps\n\nAfter setting up file-based route testing, you might want to:\n\n- [How to Set Up Testing with Code-Based Routing](./setup-testing.md) - Testing patterns for manually defined routes\n- [How to Debug Router Issues](./debug-router-issues.md) - Debug file-based routing issues\n- [File-Based Routing Guide](../routing/file-based-routing.md) - Learn more about file-based routing\n\n## Related Resources\n\n- [TanStack Router File-Based Routing](../routing/file-based-routing.md) - Complete file-based routing guide\n- [File Naming Conventions](../routing/file-naming-conventions.md) - Understanding file structure\n- Testing Library - Component testing utilities\n- Vitest - Testing framework documentation\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":25558,"content_sha256":"157e418659aca5eb23b85681a538cda8bbf2a370aad4ed17ab255b0468bb831a"},{"filename":"references/docs/router/how-to/use-environment-variables.md","content":"---\ntitle: How to Use Environment Variables\n---\n\nLearn how to configure and use environment variables in your TanStack Router application for API endpoints, feature flags, and build configuration across different bundlers.\n\n## Quick Start\n\nEnvironment variables in TanStack Router are primarily used for client-side configuration and must follow bundler-specific naming conventions for security.\n\n```bash\n# .env\nVITE_API_URL=https://api.example.com\nVITE_ENABLE_DEVTOOLS=true\n```\n\n```typescript\n// Route configuration\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts')({\n loader: async () => {\n const apiUrl = import.meta.env.VITE_API_URL\n const response = await fetch(`${apiUrl}/posts`)\n return response.json()\n },\n component: PostsList,\n})\n```\n\n## Environment Variable Access Patterns\n\n### Vite-Based Projects (Most Common)\n\nWith Vite, environment variables must be prefixed with `VITE_` to be accessible in client code:\n\n```typescript\n// Route loaders\nexport const Route = createFileRoute('/dashboard')({\n loader: async () => {\n const apiUrl = import.meta.env.VITE_API_URL // ✅ Works\n const apiKey = import.meta.env.VITE_PUBLIC_API_KEY // ✅ Works\n\n // This would be undefined (security feature):\n // const secret = import.meta.env.SECRET_KEY // ❌ Undefined\n\n return fetchDashboardData(apiUrl, apiKey)\n },\n})\n\n// Components\nexport function ApiStatus() {\n const isDev = import.meta.env.DEV // ✅ Built-in Vite variable\n const isProd = import.meta.env.PROD // ✅ Built-in Vite variable\n const mode = import.meta.env.MODE // ✅ development/production\n\n return (\n \u003cdiv>\n Environment: {mode}\n {isDev && \u003cDevToolsPanel />}\n \u003c/div>\n )\n}\n```\n\n### Webpack-Based Projects\n\nConfigure webpack's DefinePlugin to inject environment variables. **Note:** Webpack doesn't support `import.meta.env` by default, so use `process.env` patterns:\n\n```typescript\n// webpack.config.js\nconst webpack = require('webpack')\n\nmodule.exports = {\n plugins: [\n new webpack.DefinePlugin({\n 'process.env.API_URL': JSON.stringify(process.env.API_URL),\n 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),\n 'process.env.ENABLE_FEATURE': JSON.stringify(process.env.ENABLE_FEATURE),\n }),\n ],\n}\n\n// Usage in routes\nexport const Route = createFileRoute('/api-data')({\n loader: async () => {\n const response = await fetch(`${process.env.API_URL}/data`)\n return response.json()\n },\n component: () => {\n const enableFeature = process.env.ENABLE_FEATURE === 'true'\n return enableFeature ? \u003cNewFeature /> : \u003cOldFeature />\n },\n})\n```\n\n### Rspack-Based Projects\n\nRspack uses the `PUBLIC_` prefix convention. **Note:** `import.meta.env` support depends on your Rspack configuration and runtime - you may need to configure `builtins.define` properly:\n\n```bash\n# .env\nPUBLIC_API_URL=https://api.example.com\nPUBLIC_FEATURE_FLAG=true\n```\n\n```typescript\n// Route usage\nexport const Route = createFileRoute('/features')({\n loader: async () => {\n const apiUrl = import.meta.env.PUBLIC_API_URL\n return fetch(`${apiUrl}/features`).then(r => r.json())\n },\n component: () => {\n const enableFeature = import.meta.env.PUBLIC_FEATURE_FLAG === 'true'\n return enableFeature ? \u003cNewFeature /> : \u003cOldFeature />\n },\n})\n```\n\n### ESBuild Projects\n\nConfigure defines manually:\n\n```typescript\n// build script\nimport { build } from 'esbuild'\n\nawait build({\n entryPoints: ['src/main.tsx'],\n define: {\n 'process.env.NODE_ENV': '\"production\"',\n 'process.env.API_URL': `\"${process.env.API_URL}\"`,\n },\n})\n```\n\n## Common Patterns\n\n### API Configuration in Route Loaders\n\n```typescript\n// src/routes/posts/index.tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nconst fetchPosts = async () => {\n const baseUrl = import.meta.env.VITE_API_URL\n const apiKey = import.meta.env.VITE_API_KEY\n\n const response = await fetch(`${baseUrl}/posts`, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n throw new Error('Failed to fetch posts')\n }\n\n return response.json()\n}\n\nexport const Route = createFileRoute('/posts/')({\n loader: fetchPosts,\n errorComponent: ({ error }) => (\n \u003cdiv>Error loading posts: {error.message}\u003c/div>\n ),\n})\n```\n\n### Environment-Based Route Configuration\n\n```typescript\n// src/routes/__root.tsx\nimport { createRootRoute, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nexport const Route = createRootRoute({\n component: () => (\n \u003c>\n \u003cOutlet />\n {/* Only show devtools in development */}\n {import.meta.env.DEV && \u003cTanStackRouterDevtools />}\n \u003c/>\n ),\n})\n```\n\n### Feature Flags in Routes\n\n```typescript\n// src/lib/features.ts\nexport const features = {\n enableNewDashboard: import.meta.env.VITE_ENABLE_NEW_DASHBOARD === 'true',\n enableAnalytics: import.meta.env.VITE_ENABLE_ANALYTICS === 'true',\n debugMode: import.meta.env.DEV,\n}\n\n// src/routes/dashboard/index.tsx\nimport { createFileRoute, redirect } from '@tanstack/react-router'\nimport { features } from '../../lib/features'\n\nexport const Route = createFileRoute('/dashboard/')({\n beforeLoad: () => {\n // Redirect to old dashboard if new one is disabled\n if (!features.enableNewDashboard) {\n throw redirect({ to: '/dashboard/legacy' })\n }\n },\n component: NewDashboard,\n})\n```\n\n### Authentication Configuration\n\n```typescript\n// src/lib/auth.ts\nexport const authConfig = {\n domain: import.meta.env.VITE_AUTH0_DOMAIN,\n clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,\n redirectUri: `${window.location.origin}/callback`,\n}\n\n// src/routes/_authenticated.tsx\nimport { createFileRoute, redirect } from '@tanstack/react-router'\nimport { authConfig } from '../lib/auth'\n\nexport const Route = createFileRoute('/_authenticated')({\n beforeLoad: async ({ location }) => {\n const isAuthenticated = await checkAuthStatus()\n\n if (!isAuthenticated) {\n // Redirect to auth provider\n const authUrl = `https://${authConfig.domain}/authorize?client_id=${authConfig.clientId}&redirect_uri=${authConfig.redirectUri}`\n window.location.href = authUrl\n return\n }\n },\n})\n```\n\n### Search Params with Environment Config\n\n```typescript\n// src/routes/search.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { z } from 'zod'\n\nconst searchSchema = z.object({\n q: z.string().optional(),\n category: z.string().optional(),\n})\n\nexport const Route = createFileRoute('/search')({\n validateSearch: searchSchema,\n loader: async ({ search }) => {\n const apiUrl = import.meta.env.VITE_SEARCH_API_URL\n const params = new URLSearchParams({\n q: search.q || '',\n category: search.category || 'all',\n api_key: import.meta.env.VITE_SEARCH_API_KEY,\n })\n\n const response = await fetch(`${apiUrl}/search?${params}`)\n return response.json()\n },\n})\n```\n\n## Environment File Setup\n\n### File Hierarchy (Vite)\n\nVite loads environment files in this order:\n\n```\n.env.local # Local overrides (add to .gitignore)\n.env.production # Production-specific\n.env.development # Development-specific\n.env # Default (commit to git)\n```\n\n### Example Configuration\n\n**.env** (committed to repository):\n\n```bash\n# API Configuration\nVITE_API_URL=https://api.example.com\nVITE_API_VERSION=v1\n\n# Feature Flags\nVITE_ENABLE_NEW_UI=false\nVITE_ENABLE_ANALYTICS=true\n\n# Auth Configuration (public keys only)\nVITE_AUTH0_DOMAIN=your-domain.auth0.com\nVITE_AUTH0_CLIENT_ID=your-client-id\n\n# Build Configuration\nVITE_APP_NAME=TanStack Router App\nVITE_APP_VERSION=1.0.0\n```\n\n**.env.local** (add to .gitignore):\n\n```bash\n# Development overrides\nVITE_API_URL=http://localhost:3001\nVITE_ENABLE_NEW_UI=true\nVITE_DEBUG_MODE=true\n```\n\n**.env.production**:\n\n```bash\n# Production-specific\nVITE_API_URL=https://api.prod.example.com\nVITE_ENABLE_ANALYTICS=true\nVITE_ENABLE_NEW_UI=true\n```\n\n## Type Safety\n\n### Vite TypeScript Declarations\n\nCreate `src/vite-env.d.ts`:\n\n```typescript\n/// \u003creference types=\"vite/client\" />\n\ninterface ImportMetaEnv {\n // API Configuration\n readonly VITE_API_URL: string\n readonly VITE_API_VERSION: string\n readonly VITE_API_KEY?: string\n\n // Feature Flags\n readonly VITE_ENABLE_NEW_UI: string\n readonly VITE_ENABLE_ANALYTICS: string\n readonly VITE_DEBUG_MODE?: string\n\n // Authentication\n readonly VITE_AUTH0_DOMAIN: string\n readonly VITE_AUTH0_CLIENT_ID: string\n\n // App Configuration\n readonly VITE_APP_NAME: string\n readonly VITE_APP_VERSION: string\n}\n\ninterface ImportMeta {\n readonly env: ImportMetaEnv\n}\n```\n\n### Runtime Validation\n\nUse Zod to validate environment variables at startup with fallbacks and optional values:\n\n```typescript\n// src/config/env.ts\nimport { z } from 'zod'\n\nconst envSchema = z.object({\n // Required variables\n VITE_API_URL: z.string().url(),\n VITE_AUTH0_DOMAIN: z.string(),\n VITE_AUTH0_CLIENT_ID: z.string(),\n VITE_APP_NAME: z.string(),\n\n // Optional with defaults\n VITE_API_VERSION: z.string().default('v1'),\n VITE_ENABLE_NEW_UI: z.string().default('false'),\n VITE_ENABLE_ANALYTICS: z.string().default('true'),\n\n // Optional variables\n VITE_DEBUG_MODE: z.string().optional(),\n VITE_SENTRY_DSN: z.string().optional(),\n})\n\n// Validate at app startup with fallbacks\nexport const env = envSchema.parse({\n ...import.meta.env,\n // Provide fallbacks for missing optional values\n VITE_API_VERSION: import.meta.env.VITE_API_VERSION || 'v1',\n VITE_ENABLE_NEW_UI: import.meta.env.VITE_ENABLE_NEW_UI || 'false',\n VITE_ENABLE_ANALYTICS: import.meta.env.VITE_ENABLE_ANALYTICS || 'true',\n})\n\n// Typed helper functions\nexport const isFeatureEnabled = (flag: keyof typeof env) => {\n return env[flag] === 'true'\n}\n\n// Type-safe boolean conversion\nexport const getBooleanEnv = (\n value: string | undefined,\n defaultValue = false,\n): boolean => {\n if (value === undefined) return defaultValue\n return value === 'true'\n}\n```\n\n### Usage with Type Safety\n\n```typescript\n// src/routes/api-data.tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { env, isFeatureEnabled } from '../config/env'\n\nexport const Route = createFileRoute('/api-data')({\n loader: async () => {\n // TypeScript knows these are strings and exist\n const response = await fetch(`${env.VITE_API_URL}/${env.VITE_API_VERSION}/data`)\n return response.json()\n },\n component: () => {\n return (\n \u003cdiv>\n \u003ch1>{env.VITE_APP_NAME}\u003c/h1>\n {isFeatureEnabled('VITE_ENABLE_NEW_UI') && \u003cNewUIComponent />}\n \u003c/div>\n )\n },\n})\n```\n\n## Bundler-Specific Configuration\n\n### Vite Configuration\n\n```typescript\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 react(),\n // tanstackRouter generates route tree and enables file-based routing\n tanstackRouter(),\n ],\n // Environment variables are handled automatically\n // Custom environment variable handling:\n define: {\n // Global constants (these become available as global variables)\n __APP_VERSION__: JSON.stringify(process.env.npm_package_version),\n },\n})\n```\n\n### Webpack Configuration\n\n```typescript\n// webpack.config.js\nconst { TanStackRouterWebpack } = require('@tanstack/router-webpack-plugin')\nconst webpack = require('webpack')\n\nmodule.exports = {\n plugins: [\n // TanStackRouterWebpack generates route tree and enables file-based routing\n new TanStackRouterWebpack(),\n new webpack.DefinePlugin({\n // Inject environment variables (use process.env for Webpack)\n 'process.env.API_URL': JSON.stringify(process.env.API_URL),\n 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),\n 'process.env.ENABLE_FEATURE': JSON.stringify(process.env.ENABLE_FEATURE),\n }),\n ],\n}\n```\n\n### Rspack Configuration\n\n```typescript\n// rspack.config.js\nconst { TanStackRouterRspack } = require('@tanstack/router-rspack-plugin')\n\nmodule.exports = {\n plugins: [\n // TanStackRouterRspack generates route tree and enables file-based routing\n new TanStackRouterRspack(),\n ],\n // Rspack automatically handles PUBLIC_ prefixed variables for import.meta.env\n // Custom handling for additional variables:\n builtins: {\n define: {\n // Define additional variables (these become global replacements)\n 'process.env.API_URL': JSON.stringify(process.env.PUBLIC_API_URL),\n __BUILD_TIME__: JSON.stringify(new Date().toISOString()),\n },\n },\n}\n```\n\n## Production Checklist\n\n- [ ] All client-exposed variables use appropriate prefix (`VITE_`, `PUBLIC_`, etc.)\n- [ ] No sensitive data (API secrets, private keys) in environment variables\n- [ ] `.env.local` is in `.gitignore`\n- [ ] Production environment variables are configured on hosting platform\n- [ ] Required environment variables are validated at build time\n- [ ] TypeScript declarations are up to date\n- [ ] Feature flags are properly configured for production\n- [ ] API URLs point to production endpoints\n\n## Common Problems\n\n### Environment Variable is Undefined\n\n**Problem**: `import.meta.env.MY_VARIABLE` returns `undefined`\n\n**Solutions**:\n\n1. **Add correct prefix**: Use `VITE_` for Vite, `PUBLIC_` for Rspack.\n Vite's default prefix may be changed in the config:\n ```ts\n // vite.config.ts\n export const config = {\n // ...rest of your config\n envPrefix: 'MYPREFIX_', // this means `MYPREFIX_MY_VARIABLE` is the new correct way\n }\n ```\n2. **Restart development server** after adding new variables\n3. **Check file location**: `.env` file must be in project root\n4. **Verify bundler configuration**: Ensure variables are properly injected\n5. **Verify variable**:\n\n- **In dev**: is in correct `.env` file or environment\n- **For prod**: is in correct `.env` file or current environment **_at bundle time_**. That's right, `VITE_`/`PUBLIC_`-prefixed variables are replaced in a macro-like fashion at bundle time, and will _never_ be read at runtime on your server. This is a common mistake, so make sure this is not your case.\n\n**Example**:\n\n```bash\n# ❌ Won't work (no prefix)\nAPI_KEY=abc123\n\n# ✅ Works with Vite\nVITE_API_KEY=abc123\n\n# ✅ Works with Rspack\nPUBLIC_API_KEY=abc123\n\n# ❌ Won't bundle the variable (assuming it is not set in the environment of the build)\nnpm run build\n\n# ✅ Works with Vite and will bundle the variable for production\nVITE_API_KEY=abc123 npm run build\n\n# ✅ Works with Rspack and will bundle the variable for production\nPUBLIC_API_KEY=abc123 npm run build\n```\n\n### Runtime Client Environment Variables at Runtime in Production\n\n**Problem**: If `VITE_`/`PUBLIC_` variables are replaced at bundle time only, how to make runtime variables available on the client ?\n\n**Solutions**:\n\nPass variables from the server down to the client:\n\n1. Add your variable to the correct `env.` file\n2. Create an endpoint on your server to read the value from the client\n\n**Example**:\n\nYou may use your prefered backend framework/libray, but here it is using Tanstack Start server functions:\n\n```tsx\nconst getRuntimeVar = createServerFn({ method: 'GET' }).handler(() => {\n return process.env.MY_RUNTIME_VAR // notice `process.env` on the server, and no `VITE_`/`PUBLIC_` prefix\n})\n\nexport const Route = createFileRoute('/')({\n loader: async () => {\n const foo = await getRuntimeVar()\n return { foo }\n },\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { foo } = Route.useLoaderData()\n // ... use your variable however you want\n}\n```\n\n### Variable Not Updating\n\n**Problem**: Environment variable changes aren't reflected in app\n\n**Solutions**:\n\n1. **Restart development server** - Required for new variables\n2. **Check file hierarchy** - `.env.local` overrides `.env`\n3. **Clear browser cache** - Hard refresh (Ctrl+Shift+R)\n4. **Verify correct file** - Make sure you're editing the right `.env` file\n\n### TypeScript Errors\n\n**Problem**: `Property 'VITE_MY_VAR' does not exist on type 'ImportMetaEnv'`\n\n**Solution**: Add declaration to `src/vite-env.d.ts`:\n\n```typescript\ninterface ImportMetaEnv {\n readonly VITE_MY_VAR: string\n}\n```\n\n### Build Errors\n\n**Problem**: Missing environment variables during build\n\n**Solutions**:\n\n1. **Configure CI/CD**: Set variables in build environment\n2. **Add validation**: Check required variables at build time\n3. **Use .env files**: Ensure production `.env` files exist\n4. **Check bundler config**: Verify environment variable injection\n\n### Security Issues\n\n**Problem**: Accidentally exposing sensitive data\n\n**Solutions**:\n\n1. **Never use secrets in client variables** - They're visible in browser\n2. **Use server-side proxies** for sensitive API calls\n3. **Audit bundle** - Check built files for leaked secrets\n4. **Follow naming conventions** - Only prefixed variables are exposed\n\n### Runtime vs Build-time Confusion\n\n**Problem**: Variables not available at runtime\n\n**Solutions**:\n\n1. **Understand static replacement** - Variables are replaced at build time\n2. **Use server-side for dynamic values** - Use APIs for runtime configuration\n3. **Validate at startup** - Check all required variables exist\n\n### Environment Variables are Always Strings\n\n**Problem**: Unexpected behavior when comparing boolean or numeric values\n\n**Solutions**:\n\n1. **Always compare as strings**: Use `=== 'true'` not `=== true`\n2. **Convert explicitly**: Use `parseInt()`, `parseFloat()`, or `Boolean()`\n3. **Use helper functions**: Create typed conversion utilities\n\n**Example**:\n\n```typescript\n// ❌ Won't work as expected\nconst isEnabled = import.meta.env.VITE_FEATURE_ENABLED // This is a string!\nif (isEnabled) {\n /* Always true if variable exists */\n}\n\n// ✅ Correct string comparison\nconst isEnabled = import.meta.env.VITE_FEATURE_ENABLED === 'true'\n\n// ✅ Safe numeric conversion\nconst port = parseInt(import.meta.env.VITE_PORT || '3000', 10)\n\n// ✅ Helper function approach\nconst getBooleanEnv = (value: string | undefined, defaultValue = false) => {\n if (value === undefined) return defaultValue\n return value.toLowerCase() === 'true'\n}\n```\n\n## Common Next Steps\n\n\n\n\n\n## Related Resources\n\n- [TanStack Router File-Based Routing](../routing/file-based-routing.md) - Learn about route configuration\n- Vite Environment Variables - Official Vite documentation\n- Webpack DefinePlugin - Webpack environment configuration\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18468,"content_sha256":"0f41baa97a6086f7b3a4a7d4b064aff105073a024ea2399bc74c1f839c3c832a"},{"filename":"references/docs/router/how-to/validate-search-params.md","content":"---\ntitle: Validate Search Parameters with Schemas\n---\n\nLearn how to add robust schema validation to your search parameters using popular validation libraries like Zod, Valibot, and ArkType. This guide covers validation setup, error handling, type safety, and common validation patterns for production applications.\n\n**Prerequisites:** [Set Up Basic Search Parameters](./setup-basic-search-params.md) - Foundation concepts for reading and working with search params.\n\n## Quick Start\n\nAdd robust validation with custom error messages, complex types, and production-ready error handling:\n\n```tsx\nimport { createFileRoute, useRouter } from '@tanstack/react-router'\nimport { zodValidator, fallback } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst productSearchSchema = z.object({\n query: z.string().min(1, 'Search query required'),\n category: z.enum(['electronics', 'clothing', 'books', 'home']).optional(),\n minPrice: fallback(z.number().min(0, 'Price cannot be negative'), 0),\n maxPrice: fallback(z.number().min(0, 'Price cannot be negative'), 1000),\n inStock: fallback(z.boolean(), true),\n tags: z.array(z.string()).optional(),\n dateRange: z\n .object({\n start: z.string().datetime().optional(),\n end: z.string().datetime().optional(),\n })\n .optional(),\n})\n\nexport const Route = createFileRoute('/products')({\n validateSearch: zodValidator(productSearchSchema),\n errorComponent: ({ error }) => {\n const router = useRouter()\n return (\n \u003cdiv className=\"error\">\n \u003ch2>Invalid Search Parameters\u003c/h2>\n \u003cp>{error.message}\u003c/p>\n \u003cbutton\n onClick={() => router.navigate({ to: '/products', search: {} })}\n >\n Reset Search\n \u003c/button>\n \u003c/div>\n )\n },\n component: ProductsPage,\n})\n\nfunction ProductsPage() {\n // All search params are validated, type-safe, and have fallback values applied\n const { query, category, minPrice, maxPrice, inStock, tags, dateRange } =\n Route.useSearch()\n\n return (\n \u003cdiv>\n \u003ch1>Products\u003c/h1>\n \u003cp>Search: {query}\u003c/p>\n \u003cp>Category: {category || 'All'}\u003c/p>\n \u003cp>\n Price Range: ${minPrice} - ${maxPrice}\n \u003c/p>\n \u003cp>In Stock Only: {inStock ? 'Yes' : 'No'}\u003c/p>\n {tags && \u003cp>Tags: {tags.join(', ')}\u003c/p>}\n {dateRange && (\n \u003cp>\n Date Range: {dateRange.start} to {dateRange.end}\n \u003c/p>\n )}\n \u003c/div>\n )\n}\n```\n\n## Validation Library Options\n\nTanStack Router supports multiple validation libraries through adapters:\n\n### Zod (Recommended)\n\nMost popular with excellent TypeScript integration:\n\n```tsx\nimport { zodValidator, fallback } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst searchSchema = z.object({\n query: z.string().min(1).max(100),\n page: fallback(z.number().int().positive(), 1),\n sortBy: z.enum(['name', 'date', 'relevance']).optional(),\n filters: z.array(z.string()).optional(),\n})\n\nexport const Route = createFileRoute('/search')({\n validateSearch: zodValidator(searchSchema),\n component: SearchPage,\n})\n```\n\n### Valibot\n\nLightweight alternative with modular design:\n\n```tsx\nimport { valibotValidator } from '@tanstack/valibot-adapter'\nimport * as v from 'valibot'\n\nconst searchSchema = v.object({\n query: v.pipe(v.string(), v.minLength(1), v.maxLength(100)),\n page: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(1)), 1),\n sortBy: v.optional(v.picklist(['name', 'date', 'relevance'])),\n filters: v.optional(v.array(v.string())),\n})\n\nexport const Route = createFileRoute('/search')({\n validateSearch: valibotValidator(searchSchema),\n component: SearchPage,\n})\n```\n\n### ArkType\n\nTypeScript-first with runtime validation:\n\n```tsx\nimport { type } from 'arktype'\n\nconst searchSchema = type({\n query: 'string>0&\u003c=100',\n page: 'number>0 = 1',\n 'sortBy?': \"'name'|'date'|'relevance'\",\n 'filters?': 'string[]',\n})\n\nexport const Route = createFileRoute('/search')({\n validateSearch: searchSchema,\n component: SearchPage,\n})\n```\n\n### Custom Validation Function\n\nFor complete control, implement your own validation logic:\n\n```tsx\nexport const Route = createFileRoute('/search')({\n validateSearch: (search: Record\u003cstring, unknown>) => {\n // Custom validation with detailed error handling\n const result = {\n page: 1,\n query: '',\n category: 'all',\n }\n\n // Validate page number\n const pageNum = Number(search.page)\n if (isNaN(pageNum) || pageNum \u003c 1) {\n throw new Error('Page must be a positive number')\n }\n result.page = pageNum\n\n // Validate query string\n if (typeof search.query === 'string' && search.query.length > 0) {\n if (search.query.length > 100) {\n throw new Error('Search query too long (max 100 characters)')\n }\n result.query = search.query\n }\n\n // Validate category\n const validCategories = ['electronics', 'clothing', 'books', 'all']\n if (\n typeof search.category === 'string' &&\n validCategories.includes(search.category)\n ) {\n result.category = search.category\n }\n\n return result\n },\n component: SearchPage,\n})\n```\n\n## Common Validation Patterns\n\n### Required vs Optional Parameters\n\nControl which search parameters are mandatory:\n\n```tsx\nconst validationSchema = z.object({\n // Required - will throw validation error if missing or invalid\n userId: z.number().int().positive(),\n action: z.enum(['view', 'edit', 'delete']),\n\n // Optional - can be undefined\n sortBy: z.string().optional(),\n\n // Optional with fallback - provides default if missing/invalid\n page: fallback(z.number().int().positive(), 1),\n limit: fallback(z.number().int().min(1).max(100), 20),\n})\n```\n\n### Complex Data Types\n\nHandle arrays, objects, and custom types:\n\n```tsx\nconst advancedSchema = z.object({\n // Array of strings\n tags: z.array(z.string()).optional(),\n\n // Array of numbers\n categoryIds: z.array(z.number().int()).optional(),\n\n // Date validation\n startDate: z.string().datetime().optional(),\n endDate: z.string().datetime().optional(),\n\n // Custom validation\n email: z.string().email().optional(),\n\n // Refined validation with custom logic\n priceRange: z\n .object({\n min: z.number().min(0),\n max: z.number().min(0),\n })\n .refine((data) => data.max >= data.min, {\n message: 'Max price must be greater than or equal to min price',\n })\n .optional(),\n})\n```\n\n### Input Transformation\n\nTransform and sanitize input values during validation:\n\n```tsx\nconst transformSchema = z.object({\n // Transform string to number\n page: z\n .string()\n .transform((val) => parseInt(val, 10))\n .pipe(z.number().int().positive()),\n\n // Transform and validate email\n email: z.string().toLowerCase().trim().pipe(z.string().email()).optional(),\n\n // Transform comma-separated string to array\n tags: z\n .string()\n .transform((val) => (val ? val.split(',').map((tag) => tag.trim()) : []))\n .pipe(z.array(z.string().min(1)))\n .optional(),\n})\n```\n\n## Error Handling Strategies\n\n### Basic Error Handling\n\nHandle validation errors through route error components:\n\n```tsx\nimport { createFileRoute, useRouter } from '@tanstack/react-router'\nimport { zodValidator } from '@tanstack/zod-adapter'\nimport { z } from 'zod'\n\nconst searchSchema = z.object({\n query: z.string().min(1, 'Search query is required'),\n page: z.number().int().positive('Page must be a positive number'),\n})\n\nexport const Route = createFileRoute('/search')({\n validateSearch: zodValidator(searchSchema),\n errorComponent: ({ error }) => {\n const router = useRouter()\n\n return (\n \u003cdiv className=\"error\">\n \u003ch2>Invalid Search Parameters\u003c/h2>\n \u003cp>{error.message}\u003c/p>\n \u003cbutton onClick={() => router.navigate({ to: '/search', search: {} })}>\n Reset Search\n \u003c/button>\n \u003cbutton\n onClick={() =>\n router.navigate({ to: '/search', search: { query: '', page: 1 } })\n }\n >\n Start Over\n \u003c/button>\n \u003c/div>\n )\n },\n component: SearchPage,\n})\n\nfunction SearchPage() {\n // Only called when validation succeeds\n const search = Route.useSearch()\n // ... rest of component\n}\n```\n\n### Custom Error Messages\n\nProvide user-friendly validation messages:\n\n```tsx\nconst userFriendlySchema = z.object({\n query: z\n .string()\n .min(2, 'Search query must be at least 2 characters')\n .max(100, 'Search query cannot exceed 100 characters'),\n\n page: fallback(\n z\n .number()\n .int('Page must be a whole number')\n .positive('Page must be greater than 0'),\n 1,\n ),\n\n category: z\n .enum(['electronics', 'clothing', 'books'], {\n errorMap: () => ({ message: 'Please select a valid category' }),\n })\n .optional(),\n})\n```\n\n### Validation Error Recovery\n\nImplement fallback behavior for invalid parameters:\n\n```tsx\nconst resilientSchema = z.object({\n // Use .catch() to provide fallback values on validation failure\n page: z.number().int().positive().catch(1),\n\n // Use .default() for missing values, .catch() for invalid values\n sortBy: z\n .enum(['name', 'date', 'relevance'])\n .default('relevance')\n .catch('relevance'),\n\n // Custom recovery logic\n dateRange: z\n .object({\n start: z.string().datetime(),\n end: z.string().datetime(),\n })\n .catch({\n start: new Date().toISOString(),\n end: new Date().toISOString(),\n })\n .optional(),\n})\n```\n\n## Advanced Validation Techniques\n\n### Conditional Validation\n\nApply different validation rules based on other parameters:\n\n```tsx\nconst conditionalSchema = z\n .object({\n searchType: z.enum(['basic', 'advanced']),\n query: z.string().min(1),\n })\n .and(\n z.discriminatedUnion('searchType', [\n z.object({\n searchType: z.literal('basic'),\n // Basic search requires only query\n }),\n z.object({\n searchType: z.literal('advanced'),\n // Advanced search requires additional fields\n category: z.string().min(1),\n minPrice: z.number().min(0),\n maxPrice: z.number().min(0),\n }),\n ]),\n )\n```\n\n### Schema Composition\n\nCombine and extend schemas for reusability:\n\n```tsx\n// Base pagination schema\nconst paginationSchema = z.object({\n page: fallback(z.number().int().positive(), 1),\n limit: fallback(z.number().int().min(1).max(100), 20),\n})\n\n// Base filter schema\nconst filterSchema = z.object({\n sortBy: z.enum(['name', 'date', 'relevance']).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n})\n\n// Compose schemas for different routes\nconst productSearchSchema = paginationSchema.extend({\n category: z.string().optional(),\n inStock: fallback(z.boolean(), true),\n})\n\nconst userSearchSchema = paginationSchema.merge(filterSchema).extend({\n role: z.enum(['admin', 'user', 'moderator']).optional(),\n isActive: fallback(z.boolean(), true),\n})\n```\n\n### Performance Optimization\n\nOptimize validation for better performance:\n\n```tsx\n// Pre-compile schemas for better performance\nconst compiledSchema = zodValidator(\n z.object({\n query: z.string().min(1),\n page: fallback(z.number().int().positive(), 1),\n }),\n)\n\nexport const Route = createFileRoute('/search')({\n validateSearch: compiledSchema,\n component: SearchPage,\n})\n\n// Use selective validation for expensive operations\nfunction SearchPage() {\n // Only validate specific fields when needed\n const search = Route.useSearch({\n select: (search) => ({\n query: search.query,\n page: search.page,\n }),\n })\n\n return \u003cdiv>Search Results\u003c/div>\n}\n```\n\n## Testing Search Parameter Validation\n\nFocus on testing validation behavior specific to your schemas:\n\n```tsx\nimport { render, screen, waitFor } from '@testing-library/react'\nimport {\n createRouter,\n createMemoryHistory,\n RouterProvider,\n} from '@tanstack/react-router'\n\ndescribe('Search Validation Behavior', () => {\n it('should show error component when validation fails', async () => {\n const router = createRouter({\n routeTree,\n history: createMemoryHistory({\n initialEntries: ['/search?page=invalid&query='],\n }),\n })\n\n render(\u003cRouterProvider router={router} />)\n\n await waitFor(() => {\n expect(screen.getByText('Invalid Search Parameters')).toBeInTheDocument()\n })\n })\n\n it('should apply fallback values correctly', async () => {\n const router = createRouter({\n routeTree,\n history: createMemoryHistory({\n initialEntries: ['/search?query=laptops'], // page missing\n }),\n })\n\n render(\u003cRouterProvider router={router} />)\n\n await waitFor(() => {\n expect(screen.getByText('Page: 1')).toBeInTheDocument() // Fallback applied\n })\n })\n})\n```\n\n**For comprehensive route testing patterns, see:** [Set Up Testing](./setup-testing.md) and [Test File-Based Routing](./test-file-based-routing.md)\n\n## Common Problems\n\n### Problem: Validation errors break the entire route\n\n**Symptoms:** Page fails to load when URL contains invalid search parameters.\n\n**Solution:** Use fallback values and error boundaries:\n\n```tsx\n// ❌ Wrong - will throw error and break route\nconst strictSchema = z.object({\n page: z.number().int().positive(), // No fallback\n})\n\n// ✅ Correct - provides fallback for invalid values\nconst resilientSchema = z.object({\n page: fallback(z.number().int().positive(), 1),\n})\n\n// ✅ Alternative - use errorComponent on route\nexport const Route = createFileRoute('/search')({\n validateSearch: resilientSchema,\n errorComponent: ({ error }) => \u003cSearchError error={error} />,\n component: SearchPage,\n})\n\nfunction SearchPage() {\n // Only called when validation succeeds\n const search = Route.useSearch()\n return \u003cSearchResults search={search} />\n}\n```\n\n### Problem: TypeScript errors with optional search parameters\n\n**Symptoms:** TypeScript complains about potentially undefined values.\n\n**Solution:** Use proper optional handling or fallback values:\n\n```tsx\n// ❌ Wrong - category might be undefined\nfunction FilterBar() {\n const { category } = Route.useSearch()\n return \u003cspan>{category.toUpperCase()}\u003c/span> // TypeScript error\n}\n\n// ✅ Correct - handle optional values\nfunction FilterBar() {\n const { category } = Route.useSearch()\n return \u003cspan>{category?.toUpperCase() || 'All Categories'}\u003c/span>\n}\n\n// ✅ Better - use fallback in schema\nconst schema = z.object({\n category: fallback(z.string(), 'all'),\n})\n```\n\n### Problem: Search parameter arrays not parsing correctly\n\n**Symptoms:** Array values appear as strings instead of arrays.\n\n**Solution:** Ensure proper array parsing in your schema:\n\n```tsx\n// ❌ Wrong - doesn't handle URL array format\nconst badSchema = z.object({\n tags: z.array(z.string()).optional(),\n})\n\n// ✅ Correct - parse comma-separated values or multiple params\nconst goodSchema = z.object({\n tags: z\n .union([\n z.array(z.string()), // Multiple ?tags=a&tags=b\n z.string().transform((val) => val.split(',')), // Single ?tags=a,b,c\n ])\n .optional(),\n})\n\n// ✅ Alternative - custom preprocessing\nconst preprocessedSchema = z.preprocess((val) => {\n if (typeof val === 'string') return val.split(',')\n return val\n}, z.array(z.string()).optional())\n```\n\n### Problem: Schema validation is too slow\n\n**Symptoms:** Noticeable delay when navigating with complex search parameters.\n\n**Solution:** Optimize schema complexity and use selective parsing:\n\n```tsx\n// ❌ Slow - complex validation on every navigation\nconst complexSchema = z.object({\n query: z.string().refine(async (val) => await validateQuery(val)),\n // ... many complex validations\n})\n\n// ✅ Fast - simplified validation with lazy refinement\nconst optimizedSchema = z.object({\n query: z.string().min(1), // Basic validation only\n // ... other simple validations\n})\n\n// Perform complex validation separately in component\nfunction SearchPage() {\n const search = Route.useSearch()\n\n // Complex validation only when needed\n const [complexValidation, setComplexValidation] = useState(null)\n\n useEffect(() => {\n validateComplexRules(search).then(setComplexValidation)\n }, [search])\n\n return \u003cSearchResults search={search} validation={complexValidation} />\n}\n```\n\n## Common Next Steps\n\nAfter setting up schema validation, you might want to:\n\n- [Work with Arrays, Objects, and Dates](./arrays-objects-dates-search-params.md) - Handle arrays, objects, dates, and nested data structures\n\n\n\n## Related Resources\n\n- TanStack Zod Adapter Documentation\n- TanStack Valibot Adapter Documentation\n- Zod Documentation - Schema validation library\n- Valibot Documentation - Lightweight validation library\n- ArkType Documentation - TypeScript-first validation\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":16558,"content_sha256":"ddec983e0b26bc3c171ca4e4da06f5b06cee588d5a90d2498974d26b415418eb"},{"filename":"references/docs/router/installation/manual.md","content":"---\ntitle: Manual Setup\n---\n\nTo set up TanStack Router manually in a React project, follow the steps below. This gives you a bare minimum setup to get going with TanStack Router using both file-based route generation and code-based route configuration:\n\n## Using File-Based Route Generation\n\n#### Install TanStack Router, Vite Plugin, and the Router Devtools\n\nInstall the necessary core dependencies:\n\n\n\nreact: @tanstack/react-router @tanstack/react-router-devtools\nsolid: @tanstack/solid-router @tanstack/solid-router-devtools\n\n\n\nInstall the necessary development dependencies:\n\n\n\nreact: @tanstack/router-plugin\nsolid: @tanstack/router-plugin\n\n\n\n#### Configure the Vite Plugin\n\n```tsx\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n plugins: [\n // Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react'\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n }),\n react(),\n // ...,\n ],\n})\n```\n\n> [!TIP]\n> If you are not using Vite, or any of the supported bundlers, you can check out the [TanStack Router CLI](./with-router-cli) guide for more info.\n\nCreate the following files:\n\n- `src/routes/__root.tsx` (with two '`_`' characters)\n- `src/routes/index.tsx`\n- `src/routes/about.tsx`\n- `src/main.tsx`\n\n\n\n# React\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRoute, Link, Outlet } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nconst RootLayout = () => (\n \u003c>\n \u003cdiv className=\"p-2 flex gap-2\">\n \u003cLink to=\"/\" className=\"[&.active]:font-bold\">\n Home\n \u003c/Link>{' '}\n \u003cLink to=\"/about\" className=\"[&.active]:font-bold\">\n About\n \u003c/Link>\n \u003c/div>\n \u003chr />\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/>\n)\n\nexport const Route = createRootRoute({ component: RootLayout })\n```\n\n```tsx title=\"src/routes/index.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/')({\n component: Index,\n})\n\nfunction Index() {\n return (\n \u003cdiv className=\"p-2\">\n \u003ch3>Welcome Home!\u003c/h3>\n \u003c/div>\n )\n}\n```\n\n```tsx title=\"src/routes/about.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/about')({\n component: About,\n})\n\nfunction About() {\n return \u003cdiv className=\"p-2\">Hello from About!\u003c/div>\n}\n```\n\n```tsx title=\"src/main.tsx\"\nimport { StrictMode } from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { RouterProvider, createRouter } from '@tanstack/react-router'\n\n// Import the generated route tree\nimport { routeTree } from './routeTree.gen'\n\n// Create a new router instance\nconst router = createRouter({ routeTree })\n\n// Register the router instance for type safety\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n\n// Render the app\nconst rootElement = document.getElementById('root')!\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement)\n root.render(\n \u003cStrictMode>\n \u003cRouterProvider router={router} />\n \u003c/StrictMode>,\n )\n}\n```\n\n\n\n# Solid\n\n\n\n```tsx title=\"src/routes/__root.tsx\"\nimport { createRootRoute, Link, Outlet } from '@tanstack/solid-router'\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n\nconst RootLayout = () => (\n \u003c>\n \u003cdiv class=\"p-2 flex gap-2\">\n \u003cLink to=\"/\" class=\"[&.active]:font-bold\">\n Home\n \u003c/Link>{' '}\n \u003cLink to=\"/about\" class=\"[&.active]:font-bold\">\n About\n \u003c/Link>\n \u003c/div>\n \u003chr />\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/>\n)\n\nexport const Route = createRootRoute({ component: RootLayout })\n```\n\n```tsx title=\"src/routes/index.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({\n component: Index,\n})\n\nfunction Index() {\n return (\n \u003cdiv class=\"p-2\">\n \u003ch3>Welcome Home!\u003c/h3>\n \u003c/div>\n )\n}\n```\n\n```tsx title=\"src/routes/about.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/about')({\n component: About,\n})\n\nfunction About() {\n return \u003cdiv class=\"p-2\">Hello from About!\u003c/div>\n}\n```\n\n```tsx title=\"src/main.tsx\"\n/* @refresh reload */\nimport { render } from 'solid-js/web'\nimport { RouterProvider, createRouter } from '@tanstack/solid-router'\n\n// Import the generated route tree\nimport { routeTree } from './routeTree.gen'\n\n// Create a new router instance\nconst router = createRouter({ routeTree })\n\n// Register the router instance for type safety\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: typeof router\n }\n}\n\n// Render the app\nconst rootElement = document.getElementById('root')!\n\nrender(() => \u003cRouterProvider router={router} />, rootElement)\n```\n\n\n\n\n\nRegardless of whether you are using the `@tanstack/router-plugin` package and running the `npm run dev`/`npm run build` scripts, or manually running the `tsr watch`/`tsr generate` commands from your package scripts, the route tree file will be generated at `src/routeTree.gen.ts`.\n\nIf you are working with this pattern you should change the `id` of the root `\u003cdiv>` on your `index.html` file to `\u003cdiv id='root'>\u003c/div>`\n\n## Using Code-Based Route Configuration\n\n> [!IMPORTANT]\n> The following example shows how to configure routes using code, and for simplicity's sake is in a single file for this demo. While code-based generation allows you to declare many routes and even the router instance in a single file, we recommend splitting your routes into separate files for better organization and performance as your application grows.\n\n\n\n# React\n\n```tsx\nimport { StrictMode } from 'react'\nimport ReactDOM from 'react-dom/client'\nimport {\n Outlet,\n RouterProvider,\n Link,\n createRouter,\n createRoute,\n createRootRoute,\n} from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nconst rootRoute = createRootRoute({\n component: () => (\n \u003c>\n \u003cdiv className=\"p-2 flex gap-2\">\n \u003cLink to=\"/\" className=\"[&.active]:font-bold\">\n Home\n \u003c/Link>{' '}\n \u003cLink to=\"/about\" className=\"[&.active]:font-bold\">\n About\n \u003c/Link>\n \u003c/div>\n \u003chr />\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/>\n ),\n})\n\nconst indexRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n component: function Index() {\n return (\n \u003cdiv className=\"p-2\">\n \u003ch3>Welcome Home!\u003c/h3>\n \u003c/div>\n )\n },\n})\n\nconst aboutRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/about',\n component: function About() {\n return \u003cdiv className=\"p-2\">Hello from About!\u003c/div>\n },\n})\n\nconst routeTree = rootRoute.addChildren([indexRoute, aboutRoute])\n\nconst router = createRouter({ routeTree })\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n\nconst rootElement = document.getElementById('app')!\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement)\n root.render(\n \u003cStrictMode>\n \u003cRouterProvider router={router} />\n \u003c/StrictMode>,\n )\n}\n```\n\n# Solid\n\n```tsx\n/* @refresh reload */\nimport { render } from 'solid-js/web'\nimport {\n Outlet,\n RouterProvider,\n Link,\n createRouter,\n createRoute,\n createRootRoute,\n} from '@tanstack/solid-router'\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n\nconst rootRoute = createRootRoute({\n component: () => (\n \u003c>\n \u003cdiv class=\"p-2 flex gap-2\">\n \u003cLink to=\"/\" class=\"[&.active]:font-bold\">\n Home\n \u003c/Link>{' '}\n \u003cLink to=\"/about\" class=\"[&.active]:font-bold\">\n About\n \u003c/Link>\n \u003c/div>\n \u003chr />\n \u003cOutlet />\n \u003cTanStackRouterDevtools />\n \u003c/>\n ),\n})\n\nconst indexRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n component: function Index() {\n return (\n \u003cdiv class=\"p-2\">\n \u003ch3>Welcome Home!\u003c/h3>\n \u003c/div>\n )\n },\n})\n\nconst aboutRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/about',\n component: function About() {\n return \u003cdiv class=\"p-2\">Hello from About!\u003c/div>\n },\n})\n\nconst routeTree = rootRoute.addChildren([indexRoute, aboutRoute])\n\nconst router = createRouter({ routeTree })\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: typeof router\n }\n}\n\nconst rootElement = document.getElementById('app')!\nrender(() => \u003cRouterProvider router={router} />, rootElement)\n```\n\n\n\nIf you glossed over these examples or didn't understand something, we don't blame you, because there's so much more to learn to really take advantage of TanStack Router! Let's move on.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8840,"content_sha256":"211bfb6388754843cbb89da61caad187a04f1c3e9e4b3090347d48881f93b5d8"},{"filename":"references/docs/router/installation/with-esbuild.md","content":"---\ntitle: Installation with Esbuild\n---\n\nTo use file-based routing with **Esbuild**, you'll need to install the `@tanstack/router-plugin` package.\n\n\n\nreact: @tanstack/router-plugin\nsolid: @tanstack/router-plugin\n\n\n\nOnce installed, you'll need to add the plugin to your configuration.\n\n\n\n# React\n\n```ts title=\"esbuild.config.js\"\nimport { tanstackRouter } from '@tanstack/router-plugin/esbuild'\n\nexport default {\n // ...\n plugins: [\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n }),\n ],\n}\n```\n\nOr, you can clone our Quickstart Esbuild example and get started.\n\n# Solid\n\n```ts title=\"build.js\"\nimport * as esbuild from 'esbuild'\nimport { solidPlugin } from 'esbuild-plugin-solid'\nimport { tanstackRouter } from '@tanstack/router-plugin/esbuild'\n\nconst isDev = process.argv.includes('--dev')\n\nconst ctx = await esbuild.context({\n entryPoints: ['src/main.tsx'],\n outfile: 'dist/main.js',\n minify: !isDev,\n bundle: true,\n format: 'esm',\n target: ['esnext'],\n sourcemap: true,\n plugins: [\n solidPlugin(),\n tanstackRouter({ target: 'solid', autoCodeSplitting: true }),\n ],\n})\n\nif (isDev) {\n await ctx.watch()\n const { host, port } = await ctx.serve({ servedir: '.', port: 3005 })\n console.log(`Server running at http://${host || 'localhost'}:${port}`)\n} else {\n await ctx.rebuild()\n await ctx.dispose()\n}\n```\n\nOr, you can clone our Quickstart Esbuild example and get started.\n\n\n\nNow that you've added the plugin to your Esbuild configuration, you're all set to start using file-based routing with TanStack Router.\n\n## Ignoring the generated route tree file\n\nIf your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter.\n\nHere are some resources to help you ignore the generated route tree file:\n\n- Prettier - https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore\n- ESLint - https://eslint.org/docs/latest/use/configure/ignore#ignoring-files\n- Biome - https://biomejs.dev/reference/configuration/#filesignore\n\n> [!WARNING]\n> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route.\n\nYou can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings:\n\n```json\n{\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n```\n\nYou can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project.\n\n## Configuration\n\nWhen using the TanStack Router Plugin with Esbuild for File-based routing, it comes with some sane defaults that should work for most projects:\n\n```json\n{\n \"routesDirectory\": \"./src/routes\",\n \"generatedRouteTree\": \"./src/routeTree.gen.ts\",\n \"routeFileIgnorePrefix\": \"-\",\n \"quoteStyle\": \"single\"\n}\n```\n\nIf these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by editing the configuration object passed into the `tanstackRouter` function.\n\nYou can find all the available configuration options in the [File-based Routing API Reference](../api/file-based-routing.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3502,"content_sha256":"e3bd2328d1b5855c4ffd6de7498a20790cc554404eb58fe961f53bfdec84afef"},{"filename":"references/docs/router/installation/with-router-cli.md","content":"---\ntitle: Installation with Router CLI\n---\n\n> [!WARNING]\n> You should only use the TanStack Router CLI if you are not using a supported bundler. The CLI only supports the generation of the route tree file and does not provide any other features.\n\nTo use file-based routing with the TanStack Router CLI, you'll need to install the `@tanstack/router-cli` package.\n\n\n\nreact: @tanstack/router-cli\nsolid: @tanstack/router-cli\n\n\n\nOnce installed, you'll need to amend your scripts in your `package.json` for the CLI to `watch` and `generate` files.\n\n```json\n{\n \"scripts\": {\n \"generate-routes\": \"tsr generate\",\n \"watch-routes\": \"tsr watch\",\n \"build\": \"npm run generate-routes && ...\",\n \"dev\": \"npm run watch-routes && ...\"\n }\n}\n```\n\n\n\n# Solid\n\nIf you are using TypeScript, you should also add the following options to your `tsconfig.json`:\n\n```json\n{\n \"compilerOptions\": {\n \"jsx\": \"preserve\",\n \"jsxImportSource\": \"solid-js\"\n }\n}\n```\n\nWith that, you're all set to start using file-based routing with TanStack Router.\n\n\n\n[//]: # 'AfterScripts'\n[//]: # 'AfterScripts'\n\nYou shouldn't forget to _ignore_ the generated route tree file. Head over to the [Ignoring the generated route tree file](#ignoring-the-generated-route-tree-file) section to learn more.\n\nWith the CLI installed, the following commands are made available via the `tsr` command\n\n## Using the `generate` command\n\nGenerates the routes for a project based on the provided configuration.\n\n```sh\ntsr generate\n```\n\n## Using the `watch` command\n\nContinuously watches the specified directories and regenerates routes as needed.\n\n**Usage:**\n\n```sh\ntsr watch\n```\n\nWith file-based routing enabled, whenever you start your application in development mode, TanStack Router will watch your configured `routesDirectory` and generate your route tree whenever a file is added, removed, or changed.\n\n## Ignoring the generated route tree file\n\nIf your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter.\n\nHere are some resources to help you ignore the generated route tree file:\n\n- Prettier - https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore\n- ESLint - https://eslint.org/docs/latest/use/configure/ignore#ignoring-files\n- Biome - https://biomejs.dev/reference/configuration/#filesignore\n\n> [!WARNING]\n> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route.\n\nYou can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings:\n\n```json\n{\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n```\n\nYou can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project.\n\n## Configuration\n\nWhen using the TanStack Router CLI for File-based routing, it comes with some sane defaults that should work for most projects:\n\n\n\n# React\n\n```json\n{\n \"routesDirectory\": \"./src/routes\",\n \"generatedRouteTree\": \"./src/routeTree.gen.ts\",\n \"routeFileIgnorePrefix\": \"-\",\n \"quoteStyle\": \"single\",\n \"target\": \"react\"\n}\n```\n\n# Solid\n\n```json\n{\n \"routesDirectory\": \"./src/routes\",\n \"generatedRouteTree\": \"./src/routeTree.gen.ts\",\n \"routeFileIgnorePrefix\": \"-\",\n \"quoteStyle\": \"single\",\n \"target\": \"solid\"\n}\n```\n\n\n\nIf these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by creating a `tsr.config.json` file in the root of your project directory.\n\nYou can find all the available configuration options in the [File-based Routing API Reference](../api/file-based-routing.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4004,"content_sha256":"04746d4d8c78deb0732c0827afd2877fb09cf218ed8e6ad895c62d674c5bb551"},{"filename":"references/docs/router/installation/with-rspack.md","content":"---\ntitle: Installation with Rspack\n---\n\nTo use file-based routing with **Rspack** or **Rsbuild**, you'll need to install the `@tanstack/router-plugin` package.\n\n\n\nreact: @tanstack/router-plugin\nsolid: @tanstack/router-plugin\n\n\n\nOnce installed, you'll need to add the plugin to your configuration.\n\n\n\n# React\n\n```ts title=\"rsbuild.config.ts\"\nimport { defineConfig } from '@rsbuild/core'\nimport { pluginReact } from '@rsbuild/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/rspack'\n\nexport default defineConfig({\n plugins: [pluginReact()],\n tools: {\n rspack: {\n plugins: [\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n }),\n ],\n },\n },\n})\n```\n\nOr, you can clone our Quickstart Rspack/Rsbuild example and get started.\n\n# Solid\n\n```ts title=\"rsbuild.config.ts\"\nimport { defineConfig } from '@rsbuild/core'\nimport { pluginBabel } from '@rsbuild/plugin-babel'\nimport { pluginSolid } from '@rsbuild/plugin-solid'\nimport { tanstackRouter } from '@tanstack/router-plugin/rspack'\n\nexport default defineConfig({\n plugins: [\n pluginBabel({\n include: /\\.(?:jsx|tsx)$/,\n }),\n pluginSolid(),\n ],\n tools: {\n rspack: {\n plugins: [tanstackRouter({ target: 'solid', autoCodeSplitting: true })],\n },\n },\n})\n```\n\nOr, you can clone our Quickstart Rspack/Rsbuild example and get started.\n\n\n\nNow that you've added the plugin to your Rspack/Rsbuild configuration, you're all set to start using file-based routing with TanStack Router.\n\n## Ignoring the generated route tree file\n\nIf your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter.\n\nHere are some resources to help you ignore the generated route tree file:\n\n- Prettier - https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore\n- ESLint - https://eslint.org/docs/latest/use/configure/ignore#ignoring-files\n- Biome - https://biomejs.dev/reference/configuration/#filesignore\n\n> [!WARNING]\n> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route.\n\nYou can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings:\n\n```json\n{\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n```\n\nYou can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project.\n\n## Configuration\n\nWhen using the TanStack Router Plugin with Rspack (or Rsbuild) for File-based routing, it comes with some sane defaults that should work for most projects:\n\n```json\n{\n \"routesDirectory\": \"./src/routes\",\n \"generatedRouteTree\": \"./src/routeTree.gen.ts\",\n \"routeFileIgnorePrefix\": \"-\",\n \"quoteStyle\": \"single\"\n}\n```\n\nIf these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by editing the configuration object passed into the `tanstackRouter` function.\n\nYou can find all the available configuration options in the [File-based Routing API Reference](../api/file-based-routing.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3480,"content_sha256":"40d500d966a6db31016c4e1fd9f55054d958d4b0608b8282f85ce1615be3bd3c"},{"filename":"references/docs/router/installation/with-vite.md","content":"---\ntitle: Installation with Vite\n---\n\nTo use file-based routing with **Vite**, you'll need to install the `@tanstack/router-plugin` package.\n\n\n\nreact: @tanstack/router-plugin\nsolid: @tanstack/router-plugin\n\n\n\nOnce installed, you'll need to add the plugin to your Vite configuration.\n\n\n\n# React\n\n```ts title=\"vite.config.ts\"\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n plugins: [\n // Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react'\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n }),\n react(),\n // ...\n ],\n})\n```\n\nOr, you can clone our Quickstart Vite example and get started.\n\n# Solid\n\n```ts title=\"vite.config.ts\"\nimport { defineConfig } from 'vite'\nimport solid from 'vite-plugin-solid'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n target: 'solid',\n autoCodeSplitting: true,\n }),\n solid(),\n // ...\n ],\n})\n```\n\nOr, you can clone our Quickstart Vite example and get started.\n\n\n\nNow that you've added the plugin to your Vite configuration, you're all set to start using file-based routing with TanStack Router.\n\n## Ignoring the generated route tree file\n\nIf your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter.\n\nHere are some resources to help you ignore the generated route tree file:\n\n- Prettier - https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore\n- ESLint - https://eslint.org/docs/latest/use/configure/ignore#ignoring-files\n- Biome - https://biomejs.dev/reference/configuration/#filesignore\n\n> [!WARNING]\n> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route.\n\nYou can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings:\n\n```json\n{\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n```\n\nYou can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project.\n\n## Configuration\n\nWhen using the TanStack Router Plugin with Vite for File-based routing, it comes with some sane defaults that should work for most projects:\n\n```json\n{\n \"routesDirectory\": \"./src/routes\",\n \"generatedRouteTree\": \"./src/routeTree.gen.ts\",\n \"routeFileIgnorePrefix\": \"-\",\n \"quoteStyle\": \"single\"\n}\n```\n\nIf these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by editing the configuration object passed into the `tanstackRouter` function.\n\nYou can find all the available configuration options in the [File-based Routing API Reference](../api/file-based-routing.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3319,"content_sha256":"c752b8e6083cb4409401220bc41c81f94cfaf4bc160d3b4a80115b279cfe6ebf"},{"filename":"references/docs/router/installation/with-webpack.md","content":"---\ntitle: Installation with Webpack\n---\n\nTo use file-based routing with **Webpack**, you'll need to install the `@tanstack/router-plugin` package.\n\n\n\nreact: @tanstack/router-plugin\nsolid: @tanstack/router-plugin\n\n\n\nOnce installed, you'll need to add the plugin to your configuration.\n\n\n\n# React\n\n```ts title=\"webpack.config.ts\"\nimport { tanstackRouter } from '@tanstack/router-plugin/webpack'\n\nexport default {\n plugins: [\n tanstackRouter({\n target: 'react',\n autoCodeSplitting: true,\n }),\n ],\n}\n```\n\nOr, you can clone our Quickstart Webpack example and get started.\n\n# Solid\n\n```ts title=\"webpack.config.ts\"\nimport { tanstackRouter } from '@tanstack/router-plugin/webpack'\n\nexport default {\n plugins: [\n tanstackRouter({\n target: 'solid',\n autoCodeSplitting: true,\n }),\n ],\n}\n```\n\nAnd in the .babelrc (SWC doesn't support solid-js, see here), add these presets:\n\n```tsx\n// .babelrc\n\n{\n \"presets\": [\"babel-preset-solid\", \"@babel/preset-typescript\"]\n}\n\n```\n\nOr, for a full webpack.config.js, you can clone our Quickstart Webpack example and get started.\n\n\n\nNow that you've added the plugin to your Webpack configuration, you're all set to start using file-based routing with TanStack Router.\n\n## Ignoring the generated route tree file\n\nIf your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter.\n\nHere are some resources to help you ignore the generated route tree file:\n\n- Prettier - https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore\n- ESLint - https://eslint.org/docs/latest/use/configure/ignore#ignoring-files\n- Biome - https://biomejs.dev/reference/configuration/#filesignore\n\n> [!WARNING]\n> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route.\n\nYou can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings:\n\n```json\n{\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n```\n\nYou can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project.\n\n## Configuration\n\nWhen using the TanStack Router Plugin with Webpack for File-based routing, it comes with some sane defaults that should work for most projects:\n\n```json\n{\n \"routesDirectory\": \"./src/routes\",\n \"generatedRouteTree\": \"./src/routeTree.gen.ts\",\n \"routeFileIgnorePrefix\": \"-\",\n \"quoteStyle\": \"single\"\n}\n```\n\nIf these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by editing the configuration object passed into the `tanstackRouter` function.\n\nYou can find all the available configuration options in the [File-based Routing API Reference](../api/file-based-routing.md).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3168,"content_sha256":"2f944d3d4a3badfc88f81d4d6c7c24afda4272806992c76d46e94ae5be2e0cb9"},{"filename":"references/docs/router/integrations/query.md","content":"---\nid: query\ntitle: TanStack Query Integration\n---\n\n> [!IMPORTANT]\n> This integration automates SSR dehydration/hydration and streaming between TanStack Router and TanStack Query. If you haven't read the standard [External Data Loading](../guide/external-data-loading.md) guide, start there.\n\n## What you get\n\n- **Automatic SSR dehydration/hydration** of your `QueryClient`\n- **Streaming of queries** that resolve during initial server render to the client\n- **Redirect handling** for `redirect()` thrown from queries/mutations\n- Optional **provider wrapping** with `QueryClientProvider`\n\n## Installation\n\nThe TanStack query integration is a separate package that you need to install:\n\n\n\nreact: @tanstack/react-router-ssr-query\nsolid: @tanstack/solid-router-ssr-query\n\n\n\n## Setup\n\nCreate your router and wire up the integration. Ensure a fresh `QueryClient` is created per request in SSR environments.\n\n\n\n# React\n\n```tsx title=\"src/router.tsx\"\nimport { QueryClient } from '@tanstack/react-query'\nimport { createRouter } from '@tanstack/react-router'\nimport { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'\nimport { routeTree } from './routeTree.gen'\n\nexport function getRouter() {\n const queryClient = new QueryClient()\n const router = createRouter({\n routeTree,\n // optionally expose the QueryClient via router context\n context: { queryClient },\n scrollRestoration: true,\n defaultPreload: 'intent',\n })\n\n setupRouterSsrQueryIntegration({\n router,\n queryClient,\n // optional:\n // handleRedirects: true,\n // wrapQueryClient: true,\n })\n\n return router\n}\n```\n\n\n\nBy default, the integration wraps your router with a `QueryClientProvider`. If you already provide your own provider, pass `wrapQueryClient: false` and keep your custom wrapper.\n\n## SSR behavior and streaming\n\n- During server render, the integration dehydrates initial queries and streams any subsequent queries that resolve while rendering.\n- On the client, the integration hydrates the initial state, then incrementally hydrates streamed queries.\n- Queries from `useSuspenseQuery` or loader prefetches participate in SSR/streaming. Plain `useQuery` does not execute on the server.\n\n## Use in routes\n\n### Using useSuspenseQuery vs useQuery\n\n- `useSuspenseQuery`: runs on the server during SSR when its data is required and will be streamed to the client as it resolves.\n- `useQuery`: does not execute on the server; it will fetch on the client after hydration. Use this for data that is not required for SSR.\n\n```tsx\n// Suspense: executes on server and streams\nconst { data } = useSuspenseQuery(postsQuery)\n\n// Non-suspense: executes only on client\nconst { data, isLoading } = useQuery(postsQuery)\n```\n\n\n\n# React\n\n```tsx\n// Suspense: executes on server and streams\nconst { data } = useSuspenseQuery(postsQuery)\n\n// Non-suspense: executes only on client\nconst { data, isLoading } = useQuery(postsQuery)\n```\n\n\n\n### Preload with a loader and read with a hook\n\nPreload critical data in the route `loader` to avoid waterfalls and loading flashes, then read it in the component. The integration ensures server-fetched data is dehydrated and streamed to the client during SSR.\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { queryOptions, useSuspenseQuery, useQuery } from '@tanstack/react-query'\nimport { createFileRoute } from '@tanstack/react-router'\n\nconst postsQuery = queryOptions({\n queryKey: ['posts'],\n queryFn: () => fetch('/api/posts').then((r) => r.json()),\n})\n\nexport const Route = createFileRoute('/posts')({\n // Ensure the data is in the cache before render\n loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),\n component: PostsPage,\n})\n\nfunction PostsPage() {\n // Prefer suspense for best SSR + streaming behavior\n const { data } = useSuspenseQuery(postsQuery)\n return \u003cdiv>{data.map((p: any) => p.title).join(', ')}\u003c/div>\n}\n```\n\n\n\n### Prefetching and streaming\n\nYou can also prefetch with `fetchQuery` or `ensureQueryData` in a loader without consuming the data in a component. If you return the promise directly from the loader, it will be awaited and thus block the SSR request until the query finishes. If you don't await the promise nor return it, the query will be started on the server and will be streamed to the client without blocking the SSR request.\n\n\n\n# React\n\n```tsx title=\"src/routes/users.$id.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\nimport { queryOptions, useQuery } from '@tanstack/react-query'\n\nconst userQuery = (id: string) =>\n queryOptions({\n queryKey: ['user', id],\n queryFn: () => fetch(`/api/users/${id}`).then((r) => r.json()),\n })\n\nexport const Route = createFileRoute('/user/$id')({\n loader: ({ params }) => {\n // do not await this nor return the promise, just kick off the query to stream it to the client\n context.queryClient.fetchQuery(userQuery(params.id))\n },\n})\n```\n\n\n\n## Redirect handling\n\nIf a query or mutation throws a `redirect(...)`, the integration intercepts it on the client and performs a router navigation.\n\n- Enabled by default\n- Disable with `handleRedirects: false` if you need custom handling\n\n## Works with TanStack Start\n\nTanStack Start uses TanStack Router under the hood. The same setup applies, and the integration will stream query results during SSR automatically.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5329,"content_sha256":"4753f62d0d2cdb4440a9d6e464cbbf6b864db334c395e71d93ba90c937ac18c3"},{"filename":"references/docs/router/latest.md","content":"---\ntitle: \"TanStack Router\"\nmeta:\n \"og:description\": \"A powerful React router for client-side and full-stack react applications. Fully type-safe APIs, first-class search-params for managing state in the URL and seamless integration with the existing React ecosystem.\"\n \"og:title\": \"TanStack Router\"\n \"twitter:description\": \"A powerful React router for client-side and full-stack react applications. Fully type-safe APIs, first-class search-params for managing state in the URL and seamless integration with the existing React ecosystem.\"\n \"twitter:title\": \"TanStack Router\"\n description: \"A powerful React router for client-side and full-stack react applications. Fully type-safe APIs, first-class search-params for managing state in the URL and seamless integration with the existing React ecosystem.\"\n---","content_type":"text/markdown; charset=utf-8","language":"markdown","size":812,"content_sha256":"a864c8639b25b38251ac5abeea7aed94e51cbce084e1e970329bc4ba77e519ca"},{"filename":"references/docs/router/llm-support.md","content":"---\nid: llm-assistance\ntitle: LLM Assistance Support\n---\n\nTanStack Router's documentation is integrated into its NPM module, making it available to install as LLM assistance rules. These rules can be integrated into various editors to provide context-aware help using `vibe-rules`.\n\n## Installation\n\nTo use `vibe-rules`, install it globally using your package manager of choice. For example, with `pnpm`:\n\n```bash\npnpm add -g vibe-rules\n```\n\nOnce installed, you can then run it in the editor of your choice. For example, to integrate with Cursor:\n\n```bash\nvibe-rules install cursor\n```\n\n## Supported Editors\n\n`vibe-rules` supports a variety of editors, including `windsurf`, `claude-code`, and more. For more information on supported editors and how to set them up, refer to the `vibe-rules` documentation.\n\n> [!IMPORTANT]\n> If you're using Yarn Workspaces, you will need to add the following configuration to your `.yarnrc.yaml` file of your application that uses TanStack Router:\n\n> ```yaml\n> pnpFallbackMode: all\n> pnpMode: loose\n> ```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1039,"content_sha256":"b814c1afdc315af101c0b6f65c45eca124391a7f491b368947ffba8d029bc816"},{"filename":"references/docs/router/overview.md","content":"---\ntitle: Overview\n---\n\n**TanStack Router is a router for building React and Solid applications**. Some of its features include:\n\n- 100% inferred TypeScript support\n- Typesafe navigation\n- Nested Routing and layout routes (with pathless layouts)\n- Built-in Route Loaders w/ SWR Caching\n- Designed for client-side data caches (TanStack Query, SWR, etc.)\n- Automatic route prefetching\n- Asynchronous route elements and error boundaries\n- File-based Route Generation\n- Typesafe JSON-first Search Params state management APIs\n- Path and Search Parameter Schema Validation\n- Search Param Navigation APIs\n- Custom Search Param parser/serializer support\n- Search param middleware\n- Route matching/loading middleware\n\nTo get started quickly, head to the next page. For a more lengthy explanation, buckle up while I bring you up to speed!\n\n## \"A Fork in the Route\"\n\nUsing a router to build applications is widely regarded as a must-have and is usually one of the first choices you’ll make in your tech stack.\n\n## Why TanStack Router?\n\nTanStack Router delivers on the same fundamental expectations as other routers that you’ve come to expect:\n\n- Nested routes, layout routes, grouped routes\n- File-based Routing\n- Parallel data loading\n- Prefetching\n- URL Path Params\n- Error Boundaries and Handling\n- SSR\n- Route Masking\n\nAnd it also delivers some new features that raise the bar:\n\n- 100% inferred TypeScript support\n- Typesafe navigation\n- Built-in SWR Caching for loaders\n- Designed for client-side data caches (TanStack Query, SWR, etc.)\n- Typesafe JSON-first Search Params state management APIs\n- Path and Search Parameter Schema Validation\n- Search Parameter Navigation APIs\n- Custom Search Param parser/serializer support\n- Search param middleware\n- Inherited Route Context\n- Mixed file-based and code-based routing\n\nLet’s dive into some of the more important ones in more detail!\n\n## 100% Inferred TypeScript Support\n\nEverything these days is written “in Typescript” or at the very least offers type definitions that are veneered over runtime functionality, but too few packages in the ecosystem actually design their APIs with TypeScript in mind. So while I’m pleased that your router is auto-completing your option fields and catching a few property/method typos here and there, there is much more to be had.\n\n- TanStack Router is fully aware of all of your routes and their configuration at any given point in your code. This includes the path, path params, search params, context, and any other configuration you’ve provided. Ultimately this means that you can navigate to any route in your app with 100% type safety and confidence that your link or navigate call will succeed.\n- TanStack Router provides lossless type-inference. It uses countless generic type parameters to enforce and propagate any type information you give it throughout the rest of its API and ultimately your app. No other router offers this level of type safety and developer confidence.\n\nWhat does all of that mean for you?\n\n- Faster feature development with auto-completion and type hints\n- Safer and faster refactors\n- Confidence that your code will work as expected\n\n## 1st Class Search Parameters\n\nSearch parameters are often an afterthought, treated like a black box of strings (or string) that you can parse and update, but not much else. Existing solutions are **not** type-safe either, adding to the caution that is required to deal with them. Even the most \"modern\" frameworks and routers leave it up to you to figure out how to manage this state. Sometimes they'll parse the search string into an object for you, or sometimes you're left to do it yourself with `URLSearchParams`.\n\nLet's step back and remember that **search params are the most powerful state manager in your entire application.** They are global, serializable, bookmarkable, and shareable making them the perfect place to store any kind of state that needs to survive a page refresh or a social share.\n\nTo live up to that responsibility, search parameters are a first-class citizen in TanStack Router. While still based on standard URLSearchParams, TanStack Router uses a powerful parser/serializer to manage deeper and more complex data structures in your search params, all while keeping them type-safe and easy to work with.\n\n**It's like having `useState` right in the URL!**\n\nSearch parameters are:\n\n- Automatically parsed and serialized as JSON\n- Validated and typed\n- Inherited from parent routes\n- Accessible in loaders, components, and hooks\n- Easily modified with the useSearch hook, Link, navigate, and router.navigate APIs\n- Customizable with a custom search filters and middleware\n- Subscribed via fine-grained search param selectors for efficient re-renders\n\nOnce you start using TanStack Router's search parameters, you'll wonder how you ever lived without them.\n\n## Built-In Caching and Friendly Data Loading\n\nData loading is a critical part of any application and while most existing routers offer some form of critical data loading APIs, they often fall short when it comes to caching and data lifecycle management. Existing solutions suffer from a few common problems:\n\n- No caching at all. Data is always fresh, but your users are left waiting for frequently accessed data to load over and over again.\n- Overly-aggressive caching. Data is cached for too long, leading to stale data and a poor user experience.\n- Blunt invalidation strategies and APIs. Data may be invalidated too often, leading to unnecessary network requests and wasted resources, or you may not have any fine-grained control over when data is invalidated at all.\n\nTanStack Router solves these problems with a two-prong approach to caching and data loading:\n\n### Built-in Cache\n\nTanStack Router provides a light-weight built-in caching layer that works seamlessly with the Router. This caching layer is loosely based on TanStack Query, but with fewer features and a much smaller API surface area. Like TanStack Query, sane but powerful defaults guarantee that your data is cached for reuse, invalidated when necessary, and garbage collected when not in use. It also provides a simple API for invalidating the cache manually when needed.\n\n### Flexible & Powerful Data Lifecycle APIs\n\nTanStack Router is designed with a flexible and powerful data loading API that more easily integrates with existing data fetching libraries like TanStack Query, SWR, Apollo, Relay, or even your own custom data fetching solution. Configurable APIs like `context`, `beforeLoad`, `loaderDeps` and `loader` work in unison to make it easy to define declarative data dependencies, prefetch data, and manage the lifecycle of an external data source with ease.\n\n## Inherited Route Context\n\nTanStack Router's router and route context is a powerful feature that allows you to define context that is specific to a route which is then inherited by all child routes. Even the router and root routes themselves can provide context. Context can be built up both synchronously and asynchronously, and can be used to share data, configuration, or even functions between routes and route configurations. This is especially useful for scenarios like:\n\n- Authentication and Authorization\n- Hybrid SSR/CSR data fetching and preloading\n- Theming\n- Singletons and global utilities\n- Curried or partial application across preloading, loading, and rendering stages\n\nAlso, what would route context be if it weren't type-safe? TanStack Router's route context is fully type-safe and inferred at zero cost to you.\n\n## File-based and/or Code-Based Routing\n\nTanStack Router supports both file-based and code-based routing at the same time. This flexibility allows you to choose the approach that best fits your project's needs.\n\nTanStack Router's file-based routing approach is uniquely user-facing. Route configuration is generated for you either by the Vite plugin or TanStack Router CLI, leaving the usage of said generated code up to you! This means that you're always in total control of your routes and router, even if you use file-based routing.\n\n## Acknowledgements\n\nTanStack Router builds on concepts and patterns popularized by many other OSS projects, including:\n\n- TRPC\n- Remix\n- Chicane\n- Next.js\n\nWe acknowledge the investment, risk and research that went into their development, but are excited to push the bar they have set even higher.\n\n## Let's go!\n\nEnough overview, there's so much more to do with TanStack Router. Hit that next button and let's get started!\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8485,"content_sha256":"22606357eda0161d107b7a29f6b50c152b7ac3266754ae229a9bd67c7ba27121"},{"filename":"references/docs/router/quick-start.md","content":"---\nid: quick-start\ntitle: Quick Start\n---\n\n## Impatient?\n\nThe fastest way to get started with TanStack Router is to scaffold a new project. Just run:\n\n\n\nreact: create-tsrouter-app@latest\nsolid: create-tsrouter-app@latest --framework solid\n\n\n\nThe CLI will guide you through a short series of prompts to customize your setup, including options for:\n\n- File-based or code-based route configuration\n- TypeScript support\n- Tailwind CSS integration\n- Toolchain setup\n- Git initialization\n\nOnce complete, a new project will be generated with TanStack Router installed and ready to use.\n\n> [!TIP]\n> For full details on available options and templates, visit the `create-tsrouter-app` documentation.\n\n## Routing Options\n\nTanStack Router supports both file-based and code-based route configurations. You can specify your preference during the CLI setup, or use these commands directly:\n\n### File-Based Route Generation\n\nThe file-based approach is the recommended option for most projects. It automatically creates routes based on your file structure, giving you the best mix of performance, simplicity, and developer experience.\n\n\n\nreact: create-tsrouter-app@latest my-app --template file-router\nsolid: create-tsrouter-app@latest my-app --framework solid --template file-router\n\n\n\n### Code-Based Route Configuration\n\nIf you prefer to define routes programmatically, you can use the code-based route configuration. This approach gives you full control over routing logic.\n\n\n\nreact: create-tsrouter-app@latest my-app\nsolid: create-tsrouter-app@latest my-app --framework solid\n\n\n\nWith either approach, navigate to your project directory and start the development server.\n\n## Existing Project\n\nIf you have an existing React project and want to add TanStack Router to it, you can install it manually.\n\n### Requirements\n\nBefore installing TanStack Router, please ensure your project meets the following requirements:\n\n\n\n# React\n\n- `react` v18 or later with `createRoot` support.\n- `react-dom` v18 or later.\n\n# Solid\n\n- `solid-js` v1.x.x\n\n\n\n> [!NOTE]\n> Using TypeScript (`v5.3.x or higher`) is recommended for the best development experience, though not strictly required. We aim to support the last 5 minor versions of TypeScript, but using the latest version will help avoid potential issues.\n\nTanStack Router is currently only compatible with React (with ReactDOM) and Solid. If you're interested in contributing to support other frameworks, such as React Native, Angular, or Vue, please reach out to us on Discord.\n\n### Installation\n\nTo install TanStack Router in your project, run the following command using your preferred package manager:\n\n\n\nreact: @tanstack/react-router\nsolid: @tanstack/solid-router\n\n\n\nOnce installed, you can verify the installation by checking your `package.json` file for the dependency.\n\n\n\n# React\n\n```json\n{\n \"dependencies\": {\n \"@tanstack/react-router\": \"^x.x.x\"\n }\n}\n```\n\n# Solid\n\n```json\n{\n \"dependencies\": {\n \"@tanstack/solid-router\": \"^x.x.x\"\n }\n}\n```\n\n\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2983,"content_sha256":"73aeacb0aad009b01b7a7ee732ae5717c7be7c131af167d0941a2d22692cfc49"},{"filename":"references/docs/router/routing/code-based-routing.md","content":"---\ntitle: Code-Based Routing\n---\n\n> [!TIP]\n> Code-based routing is not recommended for most applications. It is recommended to use [File-Based Routing](./file-based-routing.md) instead.\n\n## Before You Start\n\n- If you're using [File-Based Routing](./file-based-routing.md), **skip this guide**.\n- If you still insist on using code-based routing, you must read the [Routing Concepts](./routing-concepts.md) guide first, as it also covers core concepts of the router.\n\n## Route Trees\n\nCode-based routing is no different from file-based routing in that it uses the same route tree concept to organize, match and compose matching routes into a component tree. The only difference is that instead of using the filesystem to organize your routes, you use code.\n\nLet's consider the same route tree from the [Route Trees & Nesting](./route-trees.md#route-trees) guide, and convert it to code-based routing:\n\nHere is the file-based version:\n\n```\nroutes/\n├── __root.tsx\n├── index.tsx\n├── about.tsx\n├── posts/\n│ ├── index.tsx\n│ ├── $postId.tsx\n├── posts.$postId.edit.tsx\n├── settings/\n│ ├── profile.tsx\n│ ├── notifications.tsx\n├── _pathlessLayout.tsx\n├── _pathlessLayout/\n│ ├── route-a.tsx\n├── ├── route-b.tsx\n├── files/\n│ ├── $.tsx\n```\n\nAnd here is a summarized code-based version:\n\n\n\n# React\n\n```tsx\nimport { createRootRoute, createRoute } from '@tanstack/react-router'\n\nconst rootRoute = createRootRoute()\n\nconst indexRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n})\n\nconst aboutRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'about',\n})\n\nconst postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts',\n})\n\nconst postsIndexRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '/',\n})\n\nconst postRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '$postId',\n})\n\nconst postEditorRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts/$postId/edit',\n})\n\nconst settingsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'settings',\n})\n\nconst profileRoute = createRoute({\n getParentRoute: () => settingsRoute,\n path: 'profile',\n})\n\nconst notificationsRoute = createRoute({\n getParentRoute: () => settingsRoute,\n path: 'notifications',\n})\n\nconst pathlessLayoutRoute = createRoute({\n getParentRoute: () => rootRoute,\n id: 'pathlessLayout',\n})\n\nconst pathlessLayoutARoute = createRoute({\n getParentRoute: () => pathlessLayoutRoute,\n path: 'route-a',\n})\n\nconst pathlessLayoutBRoute = createRoute({\n getParentRoute: () => pathlessLayoutRoute,\n path: 'route-b',\n})\n\nconst filesRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'files/

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

,\n})\n```\n\n# Solid\n\n```tsx\nimport { createRootRoute, createRoute } from '@tanstack/solid-router'\n\nconst rootRoute = createRootRoute()\n\nconst indexRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/',\n})\n\nconst aboutRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'about',\n})\n\nconst postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts',\n})\n\nconst postsIndexRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '/',\n})\n\nconst postRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '$postId',\n})\n\nconst postEditorRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts/$postId/edit',\n})\n\nconst settingsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'settings',\n})\n\nconst profileRoute = createRoute({\n getParentRoute: () => settingsRoute,\n path: 'profile',\n})\n\nconst notificationsRoute = createRoute({\n getParentRoute: () => settingsRoute,\n path: 'notifications',\n})\n\nconst pathlessLayoutRoute = createRoute({\n getParentRoute: () => rootRoute,\n id: 'pathlessLayout',\n})\n\nconst pathlessLayoutARoute = createRoute({\n getParentRoute: () => pathlessLayoutRoute,\n path: 'route-a',\n})\n\nconst pathlessLayoutBRoute = createRoute({\n getParentRoute: () => pathlessLayoutRoute,\n path: 'route-b',\n})\n\nconst filesRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'files/

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

,\n})\n```\n\n\n\n## Anatomy of a Route\n\nAll other routes other than the root route are configured using the `createRoute` function:\n\n```tsx\nconst route = createRoute({\n getParentRoute: () => rootRoute,\n path: '/posts',\n component: PostsComponent,\n})\n```\n\nThe `getParentRoute` option is a function that returns the parent route of the route you're creating.\n\n** \"Wait, you're making me pass the parent route for every route I make?\"**\n\nAbsolutely! The reason for passing the parent route has **everything to do with the magical type safety** of TanStack Router. Without the parent route, TypeScript would have no idea what types to supply your route with!\n\n> [!IMPORTANT]\n> For every route that's **NOT** the **Root Route** or a **Pathless Layout Route**, a `path` option is required. This is the path that will be matched against the URL pathname to determine if the route is a match.\n\nWhen configuring route `path` option on a route, it ignores leading and trailing slashes (this does not include \"index\" route paths `/`). You can include them if you want, but they will be normalized internally by TanStack Router. Here is a table of valid paths and what they will be normalized to:\n\n| Path | Normalized Path |\n| -------- | --------------- |\n| `/` | `/` |\n| `/about` | `about` |\n| `about/` | `about` |\n| `about` | `about` |\n| ` tanstack-vue-router-skilld — Skillopedia | ` tanstack-vue-router-skilld — Skillopedia |\n| `/ tanstack-vue-router-skilld — Skillopedia | ` tanstack-vue-router-skilld — Skillopedia |\n| `/$/` | ` tanstack-vue-router-skilld — Skillopedia |\n\n## Manually building the route tree\n\nWhen building a route tree in code, it's not enough to define the parent route of each route. You must also construct the final route tree by adding each route to its parent route's `children` array. This is because the route tree is not built automatically for you like it is in file-based routing.\n\n```tsx\n/* prettier-ignore */\nconst routeTree = rootRoute.addChildren([\n indexRoute,\n aboutRoute,\n postsRoute.addChildren([\n postsIndexRoute,\n postRoute,\n ]),\n postEditorRoute,\n settingsRoute.addChildren([\n profileRoute,\n notificationsRoute,\n ]),\n pathlessLayoutRoute.addChildren([\n pathlessLayoutARoute,\n pathlessLayoutBRoute,\n ]),\n filesRoute.addChildren([\n fileRoute,\n ]),\n])\n/* prettier-ignore-end */\n```\n\nBut before you can go ahead and build the route tree, you need to understand how the Routing Concepts for Code-Based Routing work.\n\n## Routing Concepts for Code-Based Routing\n\nBelieve it or not, file-based routing is really a superset of code-based routing and uses the filesystem and a bit of code-generation abstraction on top of it to generate this structure you see above automatically.\n\nWe're going to assume you've read the [Routing Concepts](./routing-concepts.md) guide and are familiar with each of these main concepts:\n\n- The Root Route\n- Basic Routes\n- Index Routes\n- Dynamic Route Segments\n- Splat / Catch-All Routes\n- Layout Routes\n- Pathless Routes\n- Non-Nested Routes\n\nNow, let's take a look at how to create each of these route types in code.\n\n## The Root Route\n\nCreating a root route in code-based routing is thankfully the same as doing so in file-based routing. Call the `createRootRoute()` function.\n\nUnlike file-based routing however, you do not need to export the root route if you don't want to. It's certainly not recommended to build an entire route tree and application in a single file (although you can and we do this in the examples to demonstrate routing concepts in brevity).\n\n\n\n# React\n\n```tsx\n// Standard root route\nimport { createRootRoute } from '@tanstack/react-router'\n\nconst rootRoute = createRootRoute()\n\n// Root route with Context\nimport { createRootRouteWithContext } from '@tanstack/react-router'\nimport type { QueryClient } from '@tanstack/react-query'\n\nexport interface MyRouterContext {\n queryClient: QueryClient\n}\nconst rootRoute = createRootRouteWithContext\u003cMyRouterContext>()\n```\n\n# Solid\n\n```tsx\n// Standard root route\nimport { createRootRoute } from '@tanstack/solid-router'\n\nconst rootRoute = createRootRoute()\n\n// Root route with Context\nimport { createRootRouteWithContext } from '@tanstack/solid-router'\nimport type { QueryClient } from '@tanstack/solid-query'\n\nexport interface MyRouterContext {\n queryClient: QueryClient\n}\nconst rootRoute = createRootRouteWithContext\u003cMyRouterContext>()\n```\n\n\n\nTo learn more about Context in TanStack Router, see the [Router Context](../guide/router-context.md) guide.\n\n## Basic Routes\n\nTo create a basic route, simply provide a normal `path` string to the `createRoute` function:\n\n```tsx\nconst aboutRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'about',\n})\n```\n\nSee, it's that simple! The `aboutRoute` will match the URL `/about`.\n\n## Index Routes\n\nUnlike file-based routing, which uses the `index` filename to denote an index route, code-based routing uses a single slash `/` to denote an index route. For example, the `posts.index.tsx` file from our example route tree above would be represented in code-based routing like this:\n\n```tsx\nconst postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts',\n})\n\nconst postsIndexRoute = createRoute({\n getParentRoute: () => postsRoute,\n // Notice the single slash `/` here\n path: '/',\n})\n```\n\nSo, the `postsIndexRoute` will match the URL `/posts/` (or `/posts`).\n\n## Dynamic Route Segments\n\nDynamic route segments work exactly the same in code-based routing as they do in file-based routing. Simply prefix a segment of the path with a ` tanstack-vue-router-skilld — Skillopedia and it will be captured into the `params` object of the route's `loader` or `component`:\n\n```tsx\nconst postIdRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '$postId',\n // In a loader\n loader: ({ params }) => fetchPost(params.postId),\n // Or in a component\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const { postId } = postIdRoute.useParams()\n return \u003cdiv>Post ID: {postId}\u003c/div>\n}\n```\n\n> [!TIP]\n> If your component is code-split, you can use the [getRouteApi function](../guide/code-splitting.md#manually-accessing-route-apis-in-other-files-with-the-getrouteapi-helper) to avoid having to import the `postIdRoute` configuration to get access to the typed `useParams()` hook.\n\n## Splat / Catch-All Routes\n\nAs expected, splat/catch-all routes also work the same in code-based routing as they do in file-based routing. Simply prefix a segment of the path with a ` tanstack-vue-router-skilld — Skillopedia and it will be captured into the `params` object under the `_splat` key:\n\n```tsx\nconst filesRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'files',\n})\n\nconst fileRoute = createRoute({\n getParentRoute: () => filesRoute,\n path: '

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…

,\n})\n```\n\nFor the URL `/documents/hello-world`, the `params` object will look like this:\n\n```js\n{\n '_splat': 'documents/hello-world'\n}\n```\n\n## Layout Routes\n\nLayout routes are routes that wrap their children in a layout component. In code-based routing, you can create a layout route by simply nesting a route under another route:\n\n```tsx\nconst postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts',\n component: PostsLayoutComponent, // The layout component\n})\n\nfunction PostsLayoutComponent() {\n return (\n \u003cdiv>\n \u003ch1>Posts\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n\nconst postsIndexRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '/',\n})\n\nconst postsCreateRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: 'create',\n})\n\nconst routeTree = rootRoute.addChildren([\n // The postsRoute is the layout route\n // Its children will be nested under the PostsLayoutComponent\n postsRoute.addChildren([postsIndexRoute, postsCreateRoute]),\n])\n```\n\nNow, both the `postsIndexRoute` and `postsCreateRoute` will render their contents inside of the `PostsLayoutComponent`:\n\n```tsx\n// URL: /posts\n\u003cPostsLayoutComponent>\n \u003cPostsIndexComponent />\n\u003c/PostsLayoutComponent>\n\n// URL: /posts/create\n\u003cPostsLayoutComponent>\n \u003cPostsCreateComponent />\n\u003c/PostsLayoutComponent>\n```\n\n## Pathless Layout Routes\n\nIn file-based routing a pathless layout route is prefixed with a `_`, but in code-based routing, this is simply a route with an `id` instead of a `path` option. This is because code-based routing does not use the filesystem to organize routes, so there is no need to prefix a route with a `_` to denote that it has no path.\n\n```tsx\nconst pathlessLayoutRoute = createRoute({\n getParentRoute: () => rootRoute,\n id: 'pathlessLayout',\n component: PathlessLayoutComponent,\n})\n\nfunction PathlessLayoutComponent() {\n return (\n \u003cdiv>\n \u003ch1>Pathless Layout\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n\nconst pathlessLayoutARoute = createRoute({\n getParentRoute: () => pathlessLayoutRoute,\n path: 'route-a',\n})\n\nconst pathlessLayoutBRoute = createRoute({\n getParentRoute: () => pathlessLayoutRoute,\n path: 'route-b',\n})\n\nconst routeTree = rootRoute.addChildren([\n // The pathless layout route has no path, only an id\n // So its children will be nested under the pathless layout route\n pathlessLayoutRoute.addChildren([pathlessLayoutARoute, pathlessLayoutBRoute]),\n])\n```\n\nNow both `/route-a` and `/route-b` will render their contents inside of the `PathlessLayoutComponent`:\n\n```tsx\n// URL: /route-a\n\u003cPathlessLayoutComponent>\n \u003cRouteAComponent />\n\u003c/PathlessLayoutComponent>\n\n// URL: /route-b\n\u003cPathlessLayoutComponent>\n \u003cRouteBComponent />\n\u003c/PathlessLayoutComponent>\n```\n\n## Non-Nested Routes\n\nBuilding non-nested routes in code-based routing does not require using a trailing `_` in the path, but does require you to build your route and route tree with the right paths and nesting. Let's consider the route tree where we want the post editor to **not** be nested under the posts route:\n\n- `/posts_/$postId/edit`\n- `/posts`\n - `$postId`\n\nTo do this we need to build a separate route for the post editor and include the entire path in the `path` option from the root of where we want the route to be nested (in this case, the root):\n\n```tsx\n// The posts editor route is nested under the root route\nconst postEditorRoute = createRoute({\n getParentRoute: () => rootRoute,\n // The path includes the entire path we need to match\n path: 'posts/$postId/edit',\n})\n\nconst postsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: 'posts',\n})\n\nconst postRoute = createRoute({\n getParentRoute: () => postsRoute,\n path: '$postId',\n})\n\nconst routeTree = rootRoute.addChildren([\n // The post editor route is nested under the root route\n postEditorRoute,\n postsRoute.addChildren([postRoute]),\n])\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14664,"content_sha256":"9f3b3b1f89dd510a5e24e5e949aac6f3be0f6d77cd33602758d968ed6d61426a"},{"filename":"references/docs/router/routing/file-based-routing.md","content":"---\ntitle: File-Based Routing\n---\n\nMost of the TanStack Router documentation is written for file-based routing and is intended to help you understand in more detail how to configure file-based routing and the technical details behind how it works. While file-based routing is the preferred and recommended way to configure TanStack Router, you can also use [code-based routing](./code-based-routing.md) if you prefer.\n\n## What is File-Based Routing?\n\nFile-based routing is a way to configure your routes using the filesystem. Instead of defining your route structure via code, you can define your routes using a series of files and directories that represent the route hierarchy of your application. This brings a number of benefits:\n\n- **Simplicity**: File-based routing is visually intuitive and easy to understand for both new and experienced developers.\n- **Organization**: Routes are organized in a way that mirrors the URL structure of your application.\n- **Scalability**: As your application grows, file-based routing makes it easy to add new routes and maintain existing ones.\n- **Code-Splitting**: File-based routing allows TanStack Router to automatically code-split your routes for better performance.\n- **Type-Safety**: File-based routing raises the ceiling on type-safety by generating managing type linkages for your routes, which can otherwise be a tedious process via code-based routing.\n- **Consistency**: File-based routing enforces a consistent structure for your routes, making it easier to maintain and update your application and move from one project to another.\n\n## `/`s or `.`s?\n\nWhile directories have long been used to represent route hierarchy, file-based routing introduces an additional concept of using the `.` character in the file-name to denote a route nesting. This allows you to avoid creating directories for few deeply nested routes and continue to use directories for wider route hierarchies. Let's take a look at some examples!\n\n## Directory Routes\n\nDirectories can be used to denote route hierarchy, which can be useful for organizing multiple routes into logical groups and also cutting down on the filename length for large groups of deeply nested routes.\n\nSee the example below:\n\n| Filename | Route Path | Component Output |\n| ----------------------- | ------------------------- | --------------------------------- |\n| ʦ `__root.tsx` | | `\u003cRoot>` |\n| ʦ `index.tsx` | `/` (exact) | `\u003cRoot>\u003cRootIndex>` |\n| ʦ `about.tsx` | `/about` | `\u003cRoot>\u003cAbout>` |\n| ʦ `posts.tsx` | `/posts` | `\u003cRoot>\u003cPosts>` |\n| `posts` | | |\n| ┄ ʦ `index.tsx` | `/posts` (exact) | `\u003cRoot>\u003cPosts>\u003cPostsIndex>` |\n| ┄ ʦ `$postId.tsx` | `/posts/$postId` | `\u003cRoot>\u003cPosts>\u003cPost>` |\n| `posts_` | | |\n| ┄ `$postId` | | |\n| ┄ ┄ ʦ `edit.tsx` | `/posts/$postId/edit` | `\u003cRoot>\u003cEditPost>` |\n| ʦ `settings.tsx` | `/settings` | `\u003cRoot>\u003cSettings>` |\n| `settings` | | `\u003cRoot>\u003cSettings>` |\n| ┄ ʦ `profile.tsx` | `/settings/profile` | `\u003cRoot>\u003cSettings>\u003cProfile>` |\n| ┄ ʦ `notifications.tsx` | `/settings/notifications` | `\u003cRoot>\u003cSettings>\u003cNotifications>` |\n| ʦ `_pathlessLayout.tsx` | | `\u003cRoot>\u003cPathlessLayout>` |\n| `_pathlessLayout` | | |\n| ┄ ʦ `route-a.tsx` | `/route-a` | `\u003cRoot>\u003cPathlessLayout>\u003cRouteA>` |\n| ┄ ʦ `route-b.tsx` | `/route-b` | `\u003cRoot>\u003cPathlessLayout>\u003cRouteB>` |\n| `files` | | |\n| ┄ ʦ `$.tsx` | `/files/ tanstack-vue-router-skilld — Skillopedia | `\u003cRoot>\u003cFiles>` |\n| `account` | | |\n| ┄ ʦ `route.tsx` | `/account` | `\u003cRoot>\u003cAccount>` |\n| ┄ ʦ `overview.tsx` | `/account/overview` | `\u003cRoot>\u003cAccount>\u003cOverview>` |\n\n## Flat Routes\n\nFlat routing gives you the ability to use `.`s to denote route nesting levels.\n\nThis can be useful when you have a large number of uniquely deeply nested routes and want to avoid creating directories for each one:\n\nSee the example below:\n\n| Filename | Route Path | Component Output |\n| ------------------------------- | ------------------------- | --------------------------------- |\n| ʦ `__root.tsx` | | `\u003cRoot>` |\n| ʦ `index.tsx` | `/` (exact) | `\u003cRoot>\u003cRootIndex>` |\n| ʦ `about.tsx` | `/about` | `\u003cRoot>\u003cAbout>` |\n| ʦ `posts.tsx` | `/posts` | `\u003cRoot>\u003cPosts>` |\n| ʦ `posts.index.tsx` | `/posts` (exact) | `\u003cRoot>\u003cPosts>\u003cPostsIndex>` |\n| ʦ `posts.$postId.tsx` | `/posts/$postId` | `\u003cRoot>\u003cPosts>\u003cPost>` |\n| ʦ `posts_.$postId.edit.tsx` | `/posts/$postId/edit` | `\u003cRoot>\u003cEditPost>` |\n| ʦ `settings.tsx` | `/settings` | `\u003cRoot>\u003cSettings>` |\n| ʦ `settings.profile.tsx` | `/settings/profile` | `\u003cRoot>\u003cSettings>\u003cProfile>` |\n| ʦ `settings.notifications.tsx` | `/settings/notifications` | `\u003cRoot>\u003cSettings>\u003cNotifications>` |\n| ʦ `_pathlessLayout.tsx` | | `\u003cRoot>\u003cPathlessLayout>` |\n| ʦ `_pathlessLayout.route-a.tsx` | `/route-a` | `\u003cRoot>\u003cPathlessLayout>\u003cRouteA>` |\n| ʦ `_pathlessLayout.route-b.tsx` | `/route-b` | `\u003cRoot>\u003cPathlessLayout>\u003cRouteB>` |\n| ʦ `files.$.tsx` | `/files/ tanstack-vue-router-skilld — Skillopedia | `\u003cRoot>\u003cFiles>` |\n| ʦ `account.tsx` | `/account` | `\u003cRoot>\u003cAccount>` |\n| ʦ `account.overview.tsx` | `/account/overview` | `\u003cRoot>\u003cAccount>\u003cOverview>` |\n\n## Mixed Flat and Directory Routes\n\nIt's extremely likely that a 100% directory or flat route structure won't be the best fit for your project, which is why TanStack Router allows you to mix both flat and directory routes together to create a route tree that uses the best of both worlds where it makes sense:\n\nSee the example below:\n\n| Filename | Route Path | Component Output |\n| ------------------------------ | ------------------------- | --------------------------------- |\n| ʦ `__root.tsx` | | `\u003cRoot>` |\n| ʦ `index.tsx` | `/` (exact) | `\u003cRoot>\u003cRootIndex>` |\n| ʦ `about.tsx` | `/about` | `\u003cRoot>\u003cAbout>` |\n| ʦ `posts.tsx` | `/posts` | `\u003cRoot>\u003cPosts>` |\n| `posts` | | |\n| ┄ ʦ `index.tsx` | `/posts` (exact) | `\u003cRoot>\u003cPosts>\u003cPostsIndex>` |\n| ┄ ʦ `$postId.tsx` | `/posts/$postId` | `\u003cRoot>\u003cPosts>\u003cPost>` |\n| ┄ ʦ `$postId.edit.tsx` | `/posts/$postId/edit` | `\u003cRoot>\u003cPosts>\u003cPost>\u003cEditPost>` |\n| ʦ `settings.tsx` | `/settings` | `\u003cRoot>\u003cSettings>` |\n| ʦ `settings.profile.tsx` | `/settings/profile` | `\u003cRoot>\u003cSettings>\u003cProfile>` |\n| ʦ `settings.notifications.tsx` | `/settings/notifications` | `\u003cRoot>\u003cSettings>\u003cNotifications>` |\n| ʦ `account.tsx` | `/account` | `\u003cRoot>\u003cAccount>` |\n| ʦ `account.overview.tsx` | `/account/overview` | `\u003cRoot>\u003cAccount>\u003cOverview>` |\n\nBoth flat and directory routes can be mixed together to create a route tree that uses the best of both worlds where it makes sense.\n\n> [!TIP]\n> If you find that the default file-based routing structure doesn't fit your needs, you can always use [Virtual File Routes](./virtual-file-routes.md) to control the source of your routes whilst still getting the awesome performance benefits of file-based routing.\n\n## Getting started with File-Based Routing\n\nTo get started with file-based routing, you'll need to configure your project's bundler to use the TanStack Router Plugin or the TanStack Router CLI.\n\nTo enable file-based routing, you'll need to be using React with a supported bundler. See if your bundler is listed in the configuration guides below.\n\n\n\n# React\n\n- [Installation with Vite](../installation/with-vite)\n- [Installation with Rspack/Rsbuild](../installation/with-rspack)\n- [Installation with Webpack](../installation/with-webpack)\n- [Installation with Esbuild](../installation/with-esbuild)\n\n# Solid\n\n- [Installation with Vite](../installation/with-vite)\n- [Installation with Rspack/Rsbuild](../installation/with-rspack)\n- [Installation with Webpack](../installation/with-webpack)\n- [Installation with Esbuild](../installation/with-esbuild)\n\n\n\nWhen using TanStack Router's file-based routing through one of the supported bundlers, our plugin will **automatically generate your route configuration through your bundler's dev and build processes**. It is the easiest way to use TanStack Router's route generation features.\n\nIf your bundler is not yet supported, you can reach out to us on Discord or GitHub to let us know.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10096,"content_sha256":"9d7372077bddaf51d633dbd22405aaaa01fb972de6ef39fa635a66bbb7b3d70c"},{"filename":"references/docs/router/routing/file-naming-conventions.md","content":"---\ntitle: File Naming Conventions\n---\n\nFile-based routing requires that you follow a few simple file naming conventions to ensure that your routes are generated correctly. The concepts these conventions enable are covered in detail in the [Route Trees & Nesting](./route-trees.md) guide.\n\n| Feature | Description |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`__root.tsx`** | The root route file must be named `__root.tsx` and must be placed in the root of the configured `routesDirectory`. |\n| **`.` Separator** | Routes can use the `.` character to denote a nested route. For example, `blog.post` will be generated as a child of `blog`. |\n| **` tanstack-vue-router-skilld — Skillopedia Token** | Route segments with the ` tanstack-vue-router-skilld — Skillopedia token are parameterized and will extract the value from the URL pathname as a route `param`. |\n| **`_` Prefix** | Route segments with the `_` prefix are considered to be pathless layout routes and will not be used when matching its child routes against the URL pathname. |\n| **`_` Suffix** | Route segments with the `_` suffix exclude the route from being nested under any parent routes. |\n| **`-` Prefix** | Files and folders with the `-` prefix are excluded from the route tree. They will not be added to the `routeTree.gen.ts` file and can be used to colocate logic in route folders. |\n| **`(folder)` folder name pattern** | A folder that matches this pattern is treated as a **route group**, preventing the folder from being included in the route's URL path. |\n| **`[x]` Escaping** | Square brackets escape special characters in filenames that would otherwise have routing meaning. For example, `script[.]js.tsx` becomes `/script.js` and `api[.]v1.tsx` becomes `/api.v1`. |\n| **`index` Token** | Route segments ending with the `index` token (before any file extensions) will match the parent route when the URL pathname matches the parent route exactly. This can be configured via the `indexToken` configuration option (supports both strings and regex patterns), see [options](../api/file-based-routing.md#indextoken). |\n| **`.route.tsx` File Type** | When using directories to organise routes, the `route` suffix can be used to create a route file at the directory's path. For example, `blog.post.route.tsx` or `blog/post/route.tsx` can be used as the route file for the `/blog/post` route. This can be configured via the `routeToken` configuration option (supports both strings and regex patterns), see [options](../api/file-based-routing.md#routetoken). |\n\n> ** Remember:** The file-naming conventions for your project could be affected by what [options](../api/file-based-routing.md) are configured.\n\n## Dynamic Path Params\n\nDynamic path params can be used in both flat and directory routes to create routes that can match a dynamic segment of the URL path. Dynamic path params are denoted by the ` tanstack-vue-router-skilld — Skillopedia character in the filename:\n\n| Filename | Route Path | Component Output |\n| --------------------- | ---------------- | --------------------- |\n| ... | ... | ... |\n| ʦ `posts.$postId.tsx` | `/posts/$postId` | `\u003cRoot>\u003cPosts>\u003cPost>` |\n\nWe'll learn more about dynamic path params in the [Path Params](../guide/path-params.md) guide.\n\n## Pathless Routes\n\nPathless routes wrap child routes with either logic or a component without requiring a URL path. Non-path routes are denoted by the `_` character in the filename:\n\n| Filename | Route Path | Component Output |\n| -------------- | ---------- | ---------------- |\n| ʦ `_app.tsx` | | |\n| ʦ `_app.a.tsx` | /a | `\u003cRoot>\u003cApp>\u003cA>` |\n| ʦ `_app.b.tsx` | /b | `\u003cRoot>\u003cApp>\u003cB>` |\n\nTo learn more about pathless routes, see the [Routing Concepts - Pathless Routes](./routing-concepts.md#pathless-layout-routes) guide.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6969,"content_sha256":"63c075dab4128d18713e025f53cbc4cefc28378f6fcfb3b9756b7401b7d6df74"},{"filename":"references/docs/router/routing/route-matching.md","content":"---\ntitle: Route Matching\n---\n\nRoute matching follows a consistent and predictable pattern. This guide will explain how route trees are matched.\n\nWhen TanStack Router processes your route tree, all of your routes are automatically sorted to match the most specific routes first. This means that regardless of the order your route tree is defined, routes will always be sorted in this order:\n\n- Index Route\n- Static Routes (most specific to least specific)\n- Dynamic Routes (longest to shortest)\n- Splat/Wildcard Routes\n\nConsider the following pseudo route tree:\n\n```\nRoot\n - blog\n - $postId\n - /\n - new\n - /\n - *\n - about\n - about/us\n```\n\nAfter sorting, this route tree will become:\n\n```\nRoot\n - /\n - about/us\n - about\n - blog\n - /\n - new\n - $postId\n - *\n```\n\nThis final order represents the order in which routes will be matched based on specificity.\n\nUsing that route tree, let's follow the matching process for a few different URLs:\n\n- `/blog`\n ```\n Root\n ❌ /\n ❌ about/us\n ❌ about\n ⏩ blog\n ✅ /\n - new\n - $postId\n - *\n ```\n- `/blog/my-post`\n ```\n Root\n ❌ /\n ❌ about/us\n ❌ about\n ⏩ blog\n ❌ /\n ❌ new\n ✅ $postId\n - *\n ```\n- `/`\n ```\n Root\n ✅ /\n - about/us\n - about\n - blog\n - /\n - new\n - $postId\n - *\n ```\n- `/not-a-route`\n ```\n Root\n ❌ /\n ❌ about/us\n ❌ about\n ❌ blog\n - /\n - new\n - $postId\n ✅ *\n ```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1496,"content_sha256":"c77c3398f8bf11c86e98f64ff48f7c290cd9519a104486d48350831e7075888b"},{"filename":"references/docs/router/routing/route-trees.md","content":"---\ntitle: Route Trees\n---\n\nTanStack Router uses a nested route tree to match up the URL with the correct component tree to render.\n\nTo build a route tree, TanStack Router supports:\n\n- [File-Based Routing](./file-based-routing.md)\n- [Code-Based Routing](./code-based-routing.md)\n\nBoth methods support the exact same core features and functionality, but **file-based routing requires less code for the same or better results**. For this reason, **file-based routing is the preferred and recommended way** to configure TanStack Router. Most of the documentation is written from the perspective of file-based routing.\n\n## Route Trees\n\nNested routing is a powerful concept that allows you to use a URL to render a nested component tree. For example, given the URL of `/blog/posts/123`, you could create a route hierarchy that looks like this:\n\n```tsx\n├── blog\n│ ├── posts\n│ │ ├── $postId\n```\n\nAnd render a component tree that looks like this:\n\n```tsx\n\u003cBlog>\n \u003cPosts>\n \u003cPost postId=\"123\" />\n \u003c/Posts>\n\u003c/Blog>\n```\n\nLet's take that concept and expand it out to a larger site structure, but with file-names now:\n\n```\n/routes\n├── __root.tsx\n├── index.tsx\n├── about.tsx\n├── posts/\n│ ├── index.tsx\n│ ├── $postId.tsx\n├── posts.$postId.edit.tsx\n├── settings/\n│ ├── profile.tsx\n│ ├── notifications.tsx\n├── _pathlessLayout/\n│ ├── route-a.tsx\n├── ├── route-b.tsx\n├── files/\n│ ├── $.tsx\n```\n\nThe above is a valid route tree configuration that can be used with TanStack Router! There's a lot of power and convention to unpack with file-based routing, so let's break it down a bit.\n\n## Route Tree Configuration\n\nRoute trees can be configured using a few different ways:\n\n- [Flat Routes](./file-based-routing.md#flat-routes)\n- [Directories](./file-based-routing.md#directory-routes)\n- [Mixed Flat Routes and Directories](./file-based-routing.md#mixed-flat-and-directory-routes)\n- [Virtual File Routes](./virtual-file-routes.md)\n- [Code-Based Routes](./code-based-routing.md)\n\nPlease be sure to check out the full documentation links above for each type of route tree, or just proceed to the next section to get started with file-based routing.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2279,"content_sha256":"a2ace762274d8917274cecfe74832834b4ebaef78b3ffbf58303b20c4c415c1a"},{"filename":"references/docs/router/routing/routing-concepts.md","content":"---\ntitle: Routing Concepts\n---\n\nTanStack Router supports a number of powerful routing concepts that allow you to build complex and dynamic routing systems with ease.\n\nEach of these concepts is useful and powerful, and we'll dive into each of them in the following sections.\n\n## Anatomy of a Route\n\nAll other routes, other than the [Root Route](#the-root-route), are configured using the `createFileRoute` function, which provides type safety when using file-based routing:\n\n\n\n# React\n\n```tsx title=\"src/routes/index.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/')({\n component: PostsComponent,\n})\n```\n\n# Solid\n\n```tsx title=\"src/routes/index.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({\n component: PostsComponent,\n})\n```\n\n\n\nThe `createFileRoute` function takes a single argument, the file-route's path as a string.\n\n** \"Wait, you're making me pass the path of the route file to `createFileRoute`?\"**\n\nYes! But don't worry, this path is **automatically written and managed by the router for you via the TanStack Router Bundler Plugin or Router CLI.** So, as you create new routes, move routes around or rename routes, the path will be updated for you automatically.\n\nThe reason for this pathname has everything to do with the magical type safety of TanStack Router. Without this pathname, TypeScript would have no idea what file we're in! (We wish TypeScript had a built-in for this, but they don't yet )\n\n## The Root Route\n\nThe root route is the top-most route in the entire tree and encapsulates all other routes as children.\n\n- It has no path\n- It is **always** matched\n- Its `component` is **always** rendered\n\nEven though it doesn't have a path, the root route has access to all of the same functionality as other routes including:\n\n- components\n- loaders\n- search param validation\n- etc.\n\nTo create a root route, call the `createRootRoute()` function and export it as the `Route` variable in your route file:\n\n\n\n# React\n\n```tsx\n// Standard root route\nimport { createRootRoute } from '@tanstack/react-router'\n\nexport const Route = createRootRoute()\n\n// Root route with Context\nimport { createRootRouteWithContext } from '@tanstack/react-router'\nimport type { QueryClient } from '@tanstack/react-query'\n\nexport interface MyRouterContext {\n queryClient: QueryClient\n}\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()\n```\n\n# Solid\n\n```tsx\n// Standard root route\nimport { createRootRoute } from '@tanstack/solid-router'\n\nexport const Route = createRootRoute()\n\n// Root route with Context\nimport { createRootRouteWithContext } from '@tanstack/solid-router'\nimport type { QueryClient } from '@tanstack/solid-query'\n\nexport interface MyRouterContext {\n queryClient: QueryClient\n}\nexport const Route = createRootRouteWithContext\u003cMyRouterContext>()\n```\n\n\n\nTo learn more about Context in TanStack Router, see the [Router Context](../guide/router-context.md) guide.\n\n## Basic Routes\n\nBasic routes match a specific path, for example `/about`, `/settings`, `/settings/notifications` are all basic routes, as they match the path exactly.\n\nLet's take a look at an `/about` route:\n\n\n\n# React\n\n```tsx title=\"src/routes/about.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/about')({\n component: AboutComponent,\n})\n\nfunction AboutComponent() {\n return \u003cdiv>About\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/about.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/about')({\n component: AboutComponent,\n})\n\nfunction AboutComponent() {\n return \u003cdiv>About\u003c/div>\n}\n```\n\n\n\nBasic routes are simple and straightforward. They match the path exactly and render the provided component.\n\n## Index Routes\n\nIndex routes specifically target their parent route when it is **matched exactly and no child route is matched**.\n\nLet's take a look at an index route for a `/posts` URL:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.index.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\n// Note the trailing slash, which is used to target index routes\nexport const Route = createFileRoute('/posts/')({\n component: PostsIndexComponent,\n})\n\nfunction PostsIndexComponent() {\n return \u003cdiv>Please select a post!\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.index.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\n// Note the trailing slash, which is used to target index routes\nexport const Route = createFileRoute('/posts/')({\n component: PostsIndexComponent,\n})\n\nfunction PostsIndexComponent() {\n return \u003cdiv>Please select a post!\u003c/div>\n}\n```\n\n\n\nThis route will be matched when the URL is `/posts` exactly.\n\n## Dynamic Route Segments\n\nRoute path segments that start with a ` tanstack-vue-router-skilld — Skillopedia followed by a label are dynamic and capture that section of the URL into the `params` object for use in your application. For example, a pathname of `/posts/123` would match the `/posts/$postId` route, and the `params` object would be `{ postId: '123' }`.\n\nThese params are then usable in your route's configuration and components! Let's look at a `posts.$postId.tsx` route:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts/$postId.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n // In a loader\n loader: ({ params }) => fetchPost(params.postId),\n // Or in a component\n component: PostComponent,\n})\n\nfunction PostComponent() {\n // In a component!\n const { postId } = Route.useParams()\n return \u003cdiv>Post ID: {postId}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n // In a loader\n loader: ({ params }) => fetchPost(params.postId),\n // Or in a component\n component: PostComponent,\n})\n\nfunction PostComponent() {\n // In a component!\n const { postId } = Route.useParams()\n return \u003cdiv>Post ID: {postId()}\u003c/div>\n}\n```\n\n\n\n> Dynamic segments work at **each** segment of the path. For example, you could have a route with the path of `/posts/$postId/$revisionId` and each ` tanstack-vue-router-skilld — Skillopedia segment would be captured into the `params` object.\n\n## Splat / Catch-All Routes\n\nA route with a path of only ` tanstack-vue-router-skilld — Skillopedia is called a \"splat\" route because it _always_ captures _any_ remaining section of the URL pathname from the ` tanstack-vue-router-skilld — Skillopedia to the end. The captured pathname is then available in the `params` object under the special `_splat` property.\n\nFor example, a route targeting the `files/ tanstack-vue-router-skilld — Skillopedia path is a splat route. If the URL pathname is `/files/documents/hello-world`, the `params` object would contain `documents/hello-world` under the special `_splat` property:\n\n```js\n{\n '_splat': 'documents/hello-world'\n}\n```\n\n> In v1 of the router, splat routes are also denoted with a `*` instead of a `_splat` key for backwards compatibility. This will be removed in v2.\n\n> Why use ` tanstack-vue-router-skilld — Skillopedia ? Thanks to tools like Remix, we know that despite `*`s being the most common character to represent a wildcard, they do not play nice with filenames or CLI tools, so just like them, we decided to use ` tanstack-vue-router-skilld — Skillopedia instead.\n\n## Optional Path Parameters\n\nOptional path parameters allow you to define route segments that may or may not be present in the URL. They use the `{-$paramName}` syntax and provide flexible routing patterns where certain parameters are optional.\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.{-$category}.tsx\"\n// The `-$category` segment is optional, so this route matches both `/posts` and `/posts/tech`\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/{-$category}')({\n component: PostsComponent,\n})\n\nfunction PostsComponent() {\n const { category } = Route.useParams()\n\n return \u003cdiv>{category ? `Posts in ${category}` : 'All Posts'}\u003c/div>\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.{-$category}.tsx\"\n// The `-$category` segment is optional, so this route matches both `/posts` and `/posts/tech`\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/posts/{-$category}')({\n component: PostsComponent,\n})\n\nfunction PostsComponent() {\n const { category } = Route.useParams()\n\n return \u003cdiv>{category ? `Posts in ${category()}` : 'All Posts'}\u003c/div>\n}\n```\n\n\n\nThis route will match both `/posts` (category is `undefined`) and `/posts/tech` (category is `\"tech\"`).\n\nYou can also define multiple optional parameters in a single route:\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.{-$category}.${-$slug}.tsx\"\n// The `-$category` segment is optional, so this route matches both `/posts` and `/posts/tech`\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/{-$category}/{-$slug}')({\n component: PostsComponent,\n})\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.{-$category}.${-$slug}.tsx\"\n// The `-$category` segment is optional, so this route matches both `/posts` and `/posts/tech`\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/posts/{-$category}/{-$slug}')({\n component: PostsComponent,\n})\n```\n\n\n\nThis route matches `/posts`, `/posts/tech`, and `/posts/tech/hello-world`.\n\n> Routes with optional parameters are ranked lower in priority than exact matches, ensuring that more specific routes like `/posts/featured` are matched before `/posts/{-$category}`.\n\n## Layout Routes\n\nLayout routes are used to wrap child routes with additional components and logic. They are useful for:\n\n- Wrapping child routes with a layout component\n- Enforcing a `loader` requirement before displaying any child routes\n- Validating and providing search params to child routes\n- Providing fallbacks for error components or pending elements to child routes\n- Providing shared context to all child routes\n- And more!\n\nLet's take a look at an example layout route called `app.tsx`:\n\n```\nroutes/\n├── app.tsx\n├── app.dashboard.tsx\n├── app.settings.tsx\n```\n\nIn the tree above, `app.tsx` is a layout route that wraps two child routes, `app.dashboard.tsx` and `app.settings.tsx`.\n\nThis tree structure is used to wrap the child routes with a layout component:\n\n\n\n# React\n\n```tsx title=\"src/routes/app.tsx\"\nimport { Outlet, createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/app')({\n component: AppLayoutComponent,\n})\n\nfunction AppLayoutComponent() {\n return (\n \u003cdiv>\n \u003ch1>App Layout\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/app.tsx\"\nimport { Outlet, createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/app')({\n component: AppLayoutComponent,\n})\n\nfunction AppLayoutComponent() {\n return (\n \u003cdiv>\n \u003ch1>App Layout\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n\n\nThe following table shows which component(s) will be rendered based on the URL:\n\n| URL Path | Component |\n| ---------------- | ------------------------ |\n| `/app` | `\u003cAppLayout>` |\n| `/app/dashboard` | `\u003cAppLayout>\u003cDashboard>` |\n| `/app/settings` | `\u003cAppLayout>\u003cSettings>` |\n\nSince TanStack Router supports mixed flat and directory routes, you can also express your application's routing using layout routes within directories:\n\n```\nroutes/\n├── app/\n│ ├── route.tsx\n│ ├── dashboard.tsx\n│ ├── settings.tsx\n```\n\nIn this nested tree, the `app/route.tsx` file is a configuration for the layout route that wraps two child routes, `app/dashboard.tsx` and `app/settings.tsx`.\n\nLayout Routes also let you enforce component and loader logic for Dynamic Route Segments:\n\n```\nroutes/\n├── app/users/\n│ ├── $userId/\n| | ├── route.tsx\n| | ├── index.tsx\n| | ├── edit.tsx\n```\n\n## Pathless Layout Routes\n\nLike [Layout Routes](#layout-routes), Pathless Layout Routes are used to wrap child routes with additional components and logic. However, pathless layout routes do not require a matching `path` in the URL and are used to wrap child routes with additional components and logic without requiring a matching `path` in the URL.\n\nPathless Layout Routes are prefixed with an underscore (`_`) to denote that they are \"pathless\".\n\n> The part of the path after the `_` prefix is used as the route's ID and is required because every route must be uniquely identifiable, especially when using TypeScript so as to avoid type errors and accomplish autocomplete effectively.\n\nLet's take a look at an example route called `_pathlessLayout.tsx`:\n\n```\n\nroutes/\n├── _pathlessLayout.tsx\n├── _pathlessLayout.a.tsx\n├── _pathlessLayout.b.tsx\n\n```\n\nIn the tree above, `_pathlessLayout.tsx` is a pathless layout route that wraps two child routes, `_pathlessLayout.a.tsx` and `_pathlessLayout.b.tsx`.\n\nThe `_pathlessLayout.tsx` route is used to wrap the child routes with a Pathless layout component:\n\n\n\n# React\n\n```tsx title=\"src/routes/_pathlessLayout.tsx\"\nimport { Outlet, createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_pathlessLayout')({\n component: PathlessLayoutComponent,\n})\n\nfunction PathlessLayoutComponent() {\n return (\n \u003cdiv>\n \u003ch1>Pathless layout\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/_pathlessLayout.tsx\"\nimport { Outlet, createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/_pathlessLayout')({\n component: PathlessLayoutComponent,\n})\n\nfunction PathlessLayoutComponent() {\n return (\n \u003cdiv>\n \u003ch1>Pathless layout\u003c/h1>\n \u003cOutlet />\n \u003c/div>\n )\n}\n```\n\n\n\nThe following table shows which component will be rendered based on the URL:\n\n| URL Path | Component |\n| -------- | --------------------- |\n| `/` | `\u003cIndex>` |\n| `/a` | `\u003cPathlessLayout>\u003cA>` |\n| `/b` | `\u003cPathlessLayout>\u003cB>` |\n\nSince TanStack Router supports mixed flat and directory routes, you can also express your application's routing using pathless layout routes within directories:\n\n```\nroutes/\n├── _pathlessLayout/\n│ ├── route.tsx\n│ ├── a.tsx\n│ ├── b.tsx\n```\n\nHowever, unlike Layout Routes, since Pathless Layout Routes do not match based on URL path segments, this means that these routes do not support [Dynamic Route Segments](#dynamic-route-segments) as part of their path and therefore cannot be matched in the URL.\n\nThis means that you cannot do this:\n\n```\nroutes/\n├── _$postId/ ❌\n│ ├── ...\n```\n\nRather, you'd have to do this:\n\n```\nroutes/\n├── $postId/\n├── _postPathlessLayout/ ✅\n│ ├── ...\n```\n\n## Non-Nested Routes\n\nNon-nested routes can be created by suffixing a parent file route segment with a `_` and are used to **un-nest** a route from its parents and render its own component tree.\n\nConsider the following flat route tree:\n\n```\nroutes/\n├── posts.tsx\n├── posts.$postId.tsx\n├── posts_.$postId.edit.tsx\n```\n\nThe following table shows which component will be rendered based on the URL:\n\n| URL Path | Component |\n| ----------------- | ---------------------------- |\n| `/posts` | `\u003cPosts>` |\n| `/posts/123` | `\u003cPosts>\u003cPost postId=\"123\">` |\n| `/posts/123/edit` | `\u003cPostEditor postId=\"123\">` |\n\n- The `posts.$postId.tsx` route is nested as normal under the `posts.tsx` route and will render `\u003cPosts>\u003cPost>`.\n- The `posts_.$postId.edit.tsx` route **does not share** the same `posts` prefix as the other routes and therefore will be treated as if it is a top-level route and will render `\u003cPostEditor>`.\n\n## Excluding Files and Folders from Routes\n\nFiles and folders can be excluded from route generation with a `-` prefix attached to the file name. This gives you the ability to colocate logic in the route directories.\n\nConsider the following route tree:\n\n```\nroutes/\n├── posts.tsx\n├── -posts-table.tsx // 👈🏼 ignored\n├── -components/ // 👈🏼 ignored\n│ ├── header.tsx // 👈🏼 ignored\n│ ├── footer.tsx // 👈🏼 ignored\n│ ├── ...\n```\n\nWe can import from the excluded files into our posts route\n\n\n\n# React\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/react-router'\nimport { PostsTable } from './-posts-table'\nimport { PostsHeader } from './-components/header'\nimport { PostsFooter } from './-components/footer'\n\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const posts = Route.useLoaderData()\n\n return (\n \u003cdiv>\n \u003cPostsHeader />\n \u003cPostsTable posts={posts} />\n \u003cPostsFooter />\n \u003c/div>\n )\n}\n```\n\n# Solid\n\n```tsx title=\"src/routes/posts.tsx\"\nimport { createFileRoute } from '@tanstack/solid-router'\nimport { PostsTable } from './-posts-table'\nimport { PostsHeader } from './-components/header'\nimport { PostsFooter } from './-components/footer'\n\nexport const Route = createFileRoute('/posts')({\n loader: () => fetchPosts(),\n component: PostComponent,\n})\n\nfunction PostComponent() {\n const posts = Route.useLoaderData()\n\n return (\n \u003cdiv>\n \u003cPostsHeader />\n \u003cPostsTable posts={posts} />\n \u003cPostsFooter />\n \u003c/div>\n )\n}\n```\n\n\n\nThe excluded files will not be added to `routeTree.gen.ts`.\n\n## Pathless Route Group Directories\n\nPathless route group directories use `()` as a way to group routes files together regardless of their path. They are purely organizational and do not affect the route tree or component tree in any way.\n\n```\nroutes/\n├── index.tsx\n├── (app)/\n│ ├── dashboard.tsx\n│ ├── settings.tsx\n│ ├── users.tsx\n├── (auth)/\n│ ├── login.tsx\n│ ├── register.tsx\n```\n\nIn the example above, the `app` and `auth` directories are purely organizational and do not affect the route tree or component tree in any way. They are used to group related routes together for easier navigation and organization.\n\nThe following table shows which component will be rendered based on the URL:\n\n| URL Path | Component |\n| ------------ | ------------- |\n| `/` | `\u003cIndex>` |\n| `/dashboard` | `\u003cDashboard>` |\n| `/settings` | `\u003cSettings>` |\n| `/users` | `\u003cUsers>` |\n| `/login` | `\u003cLogin>` |\n| `/register` | `\u003cRegister>` |\n\nAs you can see, the `app` and `auth` directories are purely organizational and do not affect the route tree or component tree in any way.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18569,"content_sha256":"2506f7e12b455ade79452716e1d055710a3c14748b079ddae4d17cdd36220f40"},{"filename":"references/docs/router/routing/virtual-file-routes.md","content":"---\nid: virtual-file-routes\ntitle: Virtual File Routes\n---\n\n> We'd like to thank the Remix team for pioneering the concept of virtual file routes. We've taken inspiration from their work and adapted it to work with TanStack Router's existing file-based route-tree generation.\n\nVirtual file routes are a powerful concept that allows you to build a route tree programmatically using code that references real files in your project. This can be useful if:\n\n- You have an existing route organization that you want to keep.\n- You want to customize the location of your route files.\n- You want to completely override TanStack Router's file-based route generation and build your own convention.\n\nHere's a quick example of using virtual file routes to map a route tree to a set of real files in your project:\n\n```tsx\n// routes.ts\nimport {\n rootRoute,\n route,\n index,\n layout,\n physical,\n} from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('root.tsx', [\n index('index.tsx'),\n layout('pathlessLayout.tsx', [\n route('/dashboard', 'app/dashboard.tsx', [\n index('app/dashboard-index.tsx'),\n route('/invoices', 'app/dashboard-invoices.tsx', [\n index('app/invoices-index.tsx'),\n route('$id', 'app/invoice-detail.tsx'),\n ]),\n ]),\n physical('/posts', 'posts'),\n ]),\n])\n```\n\n## Configuration\n\nVirtual file routes can be configured either via:\n\n- The `TanStackRouter` plugin for Vite/Rspack/Webpack\n- The `tsr.config.json` file for the TanStack Router CLI\n\n## Configuration via the TanStackRouter Plugin\n\nIf you're using the `TanStackRouter` plugin for Vite/Rspack/Webpack, you can configure virtual file routes by passing the path of your routes file to the `virtualRoutesConfig` option when setting up the plugin:\n\n\n\n# React\n\n```tsx title=\"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 target: 'react',\n virtualRouteConfig: './routes.ts',\n }),\n react(),\n ],\n})\n```\n\n# Solid\n\n```tsx title=\"vite.config.ts\"\nimport { defineConfig } from 'vite'\nimport solid from 'vite-plugin-solid'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({\n target: 'solid',\n virtualRouteConfig: './routes.ts',\n }),\n solid(),\n ],\n})\n```\n\n\n\nOr, you choose to define the virtual routes directly in the configuration:\n\n```tsx\n// vite.config.ts\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\nimport { rootRoute } from '@tanstack/virtual-file-routes'\n\nconst routes = rootRoute('root.tsx', [\n // ... the rest of your virtual route tree\n])\n\nexport default defineConfig({\n plugins: [tanstackRouter({ virtualRouteConfig: routes }), react()],\n})\n```\n\n\n\n# React\n\n```tsx title=\"vite.config.ts\"\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nconst routes = rootRoute('root.tsx', [\n // ... the rest of your virtual route tree\n])\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({ virtualRouteConfig: routes, target: 'react' }),\n react(),\n ],\n})\n```\n\n# Solid\n\n```tsx title=\"vite.config.ts\"\nimport { defineConfig } from 'vite'\nimport solid from 'vite-plugin-solid'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n\nconst routes = rootRoute('root.tsx', [\n // ... the rest of your virtual route tree\n])\n\nexport default defineConfig({\n plugins: [\n tanstackRouter({ virtualRouteConfig: routes, target: 'solid' }),\n solid(),\n ],\n})\n```\n\n\n\n## Creating Virtual File Routes\n\nTo create virtual file routes, you'll need to import the `@tanstack/virtual-file-routes` package. This package provides a set of functions that allow you to create virtual routes that reference real files in your project. A few utility functions are exported from the package:\n\n- `rootRoute` - Creates a virtual root route.\n- `route` - Creates a virtual route.\n- `index` - Creates a virtual index route.\n- `layout` - Creates a virtual pathless layout route.\n- `physical` - Creates a physical virtual route (more on this later).\n\n## Virtual Root Route\n\nThe `rootRoute` function is used to create a virtual root route. It takes a file name and an array of children routes. Here's an example of a virtual root route:\n\n```tsx\n// routes.ts\nimport { rootRoute } from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('root.tsx', [\n // ... children routes\n])\n```\n\n## Virtual Route\n\nThe `route` function is used to create a virtual route. It takes a path, a file name, and an array of children routes. Here's an example of a virtual route:\n\n```tsx\n// routes.ts\nimport { route } from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('root.tsx', [\n route('/about', 'about.tsx', [\n // ... children routes\n ]),\n])\n```\n\nYou can also define a virtual route without a file name. This allows to set a common path prefix for its children:\n\n```tsx\n// routes.ts\nimport { route } from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('root.tsx', [\n route('/hello', [\n route('/world', 'world.tsx'), // full path will be \"/hello/world\"\n route('/universe', 'universe.tsx'), // full path will be \"/hello/universe\"\n ]),\n])\n```\n\n## Virtual Index Route\n\nThe `index` function is used to create a virtual index route. It takes a file name. Here's an example of a virtual index route:\n\n```tsx\nimport { index } from '@tanstack/virtual-file-routes'\n\nconst routes = rootRoute('root.tsx', [index('index.tsx')])\n```\n\n## Virtual Pathless Route\n\nThe `layout` function is used to create a virtual pathless route. It takes a file name, an array of children routes, and an optional pathless ID. Here's an example of a virtual pathless route:\n\n```tsx\n// routes.ts\nimport { layout } from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('root.tsx', [\n layout('pathlessLayout.tsx', [\n // ... children routes\n ]),\n])\n```\n\nYou can also specify a pathless ID to give the route a unique identifier that is different from the filename:\n\n```tsx\n// routes.ts\nimport { layout } from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('root.tsx', [\n layout('my-pathless-layout-id', 'pathlessLayout.tsx', [\n // ... children routes\n ]),\n])\n```\n\n## Physical Virtual Routes\n\nPhysical virtual routes are a way to \"mount\" a directory of good ol' TanStack Router File Based routing convention under a specific URL path. This can be useful if you are using virtual routes to customize a small portion of your route tree high up in the hierarchy, but want to use the standard file-based routing convention for sub-routes and directories.\n\nConsider the following file structure:\n\n```\n/routes\n├── root.tsx\n├── index.tsx\n├── pathlessLayout.tsx\n├── app\n│ ├── dashboard.tsx\n│ ├── dashboard-index.tsx\n│ ├── dashboard-invoices.tsx\n│ ├── invoices-index.tsx\n│ ├── invoice-detail.tsx\n└── posts\n ├── index.tsx\n ├── $postId.tsx\n ├── $postId.edit.tsx\n ├── comments/\n │ ├── index.tsx\n │ ├── $commentId.tsx\n └── likes/\n ├── index.tsx\n ├── $likeId.tsx\n```\n\nLet's use virtual routes to customize our route tree for everything but `posts`, then use physical virtual routes to mount the `posts` directory under the `/posts` path:\n\n```tsx\n// routes.ts\nexport const routes = rootRoute('root.tsx', [\n // Set up your virtual routes as normal\n index('index.tsx'),\n layout('pathlessLayout.tsx', [\n route('/dashboard', 'app/dashboard.tsx', [\n index('app/dashboard-index.tsx'),\n route('/invoices', 'app/dashboard-invoices.tsx', [\n index('app/invoices-index.tsx'),\n route('$id', 'app/invoice-detail.tsx'),\n ]),\n ]),\n // Mount the `posts` directory under the `/posts` path\n physical('/posts', 'posts'),\n ]),\n])\n```\n\n### Merging Physical Routes at Current Level\n\nYou can also use `physical` with an empty path prefix (or a single argument) to merge routes from a physical directory directly at the current level, without adding a path prefix. This is useful when you want to organize your routes into separate directories but have them appear at the same URL level.\n\nConsider the following file structure:\n\n```\n/routes\n├── __root.tsx\n├── about.tsx\n└── features\n ├── index.tsx\n └── contact.tsx\n```\n\nYou can merge the `features` directory routes at the root level:\n\n```tsx\n// routes.ts\nimport { physical, rootRoute, route } from '@tanstack/virtual-file-routes'\n\nexport const routes = rootRoute('__root.tsx', [\n route('/about', 'about.tsx'),\n // Merge features/ routes at root level (no path prefix)\n physical('features'),\n // Or equivalently: physical('', 'features')\n])\n```\n\nThis will produce the following routes:\n\n- `/about` - from `about.tsx`\n- `/` - from `features/index.tsx`\n- `/contact` - from `features/contact.tsx`\n\n> **Note:** When merging at the same level, ensure there are no conflicting route paths between your virtual routes and the physical directory routes. If a conflict occurs (e.g., both have an `/about` route), the generator will throw an error.\n\n## Virtual Routes inside of TanStack Router File Based routing\n\nThe previous section showed you how you can use TanStack Router's File Based routing convention inside of a virtual route configuration.\nHowever, the opposite is possible as well. \nYou can configure the main part of your app's route tree using TanStack Router's File Based routing convention and opt into virtual route configuration for specific subtrees.\n\nConsider the following file structure:\n\n```\n/routes\n├── __root.tsx\n├── foo\n│ ├── bar\n│ │ ├── __virtual.ts\n│ │ ├── details.tsx\n│ │ ├── home.tsx\n│ │ └── route.ts\n│ └── bar.tsx\n└── index.tsx\n```\n\nLet's look at the `bar` directory which contains a special file named `__virtual.ts`. This file instructs the generator to switch over to virtual file route configuration for this directory (and its child directories).\n\n`__virtual.ts` configures the virtual routes for that particular subtree of the route tree. It uses the same API as explained above, with the only difference being that no `rootRoute` is defined for that subtree:\n\n```tsx\n// routes/foo/bar/__virtual.ts\nimport {\n defineVirtualSubtreeConfig,\n index,\n route,\n} from '@tanstack/virtual-file-routes'\n\nexport default defineVirtualSubtreeConfig([\n index('home.tsx'),\n route('$id', 'details.tsx'),\n])\n```\n\nThe helper function `defineVirtualSubtreeConfig` is closely modeled after vite's `defineConfig` and allows you to define a subtree configuration via a default export. The default export can either be\n\n- a subtree config object\n- a function returning a subtree config object\n- an async function returning a subtree config object\n\n## Inception\n\nYou can mix and match TanStack Router's File Based routing convention and virtual route configuration however you like. \nLet's go deeper! \nCheck out the following example that starts off using File Based routing convention, switches over to virtual route configuration for `/posts`, switches back to File Based routing convention for `/posts/lets-go` only to switch over to virtual route configuration again for `/posts/lets-go/deeper`.\n\n```\n├── __root.tsx\n├── index.tsx\n├── posts\n│ ├── __virtual.ts\n│ ├── details.tsx\n│ ├── home.tsx\n│ └── lets-go\n│ ├── deeper\n│ │ ├── __virtual.ts\n│ │ └── home.tsx\n│ └── index.tsx\n└── posts.tsx\n```\n\n## Configuration via the TanStack Router CLI\n\nIf you're using the TanStack Router CLI, you can configure virtual file routes by defining the path to your routes file in the `tsr.config.json` file:\n\n```json\n// tsr.config.json\n{\n \"virtualRouteConfig\": \"./routes.ts\"\n}\n```\n\nOr you can define the virtual routes directly in the configuration, while much less common allows you to configure them via the TanStack Router CLI by adding a `virtualRouteConfig` object to your `tsr.config.json` file and defining your virtual routes and passing the resulting JSON that is generated by calling the actual `rootRoute`/`route`/`index`/etc functions from the `@tanstack/virtual-file-routes` package:\n\n```json\n// tsr.config.json\n{\n \"virtualRouteConfig\": {\n \"type\": \"root\",\n \"file\": \"root.tsx\",\n \"children\": [\n {\n \"type\": \"index\",\n \"file\": \"home.tsx\"\n },\n {\n \"type\": \"route\",\n \"file\": \"posts/posts.tsx\",\n \"path\": \"/posts\",\n \"children\": [\n {\n \"type\": \"index\",\n \"file\": \"posts/posts-home.tsx\"\n },\n {\n \"type\": \"route\",\n \"file\": \"posts/posts-detail.tsx\",\n \"path\": \"$postId\"\n }\n ]\n },\n {\n \"type\": \"layout\",\n \"id\": \"first\",\n \"file\": \"layout/first-pathless-layout.tsx\",\n \"children\": [\n {\n \"type\": \"layout\",\n \"id\": \"second\",\n \"file\": \"layout/second-pathless-layout.tsx\",\n \"children\": [\n {\n \"type\": \"route\",\n \"file\": \"a.tsx\",\n \"path\": \"/route-a\"\n },\n {\n \"type\": \"route\",\n \"file\": \"b.tsx\",\n \"path\": \"/route-b\"\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13760,"content_sha256":"757dabf02bb5b12b67f5baf328745639295574b589d432aea39dd4c3c20d1599"},{"filename":"references/issues/_INDEX.md","content":"---\ntotal: 30\nopen: 16\nclosed: 14\n---\n\n# Issues Index\n\n## Other (27)\n\n- [#3023](./issue-3023.md): How to use CSS Modules with TanStack Start? (+57) [closed] (2024-12-16)\n- [#3997](./issue-3997.md): Performance is horrible when using recommended Authentication patterns (+46) (2025-04-15)\n- [#4010](./issue-4010.md): Error \"Invariant failed: Could not find match for from\" when layout + file-based routing is used (+36) [closed] (2025-04-17)\n- [#3468](./issue-3468.md): Cloudflare Env Vars Not Passed to SSR in TanStack Start (+35) [closed] (2025-02-18)\n- [#6483](./issue-6483.md): @tanstack/router-is-server not published to npm - breaks 1.157.0 installs (+22) [closed] (2026-01-24)\n- [#5967](./issue-5967.md): nitro + spa mode breaks since v1.138.0 (+24) [closed] (2025-11-26)\n- [#5217](./issue-5217.md): Start RC - Environment Variables aren't loaded automatically (+26) [closed] (2025-09-24)\n- [#5469](./issue-5469.md): Improve start error message when importing server code on the client (+23) [closed] (2025-10-13)\n- [#1992](./issue-1992.md): [TanStack Start] HMR is not working from root route (__root) (+37) [closed] (2024-07-19)\n- [#5673](./issue-5673.md): ReferenceError: Cannot access '__vite_ssr_import_2__' before initialization (+21) [closed] (2025-10-28)\n- [#597](./issue-597.md): Beta.87 Link Path Changes but Page Content Fails to Load (+47) [closed] (2023-05-08)\n- [#3462](./issue-3462.md): Error thrown from `beforeLoad` and handled through `errorComponent` is ignored on initial request (+28) [closed] (2025-02-17)\n- [#4473](./issue-4473.md): Start: Full support by `@cloudflare/vite-plugin` (+24) [closed] (2025-06-19)\n- [#4729](./issue-4729.md): @tanstack/start-storage-context seem wrong bundled in client side code that cause a node:async_hooks.AsyncLocalStorage bug (+22) [closed] (2025-07-21)\n- [#1120](./issue-1120.md): Documentation Search Functionality Incorrectly Redirecting to Overview Page (+32) [closed] (2024-01-31)\n- [#2072](./issue-2072.md): Router context invalidate Promise resolve does not invalidate context (+27) (2024-08-01)\n- [#5738](./issue-5738.md): Some server-side modules incorrectly bundled into the client? (+16) (2025-11-03)\n- [#4988](./issue-4988.md): vite-plugin-pwa incompatible with tanstack start production builds (+16) (2025-08-17)\n- [#2783](./issue-2783.md): Start - Server Function Middleware Included in Client Bundle (+21) (2024-11-16)\n- [#4476](./issue-4476.md): Tanstack Router (with Tanstack Query) throws CancelledError when navigating between pages (+16) (2025-06-19)\n- [#3804](./issue-3804.md): Page doesn't scroll to top after navigation (+16) (2025-03-18)\n- [#5774](./issue-5774.md): TanStack Start + Sentry add-on not working (+12) (2025-11-06)\n- [#2845](./issue-2845.md): Router searchParameter gets reset to default value (of zod schema) after navigations (and using retainSearchParams) (+17) (2024-11-24)\n- [#5795](./issue-5795.md): Could not resolve \"#tanstack-router-entry\" (+11) (2025-11-08)\n- [#4973](./issue-4973.md): Search Params as Actual State (+12) (2025-08-16)\n- [#3110](./issue-3110.md): `useLocation` re-renders before navigating (+15) (2025-01-04)\n- [#4499](./issue-4499.md): useMatchRoute not updating when using React Compiler (+11) (2025-06-22)\n\n## Feature Requests (3)\n\n- [#4901](./issue-4901.md): Link should accept external links (+34) (2025-08-09)\n- [#918](./issue-918.md): Accessibility (+47) (2024-01-04)\n- [#5704](./issue-5704.md): Support streaming file uploads in server functions (+7) (2025-10-30)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3499,"content_sha256":"d61471b1869fe98501144ad74226022fcfd96531d5bd71e5a59a023923682f19"},{"filename":"references/issues/issue-1120.md","content":"---\nnumber: 1120\ntitle: Documentation Search Functionality Incorrectly Redirecting to Overview Page\ntype: other\nstate: closed\ncreated: 2024-01-31\nurl: \"https://github.com/TanStack/router/issues/1120\"\nreactions: 32\ncomments: 4\n---\n\n# Documentation Search Functionality Incorrectly Redirecting to Overview Page\n\n### Describe the bug\n\nOn the website documentation, using the algolia search bar to jump to different parts of the documentation leads to an incorrect redirect.\n\n### Your Example Website or App\n\nhttps://tanstack.com/router/v1/docs/framework/react/overview\n\n### Steps to Reproduce the Bug or Issue\n\n1. Go to the docs\n2. Hit CMD-K (or CTRL-K) to open the search bar\n3. search for something like Search Params\n4. You will be redirected to the Overview page.\n\n### Expected behavior\n\nThe expected behavior is to see the Search Params Page, rather than the Overview Page.\n\n### Screenshots or Videos\n\n\u003cimg width=\"853\" alt=\"image\" src=\"https://github.com/TanStack/router/assets/55081439/4cf99efb-b47b-4c9f-9170-f91b5c03350c\">\n\n\n### Platform\n\n- OS: [macOS]\n- Browser: [Edge]\n\n\n### Additional context\n\n_No response_","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1115,"content_sha256":"9743a89e6518bbe2b953728c773a61e6bbc66ae49a0ddb663849f4c8ed2dfcf2"},{"filename":"references/issues/issue-1992.md","content":"---\nnumber: 1992\ntitle: \"[TanStack Start] HMR is not working from root route (__root)\"\ntype: other\nstate: closed\ncreated: 2024-07-19\nurl: \"https://github.com/TanStack/router/issues/1992\"\nreactions: 37\ncomments: 47\nlabels: \"[start]\"\n---\n\n# [TanStack Start] HMR is not working from root route (__root)\n\n### Describe the bug\n\nHMR is not working in root route. only working from normal routes.\n\n### Your Example Website or App\n\nhttps://github.com/TanStack/router/tree/main/examples/react/start-basic-rsc\n\n### Steps to Reproduce the Bug or Issue\n\n1. Clone (or copy or degit) start-basic-rsc example, install dependencies and run dev server.\n2. Open http://localhost:3000\n3. Try to edit from `__root.tsx`. I just added a link beside Home.\n\n### Expected behavior\n\nIn terminal, dev server printed a log about page has reloaded.\n```\n[vite] page reload app/routes/__root.tsx\n```\n\nbut the changes are not reflected in the browser.\n\n### Screenshots or Videos\n\n\nhttps://github.com/user-attachments/assets/26aaa1a3-80c1-40af-8dd8-f2403051519e\n\n\n\n### Platform\n\n- OS: macOs\n- Browser: Arc / Chrome\n- Version: 126.0.6478.127\n\n\n### Additional context\n\n_No response_\n\n---\n\n## Top Comments\n\n**@KiwiKilian** (+19):\n\nTo summarize, we see the following HMR problems with file based routing:\n\n## HMR is not triggered when editing\n- `routes/__root.tsx`\n- `components/Example.tsx` imported into any `routes/**.tsx` file\n- `routes/_layout.tsx`\n\n## HMR is triggered when editing\n- `routes/index.tsx`\n- `routes/example.example.tsx` (@anulman are you sure this doesn't work for any plain route (no `_` prefix) file?)\n- `routes/with-directory/example.tsx`\n\n...\n\n**@anulman** (+9):\n\nI'm experiencing this as well, not just in my root route but in any top-level route.\n\nI also have not been able to separate out `components/Foo.tsx` files and have them hot-reload. The dev console & JS console confirm the HMR event has been emitted and received, but the React JS—even for route sub-components modified in non-route files (e.g. `import { FooPage } from '~/components/FooPage';`) does not re-render the HMR'ed component.\n\n**@ChrisEdgington** (+5):\n\nWe just added TanStack router to our vite-based project, and now we are also experiencing this HMR failure. I tried a few different routes, it does seem to only be happening with __root.tsx.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2308,"content_sha256":"6b763631683593003894e2757915b457a748272472c0379f3ce5f6b4e574453f"},{"filename":"references/issues/issue-2072.md","content":"---\nnumber: 2072\ntitle: Router context invalidate Promise resolve does not invalidate context\ntype: other\nstate: open\ncreated: 2024-08-01\nurl: \"https://github.com/TanStack/router/issues/2072\"\nreactions: 27\ncomments: 24\n---\n\n# Router context invalidate Promise resolve does not invalidate context\n\n### Describe the bug\n\nIn the code below, which was suppose to invalidate the context and navigate to the following route. for context i'm following the example given in tanstack authenticated routes example. Even after navigation the context isn't invalidated which I can see in console log. It results in redirect to login page not working properly after clicking logout and just stuck in the \"/\" page. \n\n`router.invalidate().finally(() => {\n navigate({ to: \"/\" });\n });`\n\nIt only invalidates or I can see it invalidates when I manually reload the page, which means during the navigation the context doesn't invalidate.\n\nPS: I do have an api request in the logout function.\n\n### Your Example Website or App\n\n...\n\n### Steps to Reproduce the Bug or Issue\n\nFollowing the tanstack example locally.\n\nhttps://stackblitz.com/edit/tanstack-router-4achrw?file=src%2Froutes%2F_auth.tsx\n\n### Expected behavior\nRouter context should've been already invalid in .finally or .then even before navigating.\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- OS: [e.g. macOS, Windows, Linux]\n- Browser: [e.g. Chrome, Safari, Firefox]\n- Version: [e.g. 91.1]\n\n\n### Additional context\n\n_No response_","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1501,"content_sha256":"6ca34f6cd0153e39cacd937005bd21f000cc4d662cc412a4a501f3b1c007c396"},{"filename":"references/issues/issue-2783.md","content":"---\nnumber: 2783\ntitle: Start - Server Function Middleware Included in Client Bundle\ntype: other\nstate: open\ncreated: 2024-11-16\nurl: \"https://github.com/TanStack/router/issues/2783\"\nreactions: 21\ncomments: 33\nlabels: \"[start, revisit-after-devinxi]\"\n---\n\n# Start - Server Function Middleware Included in Client Bundle\n\n### Which project does this relate to?\n\nStart\n\n### Describe the bug\n\nCalling a serverFn using middleware in the client with `useServerFn` results in server side code being included in client bundle.\n\nI'll try to get a minimal reproduction later this weekend or early next week. But here is an example of the issue. \n\n### Your Example Website or App\n\nStackBlitz Reproduction Link\n\nIn this example, `app/routes/index.tsx` contains an client only middleware that causes the issue. If you uncomment the server section the error will go away.\n\n### Steps to Reproduce the Bug or Issue\n\n\n\nBasic Example:\n```ts\nimport { getSupabaseServerClient } from \"@/features/supabase/utils\";\nimport { createServerFn, createMiddleware } from \"@tanstack/start\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { z } from 'zod'\n\n\nexport const authMiddleware = createMiddleware().server(async ({ next }) => {\n const supa = getSupabaseServerClient();\n const user = await supa.auth.getUser();\n return next({ context: { user } });\n});\n\nconst serverFilters = z.object({ })\n\n\nconst getQuery = createServerFn({ method: \"GET\" })\n .middleware([authMiddleware])\n .validator((input: unknown) => serverFilters.parse(input))\n .handler(async ({ data, context }) => {\n ...\n });\n\nfunction useQueryData(data: any) {\n const getData = useServerFn(getQuery);\n return useQuery({\n queryKey: [\"id\", JSON.stringify(filters)],\n queryFn: () => getData({ data: filters }),\n });\n \n}\n\n```\n\n\n\n\nClient Side Error:\nI see a request for the file \"@/features/supabase/utils\", and get this console error.\n\n...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1912,"content_sha256":"2b25036665570093c6d7cf9dd407bf48b5bb1b3ec110a0b0e70cb1db85874fc6"},{"filename":"references/issues/issue-2845.md","content":"---\nnumber: 2845\ntitle: Router searchParameter gets reset to default value (of zod schema) after navigations (and using retainSearchParams)\ntype: other\nstate: open\ncreated: 2024-11-24\nurl: \"https://github.com/TanStack/router/issues/2845\"\nreactions: 17\ncomments: 15\n---\n\n# Router searchParameter gets reset to default value (of zod schema) after navigations (and using retainSearchParams)\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nUsing retained searchParams, if we change the retained param `x` form `a` to `b` on some route (lets say route-a) and we navigate to route-b, which doesn't change param `x`, param `x` will revert back to its default value.\n\nThis is a regression because it works on using older versions.\n\nSame code older versions:\nstackblitz\nSame code latest versions:\nstackblitz\n\n\n\n\n### Your Example Website or App\n\n-see desc\n\n### Steps to Reproduce the Bug or Issue\n\n1. click on Home route (see Auth defaults to true)\n2. click on Settings route (see Auth remains true)\n3. click on About route (see now Auth is false)\n4. click on Home route:\n4.a older version: Auth remains as false (which is expected)\n4.b newer version: Auth changes back to true (which is unexpected) \n\n### Expected behavior\n\nafter chaning the retained searchParams, it should stay unchanged after navigations, and only change if explicitly said so.\n* some other weird thing here is that even without using this middle params stay on URL, kinda making retainSearchParams useless, unless im not grasping the use of it correctly. \n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- OS: [e.g. macOS, Windows, Linux]\n- Browser: [e.g. Chrome, Safari, Firefox]\n- Version: [e.g. 91.1]\n\n\n### Additional context\n\n_No response_","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1737,"content_sha256":"ad76fd924f2b21d206e9a798b83ba75980de1cc9a98cefd621041853fdb15f49"},{"filename":"references/issues/issue-3023.md","content":"---\nnumber: 3023\ntitle: How to use CSS Modules with TanStack Start?\ntype: other\nstate: closed\ncreated: 2024-12-16\nurl: \"https://github.com/TanStack/router/issues/3023\"\nreactions: 57\ncomments: 72\nlabels: \"[start, revisit-after-devinxi]\"\n---\n\n# How to use CSS Modules with TanStack Start?\n\n### Which project does this relate to?\n\nStart\n\n### Describe the bug\n\nI'm struggling to make CSS Modules work properly. Currently, the styles are being loaded only on the client, causing a flash of unstyled content (FOUC) during initial render.\nI tried in this repo https://github.com/tanstack/router/tree/main/examples/react/start-basic\n\n### Your Example Website or App\n\nhttps://github.com/nikolayemrikh/start-css-modules-bug\n\n### Steps to Reproduce the Bug or Issue\n\n1. Clone repo https://github.com/nikolayemrikh/start-css-modules-bug\n2. Add CSS Modules support by creating a .module.css file and importing it into a React component.\n3. Run the server and open the app in a browser.\n4. Observe that the styles are applied only after the client-side hydration, causing a flash of unstyled content (FOUC).\n\n### Expected behavior\n\nI would like to see a working example or documentation on how to properly configure TanStack Router with CSS Modules and SSR.\nThe desired behavior is to have styles included in the server-rendered HTML, so there is no flash of unstyled content (FOUC).\n\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- OS: [e.g. macOS, Windows, Linux]\n- Browser: [e.g. Chrome, Safari, Firefox]\n- Version: [e.g. 91.1]\n\n\n### Additional context\n\n_No response_\n\n---\n\n## Top Comments\n\n**@gioruu** (+15):\n\nThis worked:\n\n- Add a '?url' to the css import:\n`import CSS from '../app.css?url'`\n\n- Add this line to the Route object head:\n`links: [{ rel: 'stylesheet', href: CSS }]`\n\n...\n\n**@roboncode** (+9):\n\n@j-mcfarlane, not sure about his repo but when i added the the ?url it worked for me and it started including it in my build\n\n```ts\n\nimport { AppProvider } from '@shopify/polaris'\nimport POLARIS_CSS from '@shopify/polaris/build/esm/styles.css?url'\nimport enTranslations from '@shopify/polaris/locales/en.json'\nimport type { ReactNode } from 'react'\nimport CLIENT_CSS from '../client.css?url'\nimport { QueryProvider } from '../providers/QueryProvider'\n\nexport const Route = createRootRoute({\n head: () => ({\n meta: [\n {\n charSet: 'utf-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1',\n },\n {\n title: 'TanStack Start Starter',\n },\n ],\n links: [\n { rel: 'stylesheet', href: CLIENT_CSS },\n { rel: 'stylesheet', href: POLARIS_CSS },\n ],\n }),\n component: RootComponent,\n})\n```...\n\n**@nikolayemrikh** (+15):\n\n@gioruu, your example works because you use the URL directly in the root. However, it doesn't make sense; we need to have the ability to write CSS and place it next to a component using CSS Modules. Neither CSS Modules nor imports work properly within a component.\n\nIt works as expected in other frameworks like Next.js.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3043,"content_sha256":"d7e55c6d9f154243f646db044c91700bb6dfa9464718af225461fbaf29757112"},{"filename":"references/issues/issue-3110.md","content":"---\nnumber: 3110\ntitle: `useLocation` re-renders before navigating\ntype: other\nstate: open\ncreated: 2025-01-04\nurl: \"https://github.com/TanStack/router/issues/3110\"\nreactions: 15\ncomments: 10\n---\n\n# `useLocation` re-renders before navigating\n\n To anyone stumbling here: this is not a bug, it's an issue with the documentation. All of us migrating from `react-router-dom` are used to using `useLocation` for everything, but with `tanstack/router`, there are different hooks for different purposes: https://tanstack.com/router/latest/docs/framework/react/api/router\n\n\n THIS IS THE SOLUTION TO YOUR PROBLEM \n> [!IMPORTANT]\n> **TL;DR**:\n> `useLocation` is about the URL, you *most likely* do not care about the URL. What you care about is probably: \n> - the *routes* => `useMatch`, `useMatches`, `useChildMatches`, `useParentMatches`\n> - the params => `useParams`, `useSearch`\n\n THIS IS THE SOLUTION TO YOUR PROBLEM \n\n\n----\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nAssuming a simple route like this:\n```ts\nexport const Route = createFileRoute('/about')({\n component: AboutComponent,\n});\n\nfunction AboutComponent() {\n const {pathname} = useLocation()\n console.log(pathname)\n return (\n \u003cdiv className=\"p-2\">\n \u003ch3>About\u003c/h3>\n \u003c/div>\n );\n}\n```\n\nMy naive assumption would be that the only value `pathname` can ever have here is `\"/about\"`, but in reality, this will also re-render just before navigating away, with `pathname` having the value of the **next page**.\n\nAlternatively, we can also use the following:\n```ts\nconst pathname = useRouterState({ select: (s) => s.resolvedLocation.pathname });\n```\n\nBut in that case, `pathname` will *initially* have the value of the **previous page** before immediately re-rendering with the current page.\n\nOne way to access the `pathname` that seems stable is this:\n```ts\nconst pathname = useMatch({ strict: false, select: (s) => s.pathname });\n```\n\n\n### Your Example Website or App\n\nhttps://stackblitz.com/edit/tanstack-router-fpv611d5?file=src%2Froutes%2F_yo%2Fabout.tsx\n\n### Steps to Reproduce the Bug or Issue\n\n1. Go to '/about' in the demo above\n2. open the console to see what gets logged\n3. Go to any other page\n\n...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2200,"content_sha256":"90d6cd78024610ab04e1ab7a8bafbc9d573ae1fb8bcd611f312c3441bc6773fc"},{"filename":"references/issues/issue-3462.md","content":"---\nnumber: 3462\ntitle: Error thrown from `beforeLoad` and handled through `errorComponent` is ignored on initial request\ntype: other\nstate: closed\ncreated: 2025-02-17\nurl: \"https://github.com/TanStack/router/issues/3462\"\nreactions: 28\ncomments: 5\n---\n\n# Error thrown from `beforeLoad` and handled through `errorComponent` is ignored on initial request\n\n### Which project does this relate to?\n\nStart\n\n### Describe the bug\n\nWhen an error is thrown from `beforeLoad` and handled through`errorComponent` it only works as expected on client navigation. When the page is reloaded/first requested the return for `errorComponent` is ignored and `component`/rest of the tree is rendered. This breaks many auth setups currently.\n\n### Your Example Website or App\n\nhttps://tanstack.com/start/latest/docs/framework/react/examples/start-basic-auth\n\n### Steps to Reproduce the Bug or Issue\n\nThis can be reproduced using the Start Basic Auth example.\n\n1. Navigate to \"Posts\" – the login is shown \n2. Now reload the \"Posts\" page – error is shown instead of login \n\nThis error is shown:\n\n> posts is not iterable\n\nYou can also add a `console.log` to `app/routes/_authed/posts.tsx`. This shows it tries to render the component, even though we are still unauthenticated and `beforeLoad` in `app/routes/_authed.tsx` throws an error which is handled by `errorComponent`, which should prevent rendering of the posts/`component`.\n\n\n### Expected behavior\n\nWhen throwing an error from `beforeLoad` and handling it within `errorComponent` the `component` should not be rendered – even on intial request handled by the server without client side navigation.\n\n### Screenshots or Videos\n\nhttps://github.com/user-attachments/assets/8fe225d8-be5b-4ef8-9e4b-8d6731ffa785\n\n### Platform\n\n- OS: macOS\n- Browser: Chromium\n- Version: 133\n\n\n### Additional context\n\n_No response_\n\n---\n\n## Top Comments\n\n**@KiwiKilian** (+8):\n\nUntil the bug is fixed our workaround looks like this:\n- `redirect` to `/login` instead of throwing an error\n- Move the `Login` component to that `/login` page\n- Append the path the user actually wanted to access as a search param\n- Redirect back to the search param path on successful login.\n\n**@Grimthul**:\n\nHaving the exact same issue. It happens even on more trivial case, where I try to redirect in beforeLoad function. \n```\nexport const Route = createFileRoute(\"/_auth\")({\n beforeLoad: ({ context }) => {\n console.log(\"auth before load\", context, !context.user);\n if (!context.user) {\n throw redirect({ to: \"/login\", search: { redirect: location.href } });\n }\n },\n});\n```\nEven when the user is null, it does not redirect but goes directly to the page that should be under authentication. \nAlso it logs Hydration failed errors to the console. \n\n\n**@Grimthul**:\n\nOne more thing, in the demo, when I try to change the beforeLoad function in _authed:\n```\nexport const Route = createFileRoute('/_authed')({\n beforeLoad: ({ context }) => {\n if (!context.user) {\n throw new Error('Not authenticated')\n }\n },\n errorComponent: ({ error }) => {\n if (error.message === 'Not authenticated') {\n return \u003cLogin />\n }\n\n throw error\n },\n})\n```\nto loader function:\n```\nexport const Route = createFileRoute(\"/_authed\")({\n loader: ({ context }) => {\n if (!context.user) {\n throw new Error(\"Not authenticated\");\n }\n },\n errorComponent: ({ error }) => {\n if (error.message === \"Not authenticated\") {\n return \u003cLogin />;\n }\n\n...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3497,"content_sha256":"32517eda69f5f5c73d239d0fda864e78e9d378309fb1c46a510aa7a86080e38a"},{"filename":"references/issues/issue-3468.md","content":"---\nnumber: 3468\ntitle: Cloudflare Env Vars Not Passed to SSR in TanStack Start\ntype: other\nstate: closed\ncreated: 2025-02-18\nurl: \"https://github.com/TanStack/router/issues/3468\"\nreactions: 35\ncomments: 9\nlabels: \"[start]\"\n---\n\n# Cloudflare Env Vars Not Passed to SSR in TanStack Start\n\n### Which project does this relate to?\n\nStart\n\n### Describe the bug\n\nWhen running a TanStack Start app on Cloudflare (Workers/Pages) with SSR, the Cloudflare environment bindings (in `getContext(\"cloudflare\")`) appear not to be accessible in the SSR pipeline. They work fine for API or “server” routes, but not for SSR.\n\n### Your Example Website or App\n\nnonexistent\n\n### Steps to Reproduce the Bug or Issue\n\n### Steps to Reproduce\n\n1. **Project Setup** \n - Using TanStack Start (`@tanstack/start@^1.101.2`) with a config that has multiple routers (`api`, `ssr`, `client`, etc.). \n - Deployed on Cloudflare Pages/Workers. \n - Also tested locally via `wrangler pages dev`.\n\n2. **Code Snippets**\n\n#### `app.config.ts` (TanStack start config)\n\n```ts\nimport { defineConfig } from \"@tanstack/start/config\";\nimport { cloudflare } from \"unenv\";\nimport type { App } from \"vinxi\";\nimport { checker } from \"vite-plugin-checker\";\nimport Terminal from \"vite-plugin-terminal\";\nimport tsConfigPaths from \"vite-tsconfig-paths\";\n\nconst tanstackApp = defineConfig({\n server: {\n preset: \"cloudflare-pages\",\n unenv: cloudflare,\n },\n routers: {\n api: {\n entry: \"src/entry.api.ts\",\n },\n ssr: {\n entry: \"src/entry.server.ts\",\n },\n client: {\n entry: \"src/entry.client.tsx\",\n },\n },\n vite: {\n plugins: [\n tsConfigPaths(),\n checker({\n typescript: true,\n overlay: true,\n }),\n Terminal(),\n ],\n resolve: {\n alias: {\n \"@\": \"src\",\n },\n },\n },\n tsr: {\n routesDirectory: \"src/routes\",\n appDirectory: \"src\",\n generatedRouteTree: \"src/routeTree.gen.ts\",\n },\n});\n\nconst routers = tanstackApp.config.routers.map((r) => {\n return {\n ...r,\n // Attempt to inject env middleware for server routes\n middleware:\n r.target === \"server\"\n ? \"src/lib/server/middleware/requests/env.server.ts\"\n : undefined,\n };\n});\n\nconst app: App = {\n ...tanstackApp,\n config: {\n ...tanstackApp.config,\n routers: routers,\n },\n};\n\n...\n\n---\n\n## Top Comments\n\n**@boertel** (+3):\n\n> Yeah, having the same problem rn trying to use `waitUntil`. Any workarounds?\n\nFor whoever lands on this issue, you can access `waitUntil` from `cloudflare:env`:\n```ts\nimport { waitUntil } from \"cloudflare:workers\";\n```\n\n**@MrSquaare** (+5):\n\nPlease also note that using https://github.com/nitrojs/nitro-cloudflare-dev with dev server does not return Cloudflare bindings neither in API Routes nor in server actions.\n\n**@leonardo2204** (+1):\n\n> > How do you get access to the bindings though using the Cloudflare Vite Plugin in start?\n> \n> With `env` imported from `\"cloudflare:workers\"`\n> \n> Example:\n> \n> import { env } from \"cloudflare:workers\";\n> \n> const serverFunction = createServerFn().handler(() => {\n> \tconsole.log(env);\n> });\n> \n> export const Route = createFileRoute(\"/\")({\n> \tcomponent() {\n> \t\treturn (\n> \t\t\t\u003cbutton\n> \t\t\t\tonClick={async () => {\n> \t\t\t\t\tawait serverFunction();\n> \t\t\t\t}}\n> \t\t\t>\n> \t\t\t\tClick me\n> \t\t\t\u003c/button>\n> \t\t);\n> \t},\n> });\n> See [cloudflare docs](https://developers.cloudflare.com/workers/framework-guides/web-ap...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3419,"content_sha256":"be5e3e497b429c5c25bbd198389d0ebdc9861147c795c0b46f258567e97aec80"},{"filename":"references/issues/issue-3804.md","content":"---\nnumber: 3804\ntitle: Page doesn't scroll to top after navigation\ntype: other\nstate: open\ncreated: 2025-03-18\nurl: \"https://github.com/TanStack/router/issues/3804\"\nreactions: 16\ncomments: 6\nlabels: \"[information needed]\"\n---\n\n# Page doesn't scroll to top after navigation\n\nI'm still trying to create a minimum reproduction for this.\n\nCurrent behaviour:\n\n- By default scroll does not get reset to top after navigation\n- Setting `scrollRestoration: true` doesn't works correctly with `router.history.back()`","content_type":"text/markdown; charset=utf-8","language":"markdown","size":507,"content_sha256":"3366c4d35dca2830125b414d703d198808cfb52755cce1c2f5e7ccd01a2ba201"},{"filename":"references/issues/issue-3997.md","content":"---\nnumber: 3997\ntitle: Performance is horrible when using recommended Authentication patterns\ntype: other\nstate: open\ncreated: 2025-04-15\nurl: \"https://github.com/TanStack/router/issues/3997\"\nreactions: 46\ncomments: 22\n---\n\n# Performance is horrible when using recommended Authentication patterns\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nWhen using the recommended patterns for authentication, particularily in tanstack start, the performance of the app is garbage. \n\nbecause onBefore load for _root or _authed routes runs on every page navigation, even when you are clientside, it necessitates a sever round trip before being able to navigate resulting in incredibly unresponsive apps. (even if the auth service was processing in 1ms the server trip is usally about 200 -300 ms for most people)\n\nWith tanstack router there were some solutions to this by storing the authstate in a react context or hook outside of the inner app and so the state would persist as normal across transitions without a server trip and you could refresh you session tokens as normal when needed. \n\nWith tanstack start this is no longer an option as it does not appear as if its possible to have a global state that is persisted across route transitions, (ie client side behaviour that we love, that makes the apps fast). \n\nThis is quite obviously not in line with the promise of tanstack start from their very own landing page: \n\n> While other frameworks continue to compromise on the client-side application experience we've cultivated as a front-end community over the years, TanStack Start stays true to the client-side first developer experience, while providing a full-featured server-side capable system that won't make you compromise on user experience.\n\nThere should be some VERY clear documentation on how to avoid this performance issue, and patterns and practices on how to achieve client first performance. \n\n\n### Your Example Website or App\n\nhttps://github.com/tanstack/router/tree/main/examples/react/start-clerk-basic\n\n### Steps to Reproduce the Bug or Issue\n\nuse any of the authenticated template starters and attempt to navigate\n\n### Expected behavior\n\n...\n\n---\n\n## Top Comments\n\n**@schiller-manuel** (+59):\n\nwe are working on adding another lifecycle method that will be cached in the same way as the loader but will execute serially just like beforeLoad. stay tuned!\n\n**@Firephoenix25** (+22):\n\nany updates?\n\n**@pleunv** (+9):\n\nI've been running into a few similar surprises mostly related to the `beforeLoad`/`loader` behavior on a pathless `_authed` wrapper route.\n\nObservations:\n \n- `beforeLoad` and `loader` of the `_authed` wrapper route are triggered on every child route navigation.\n- `beforeLoad` and `loader` of the `_authed` wrapper route are triggered on every child route preload, with the `preload` param received as `false`, for some reason. This especially threw me off.\n- No amount of `staleTime`, `preloadStaleTime`, `gcTime` or `preloadGcTime` configuration seem to have any impact on how frequently the `befor...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3060,"content_sha256":"8d838b2f198f2b72cefb501a53717db9a6fd08d2d4c03ea0f780e07c487dcf88"},{"filename":"references/issues/issue-4010.md","content":"---\nnumber: 4010\ntitle: \"Error \\\"Invariant failed: Could not find match for from\\\" when layout + file-based routing is used\"\ntype: other\nstate: closed\ncreated: 2025-04-17\nurl: \"https://github.com/TanStack/router/issues/4010\"\nreactions: 36\ncomments: 46\n---\n\n# Error \"Invariant failed: Could not find match for from\" when layout + file-based routing is used\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nWhen trying to use the layout routing file, the routes created are correct but when trying to open one of the route using \u003cLink /> component, it fails to load or when trying to refresh the page which is already on the failed route.\n\n\n\n### Your Example Website or App\n\nhttps://stackblitz.com/edit/tanstack-invariant-bug\n\n### Steps to Reproduce the Bug or Issue\n\n1. Click on About or Dashboard link\n2. Error is thrown\n\n### Expected behavior\n\nI should be able to properly add a layout routing file and the nested components should work correctly.\n\nFor example below routes:\n\napp/home\napp/about\napp/dashboard\n\nI have route.tsx (default pattern for layout file) file inside app folder which contains the nav bar, then /home, /about and /dashboard should have the nav bar present in them.\n\n### Screenshots or Videos\n\n\n\n### Platform\n\n\"@tanstack/react-router\": \"^1.116.0\"\n\"@tanstack/router-plugin\": \"^1.116.1\"\n\n### Additional context\n\n_No response_\n\n---\n\n## Top Comments\n\n**@GGAlanSmithee** (+5):\n\nAlso hitting this. In my case, I want to run preload, is there any way around this? \n\n**@sep2** (+1):\n\nI just downgrade to `1.102.5`, noted that in package.json you have to add exact version of the following three package otherwise it won't downgrade.\n\n```\n \"@tanstack/react-router\": \"1.102.5\",\n \"@tanstack/router-plugin\": \"1.102.5\",\n \"@tanstack/router-generator\": \"1.102.5\"\n```\n\n> Is there any specific change in the latest version cause the same code works for v1.102.5? If I remove the basepath, is there any way to directly redirect to /app always?\n\n\n\n**@schiller-manuel**:\n\nyour example has a basepath 'app' configured AND your routes are inside an `app` folder.\nSo the runtime URL would be /app/app/..\nProbably not what you want?\nEither remove the basepath or move the routes outside of the app folder\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2258,"content_sha256":"b66cb9029ed666b9619d8c2412a096154c3ea50cac174a067387bf439dba16f3"},{"filename":"references/issues/issue-4473.md","content":"---\nnumber: 4473\ntitle: \"Start: Full support by `@cloudflare/vite-plugin`\"\ntype: other\nstate: closed\ncreated: 2025-06-19\nurl: \"https://github.com/TanStack/router/issues/4473\"\nreactions: 24\ncomments: 5\n---\n\n# Start: Full support by `@cloudflare/vite-plugin`\n\n### Which project does this relate to?\n\nStart\n\n### Describe the bug\n\n### Problem\nTanstack Start and `@cloudflare/vite-plugin` are not fully compatible with one another. For example, using virtual imports like `cloudflare:workflows` and `cloudflare:workers` causes issues; these can be marked external in vite but doing so will cause other issues during either build or deployment.\n\n### Solution\n@schiller-manuel mentioned he's talking with Cloudflare to improve Tanstack Start compatibility with `@cloudflare/vite-plugin`.\n\nCreating a tracking issue so we can follow progress without bugging core team for status updates in Discord.\n\n### Your Example Website or App\n\nn/a\n\n### Steps to Reproduce the Bug or Issue\n\n..\n\n### Expected behavior\n\nDev should be able to add `cloudflare()` from `@cloudflare/vite-plugin` to plugins in `vite.config.ts` to have access to all of the miniflare environment. It should automatically prevent issues when using cloudflare virtual imports from `cloudflare:workflows` and `cloudflare:workers`. Etc.\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- OS: macOS\n- Browser: n/a\n- Version: \n```json\n\"@cloudflare/vite-plugin\": \"^1.7.2\",\n\"wrangler\": \"^4.20.3\",\n\"@tanstack/react-query\": \"^5.80.10\",\n\"@tanstack/react-router\": \"1.121.27\",\n\"@tanstack/react-router-with-query\": \"1.121.27\",\n\"@tanstack/react-start\": \"^1.121.27\",\n\"@tanstack/router-core\": \"1.121.27\",\n\"@tanstack/router-devtools-core\": \"1.121.27\",\n\"@tanstack/router-generator\": \"1.121.27\",\n\"@tanstack/router-plugin\": \"1.121.27\",\n```\n\n\n### Additional context\n\n_No response_\n\n---\n\n## Top Comments\n\n**@schiller-manuel** (+6):\n\ndone now\n\n\n\nhttps://tanstack.com/start/latest/docs/framework/react/hosting#cloudflare-workers--official-partner\n\n\n**@Folyd** (+3):\n\nThe cloudflare host docs is outdated: \nhttps://tanstack.com/start/latest/docs/framework/react/hosting#cloudflare-pages\n\n**@juliomuhlbauer** (+3):\n\nCreated an issue: https://github.com/TanStack/router/issues/4500","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2217,"content_sha256":"b6e45eacaf218913ec3c0a35e475fa30833274ae2aadbf473b3639203125e354"},{"filename":"references/issues/issue-4476.md","content":"---\nnumber: 4476\ntitle: Tanstack Router (with Tanstack Query) throws CancelledError when navigating between pages\ntype: other\nstate: open\ncreated: 2025-06-19\nurl: \"https://github.com/TanStack/router/issues/4476\"\nreactions: 16\ncomments: 1\n---\n\n# Tanstack Router (with Tanstack Query) throws CancelledError when navigating between pages\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nBy configuring the Router as below and using a `useQuery` hook on page 1 and a `fetchQuery` call in `beforeLoad` on page 2 (same key), navigating from page 1 to page 2 crashes with CancelledError.\n\n- defaultPreload: “intent”\n- defaultPendingComponent component is set (can make defaultPendingMs 0 so the error is more instant. but we can leave as default)\n- added `if (preload) return;` in page 2 `beforeLoad` to “block” preloading (this isn't necessary to create the issue but it helps with the demo)\n\n### Your Example Website or App\n\nhttps://codesandbox.io/p/github/cirex-web/tanstack-router-cancellederror-demo/main\n\n### Steps to Reproduce the Bug or Issue\n\n1. Load home page (Observer gets mounted (by useQuery or useSuspenseQuery) with queryKey “key” on Page 1)\n2. Wait until “key” goes stale\n3. Navigate to Page 2, whose `beforeLoad` function calls queryClient.fetchQuery using the same queryKey “key”. queryFn accepts the abort signal argument.\n4. If queryFn is slow, a CancelledError is thrown by the `fetchQuery` call with the stacktrace\n\n\n(seems like this is caused by the page 1 observer unmounting)\n\nIf we move the `beforeLoad` code into `loader`, we get the same error. In fact, we don’t even need a pending component defined to get the error!\n\n\n### Expected behavior\n\nThe fetch in either `beforeLoad` or `loader` should probably just work, irrespective of observers unmounting on the previous page.\n\n### Screenshots or Videos\n\nhttps://github.com/user-attachments/assets/bf0d73b8-6a0a-430e-900d-eb434ec8a778\n\n### Platform\n\n- OS: macOS\n- Browser: Chrome\n- Router Version: 1.121.24\n- Query Version: 5.80.7\n\n\n### Additional context\n\n...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2074,"content_sha256":"93e336d6b6b8eedf577fb2f015dfb9ca40e73fbbbd1704a84c9c69e468889806"},{"filename":"references/issues/issue-4499.md","content":"---\nnumber: 4499\ntitle: useMatchRoute not updating when using React Compiler\ntype: other\nstate: open\ncreated: 2025-06-22\nurl: \"https://github.com/TanStack/router/issues/4499\"\nreactions: 11\ncomments: 5\n---\n\n# useMatchRoute not updating when using React Compiler\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nWhen using the function returned by `useMatchRoute` in combination with React Compiler, it looks the result is only evaluated on the initial render.\n\nWhen using the repro stackblitz, you can remove the react compiler config, which will make the repro case act as I would expect it to.\n\n### Your Example Website or App\n\nhttps://stackblitz.com/edit/github-jbydd83d?file=src%2Fmain.tsx\n\n### Steps to Reproduce the Bug or Issue\n\nUsing the reproduction scenario:\n\n1. Click on the \"To About\"-link\n2. The \"Matched route name\" doesn't change\n3. Refresh\n4. The \"Matched route name\" updates to the correct label\n\n### Expected behavior\n\nThe \"Matched route name\" label changes on each navigation change.\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- OS: Linux\n- Browser: Chromium\n- Version: 137\n\n\n### Additional context\n\n_No response_","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1167,"content_sha256":"30a5705a6a71a2bd1f39a7e58774bac67454b474c11d267b06329b985d850378"},{"filename":"references/issues/issue-4729.md","content":"---\nnumber: 4729\ntitle: \"@tanstack/start-storage-context seem wrong bundled in client side code that cause a node:async_hooks.AsyncLocalStorage bug\"\ntype: other\nstate: closed\ncreated: 2025-07-21\nurl: \"https://github.com/TanStack/router/issues/4729\"\nreactions: 22\ncomments: 22\n---\n\n# @tanstack/start-storage-context seem wrong bundled in client side code that cause a node:async_hooks.AsyncLocalStorage bug\n\n### Which project does this relate to?\n\nRouter, react-start\n\n### Describe the bug\n\nNew version update will cause a bug that node:async_hooks.AsyncLocalStorage is bundle in client side code. \ni have reproduce it in a sandbox. you could see it's happend on /posts route \nthis screen shot is after i add a `optimizeDeps: {\n exclude: ['@tanstack/start-storage-context', 'node:async_hooks'],\n },\n ssr: {\n noExternal: ['@tanstack/start'],\n external: ['node:async_hooks'],\n },` in vite config \n\u003cimg width=\"1126\" height=\"205\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/e89580fa-e9e3-438d-b5db-77be8dd98bc7\" />\n\n### Your Example Website or App\n\nhttps://codesandbox.io/p/sandbox/rough-snow-tqngv6\n\n### Steps to Reproduce the Bug or Issue\n\n1. Create a middleware `import { createMiddleware } from '@tanstack/react-start';\nasync function serverHandler({ next, context }: any) {\n const result = await next();\n return result;\n}\nexport const getUserContextMiddleware = createMiddleware({\n type: 'function',\n}).server(serverHandler);\n`\n2. use it in a server function `export const fetchPosts = createServerFn({ method: 'GET' }).middleware([getUserContextMiddleware]).handler(\n async () => {\n console.info('Fetching posts...')\n return axios\n .get\u003cArray\u003cPostType>>('https://jsonplaceholder.typicode.com/posts')\n .then((r) => r.data.slice(0, 10))\n },\n)`\n\n\n### Expected behavior\n\n`Module \"node:async_hooks\" has been externalized for browser compatibility. Cannot access \"node:async_hooks.AsyncLocalStorage\" in client code ` will not be occurred\n\n### Screenshots or Videos\n\n\u003cimg width=\"708\" height=\"552\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/67abfe51-1eb6-456b-a3bd-365a1d4b7d4f\" />\n\n### Platform\n\n- Router / Start Version: 1.129.0\n- OS: macOs\n- Browser: Chrome\n- Bundler: vite\n\n\n### Additional context\n\n_No response_","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2276,"content_sha256":"b1384e97c3e5f6a1df8188e7117ebf1870a7305bdef6d30b6c09a02c3efc0f9f"},{"filename":"references/issues/issue-4901.md","content":"---\nnumber: 4901\ntitle: Link should accept external links\ntype: feature\nstate: open\ncreated: 2025-08-09\nurl: \"https://github.com/TanStack/router/issues/4901\"\nreactions: 34\ncomments: 8\nlabels: \"[enhancement, types]\"\n---\n\n# Link should accept external links\n\n### Which project does this relate to?\n\nRouter\n\n### Describe the bug\n\nThe `Link` component, custom link components created with `createLink()` and possibly `linkOptions()` only accept internal links but should accept external links (https, mailto, tel, ...) as well. \n\nThe `Link` component could simply be replaced with a plain `a` element when using external links, but custom link components are often styled or may provide additional behavior which we want to use for external links as well.\n\nThere's already a check implemented for external links and passing an external URL for the `to` prop actually works at runtime but not on a type-level.\n\nhttps://github.com/TanStack/router/blob/ef74fc661bb82cbc415fcc6645cffcaec9b3b8b7/packages/react-router/src/link.tsx#L88-L94\n\n\n### Your Example Website or App\n\nhttps://stackblitz.com/edit/github-qe1tnt6t?file=src%2Fmain.tsx\n\n### Steps to Reproduce the Bug or Issue\n\nThe external links `\u003cLink to=\"https://...\" />` work at runtime but produce a TS error.\nThe link with just `\u003cLink href=\"https://...\" />` doesn't work at runtime and also produces a TS error (because `to` is required).\n\n\n### Expected behavior\n\nThere should be a way to use (custom) link components with external links without TypeScript errors.\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- Router / Start Version: 1.131.2\n- OS: macOS\n- Browser: Brave\n- Browser Version: 1.81.31\n- Bundler: Vite\n- Bundler Version: 6.3.5\n\n\n### Additional context\n\nRelated discussion on Discord: https://discord.com/channels/719702312431386674/1391990807225171968\n\n---\n\n## Top Comments\n\n**@max-ae** (+10):\n\nChiming in here: when using the `href` prop for an external URL, TS complains about missing `to`. When adding additional `from` prop here, the TS error about `to` disappears, but the Link does not navigate to the external URL at runtime, but rather to `.`\n\nWas also confused by the docs stating that this should work.\n\n**@achou11** (+1):\n\nRunning into this as well. My initial approach was actually specifying the `href` prop instead as I thought that should work based on the documentation here, but it seems that it doesn't work for me:\n\n> This can be used instead of `to` to navigate to a fully built href, e.g. pointing to an external target.\n\nEDIT: Ah I see you noted the issue with the `href` prop not working at runtime. I feel like that should be highlighted as a bigger issue \n\n**@hornta** (+1):\n\nI have this problem with the normal `Link` component imported from `import { Link } from \"@tanstack/react-router\";`. I don't think this problem is specific to only custom links.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2855,"content_sha256":"c271b3f04eba4bc3552c95ec8a475e6a5dc4d18f0002bcc211190bcec3871719"},{"filename":"references/issues/issue-4973.md","content":"---\nnumber: 4973\ntitle: Search Params as Actual State\ntype: other\nstate: open\ncreated: 2025-08-16\nurl: \"https://github.com/TanStack/router/issues/4973\"\nreactions: 12\ncomments: 6\n---\n\n# Search Params as Actual State\n\nWe tried to use TanStack Router search params as our primary global state. While the read story is great (`useSearch` + schema validation), the write and lifecycle aspects are too low-level. We ended up building a fairly involved layer to make search-params reliable, composable, and ergonomic. We’ve read \"Search Params Are State\" — we agree on the premise; the missing pieces are writes and cross-component coordination.\n\nThis issue proposes concrete primitives the router could provide to enable \"search params as state\" without userland workarounds.\n\n### What exists today (and why it's not sufficient)\n\n- Global encode/decode can be customized via `parseSearch`/`stringifySearch` (e.g., `parseSearchWith`, `stringifySearchWith`). This is helpful but applies globally, not per param.\n\n```ts\nconst router = createRouter({\n routeTree,\n parseSearch: parseSearchWith((v) => v),\n stringifySearch: stringifySearchWith(String),\n});\n```\n\n- Route-level `validateSearch` normalizes/validates incoming search values, but there’s no symmetric per-param encode on write.\n- `navigate` supports a `mask` option to control visible URL segments, but masking is specified per navigation, not per param, and does not compose across components.\n\n### Why the current API falls short\n\n- **Last-write-wins within a render**: Multiple `navigate({ search: ... })` calls during the same render/commit will override each other; only the last one survives. Real apps routinely update different params from different components in the same render. We need deterministic, atomic batching across the tree. \nRepro (navigate last-write-wins vs functional updaters): CodeSandbox demo\n\n...","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1883,"content_sha256":"7f2da5336df95b0c118071fcbb8b72a0acfccb6bdc41b6ccbe6ee55b5b5216a9"},{"filename":"references/issues/issue-4988.md","content":"---\nnumber: 4988\ntitle: vite-plugin-pwa incompatible with tanstack start production builds\ntype: other\nstate: open\ncreated: 2025-08-17\nurl: \"https://github.com/TanStack/router/issues/4988\"\nreactions: 16\ncomments: 19\n---\n\n# vite-plugin-pwa incompatible with tanstack start production builds\n\n### Which project does this relate to?\n\nStart\n\n### Describe the bug\n\nvite-plugin-pwa's build steps (generate assets, generate serviceworker bundle) are seemingly not executed when running `vite build` with VitePWA() and `tanstackStart()` both present.\n\nThis is likely because to vite-plugin-pwa not having proper support for the Vite 6 environment API.\nThere is an open PR to solve this: https://github.com/vite-pwa/vite-plugin-pwa/pull/786, however the current state of this PR has the same issue as the stable version of the package.\n\nNo PWA/SW steps are ran in build:\n\n\u003cimg width=\"1978\" height=\"1572\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/e44c44ab-9976-4a4f-a257-43e7a18b5cc7\" />\n\nbut it works in the vite dev server:\n\n\u003cimg width=\"3339\" height=\"2406\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/08428aeb-a519-42e4-9953-5c27d3855dc8\" />\n\n### Your Example Website or App\n\nhttps://stackblitz.com/edit/github-djpcrmc9?file=src%2Frouter.tsx\n\n### Steps to Reproduce the Bug or Issue\n\n1. `npm install`\n2. `npm run build`\n3. No PWA/SW steps are ran in build:\n\n### Expected behavior\n\n- Build steps actually run during `vite build`\n- Service worker gets built into tanstack start's `client-dist`, so it can also get picked up by nitro `publicAssets[]`\n- Any script injection steps such as into \u003chead/> also work(?)\n\n### Screenshots or Videos\n\n_No response_\n\n### Platform\n\n- Start Version: ^1.131.7\n- OS: macOS, Stackblitz WebContainer\n- Browser: Chrome\n- Browser Version: 139.0.7258.66 \n- Bundler: Vite\n- Bundler Version: ^6.3.5\n\n\n### Additional context\n\n_No response_","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1890,"content_sha256":"a2151220103739f6b70c4cbc46e24ae0d518c6ca5779f6567b39b31e311cb916"},{"filename":"references/releases/v1.158.2.md","content":"---\ntag: v1.158.2\nversion: 1.158.2\npublished: 2026-02-05\n---\n\n# v1.158.2\n\nVersion 1.158.2 - 2/5/26, 9:05 PM\n\n## Changes\n\n### Fix\n\n- resolve env-only functions through module re-export chains (#6599) (f5bf1cd) by Manuel Schiller\n\n## Packages\n\n- @tanstack/[email protected]\n- @tanstack/[email protected]\n- @tanstack/[email protected]\n- @tanstack/[email protected]\n- @tanstack/[email protected]","content_type":"text/markdown; charset=utf-8","language":"markdown","size":423,"content_sha256":"ab3db6f3425d7d179ae7ff2778f795dfb48b4b3c8703620fd1bed8e773baaa0d"},{"filename":"references/releases/v1.159.7.md","content":"---\ntag: v1.159.7\nversion: 1.159.7\npublished: 2026-02-13\n---\n\n# v1.159.7\n\nVersion 1.159.7 - 2/13/26, 8:10 PM\n\n## Changes\n\n### Fix\n\n- don\"t propagate directCall kind through var bindings (#6651) (d931bcf) by Manuel Schiller\n\n## Packages\n\n- @tanstack/[email protected]\n- @tanstack/[email protected]\n- @tanstack/[email protected]\n- @tanstack/[email protected]\n- @tanstack/[email protected]","content_type":"text/markdown; charset=utf-8","language":"markdown","size":418,"content_sha256":"b47f5f0c6ff468ac55b5897f5f41c03d9cdff5048ab9e4ea154d44b50dd7cea2"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"TanStack/router ","type":"text"},{"text":"@tanstack/vue-router","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Modern and scalable routing for Vue applications","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Version:","type":"text","marks":[{"type":"strong"}]},{"text":" 1.166.7 (Mar 2026) ","type":"text"},{"text":"Deps:","type":"text","marks":[{"type":"strong"}]},{"text":" @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] ","type":"text"},{"text":"Tags:","type":"text","marks":[{"type":"strong"}]},{"text":" latest: 1.166.7 (Mar 2026)","type":"text"}]},{"type":"paragraph","content":[{"text":"References:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"Docs","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/_INDEX.md","title":null}}]},{"text":" — API reference, guides","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"API Changes","type":"text"}]},{"type":"paragraph","content":[{"text":"This section documents version-specific API changes — prioritize recent major/minor releases.","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"BREAKING: ","type":"text"},{"text":"NotFoundRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":" & ","type":"text"},{"text":"routerOptions.notFoundRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":" — deprecated in v1.x; use ","type":"text"},{"text":"notFoundComponent","type":"text","marks":[{"type":"code_inline"}]},{"text":" in route options or ","type":"text"},{"text":"defaultNotFoundComponent","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/not-found-errors.md:L5","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DEPRECATED: Router Classes (","type":"text"},{"text":"Router","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Route","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"RootRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"FileRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":") — all class-based APIs are deprecated; use factory functions ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"createRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"createRootRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"createFileRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouterClass.md:L7","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DEPRECATED: ","type":"text"},{"text":"opts.navigate","type":"text","marks":[{"type":"code_inline"}]},{"text":" — the ","type":"text"},{"text":"navigate","type":"text","marks":[{"type":"code_inline"}]},{"text":" argument inside ","type":"text"},{"text":"beforeLoad","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"loader","type":"text","marks":[{"type":"code_inline"}]},{"text":" is deprecated; use ","type":"text"},{"text":"throw redirect({ to: '...' })","type":"text","marks":[{"type":"code_inline"}]},{"text":" for navigation-triggered redirects instead ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouteOptionsType.md:L118","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DEPRECATED: ","type":"text"},{"text":"parseParams","type":"text","marks":[{"type":"code_inline"}]},{"text":" & ","type":"text"},{"text":"stringifyParams","type":"text","marks":[{"type":"code_inline"}]},{"text":" — top-level route properties deprecated in favor of the nested ","type":"text"},{"text":"params.parse","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"params.stringify","type":"text","marks":[{"type":"code_inline"}]},{"text":" objects ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouteOptionsType.md:L68","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DEPRECATED: ","type":"text"},{"text":"preSearchFilters","type":"text","marks":[{"type":"code_inline"}]},{"text":" & ","type":"text"},{"text":"postSearchFilters","type":"text","marks":[{"type":"code_inline"}]},{"text":" — deprecated in favor of ","type":"text"},{"text":"search.middlewares","type":"text","marks":[{"type":"code_inline"}]},{"text":" array which provides a composable middleware pipeline for transforming search params ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouteOptionsType.md:L225","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DEPRECATED: ","type":"text"},{"text":"\u003cScrollRestoration />","type":"text","marks":[{"type":"code_inline"}]},{"text":" component — deprecated; configure scroll restoration via ","type":"text"},{"text":"scrollRestoration: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" options instead ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/scroll-restoration.md:L64","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"protocolAllowlist","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" option accepting ","type":"text"},{"text":"Array\u003cstring>","type":"text","marks":[{"type":"code_inline"}]},{"text":" of allowed URL protocols (e.g. ","type":"text"},{"text":"'https:'","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"'mailto:'","type":"text","marks":[{"type":"code_inline"}]},{"text":"); absolute URLs with unlisted protocols are blocked to prevent XSS; also exports ","type":"text"},{"text":"DEFAULT_PROTOCOL_ALLOWLIST","type":"text","marks":[{"type":"code_inline"}]},{"text":" constant ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouterOptionsType.md:L147","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"search.middlewares","type":"text","marks":[{"type":"code_inline"}]},{"text":" — route option accepting an array of middleware functions ","type":"text"},{"text":"({search, next}) => search","type":"text","marks":[{"type":"code_inline"}]},{"text":" for composable search param transformation when generating links; use with ","type":"text"},{"text":"retainSearchParams","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"stripSearchParams","type":"text","marks":[{"type":"code_inline"}]},{"text":" helpers ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouteOptionsType.md:L61","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"head","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"headers","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"scripts","type":"text","marks":[{"type":"code_inline"}]},{"text":" — route option methods for server-side document management; ","type":"text"},{"text":"head()","type":"text","marks":[{"type":"code_inline"}]},{"text":" injects ","type":"text"},{"text":"\u003cmeta>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003clink>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003cstyle>","type":"text","marks":[{"type":"code_inline"}]},{"text":" into ","type":"text"},{"text":"\u003chead>","type":"text","marks":[{"type":"code_inline"}]},{"text":"; ","type":"text"},{"text":"headers()","type":"text","marks":[{"type":"code_inline"}]},{"text":" sets HTTP response headers; ","type":"text"},{"text":"scripts()","type":"text","marks":[{"type":"code_inline"}]},{"text":" injects ","type":"text"},{"text":"\u003cscript>","type":"text","marks":[{"type":"code_inline"}]},{"text":" tags ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouteOptionsType.md:L304","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: Validation Adapters — ","type":"text"},{"text":"@tanstack/zod-adapter","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"@tanstack/valibot-adapter","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"@tanstack/arktype-adapter","type":"text","marks":[{"type":"code_inline"}]},{"text":" provide schema-based validation for search params and route params with distinct input/output type inference ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/search-params.md#zod","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"defaultViewTransition","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" option accepting ","type":"text"},{"text":"boolean | ViewTransitionOptions","type":"text","marks":[{"type":"code_inline"}]},{"text":" to enable native View Transitions API (","type":"text"},{"text":"document.startViewTransition()","type":"text","marks":[{"type":"code_inline"}]},{"text":") during navigation; supports ","type":"text"},{"text":"types","type":"text","marks":[{"type":"code_inline"}]},{"text":" array via ","type":"text"},{"text":"ViewTransitionOptions","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouterOptionsType.md:L182","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"rewrite","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" option accepting ","type":"text"},{"text":"{ input?, output? }","type":"text","marks":[{"type":"code_inline"}]},{"text":" for bidirectional URL transformation between browser URL and router's internal URL; ","type":"text"},{"text":"input","type":"text","marks":[{"type":"code_inline"}]},{"text":" transforms before matching, ","type":"text"},{"text":"output","type":"text","marks":[{"type":"code_inline"}]},{"text":" transforms before writing to history ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouterOptionsType.md:L217","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"Wrap","type":"text","marks":[{"type":"code_inline"}]},{"text":" & ","type":"text"},{"text":"InnerWrap","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"createRouter","type":"text","marks":[{"type":"code_inline"}]},{"text":" options for injecting global providers; ","type":"text"},{"text":"Wrap","type":"text","marks":[{"type":"code_inline"}]},{"text":" surrounds the entire router, ","type":"text"},{"text":"InnerWrap","type":"text","marks":[{"type":"code_inline"}]},{"text":" wraps inner content and has access to router context and hooks ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouterOptionsType.md:L295","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEW: ","type":"text"},{"text":"codeSplitGroupings","type":"text","marks":[{"type":"code_inline"}]},{"text":" — route option ","type":"text"},{"text":"Array\u003cArray\u003c'loader' | 'component' | 'pendingComponent' | 'notFoundComponent' | 'errorComponent'>>","type":"text","marks":[{"type":"code_inline"}]},{"text":" for fine-grained control over how lazy-loaded route assets are bundled into chunks ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/api/router/RouteOptionsType.md:L364","title":null}}]}]}]}]},{"type":"paragraph","content":[{"text":"Also changed:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"rootRouteWithContext","type":"text","marks":[{"type":"code_inline"}]},{"text":" deprecated → use ","type":"text"},{"text":"createRootRouteWithContext","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"useCanGoBack()","type":"text","marks":[{"type":"code_inline"}]},{"text":" new experimental hook · ","type":"text"},{"text":"defaultRemountDeps","type":"text","marks":[{"type":"code_inline"}]},{"text":" new router option · ","type":"text"},{"text":"defaultStructuralSharing","type":"text","marks":[{"type":"code_inline"}]},{"text":" new router option · ","type":"text"},{"text":"search.strict","type":"text","marks":[{"type":"code_inline"}]},{"text":" new router option · ","type":"text"},{"text":"disableGlobalCatchBoundary","type":"text","marks":[{"type":"code_inline"}]},{"text":" new router option · ","type":"text"},{"text":"scrollToTopSelectors","type":"text","marks":[{"type":"code_inline"}]},{"text":" new router option · ","type":"text"},{"text":"composeRewrites","type":"text","marks":[{"type":"code_inline"}]},{"text":" new export · ","type":"text"},{"text":"ClientOnly","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"ScriptOnce","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"HeadContent","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"Asset","type":"text","marks":[{"type":"code_inline"}]},{"text":" new components · ","type":"text"},{"text":"SearchSchemaInput","type":"text","marks":[{"type":"code_inline"}]},{"text":" tag for optional search params · ","type":"text"},{"text":"state.__TSR_key","type":"text","marks":[{"type":"code_inline"}]},{"text":" replaces deprecated ","type":"text"},{"text":"state.key","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"zodValidator()","type":"text","marks":[{"type":"code_inline"}]},{"text":" adapter with ","type":"text"},{"text":"fallback()","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of Zod's ","type":"text"},{"text":".catch()","type":"text","marks":[{"type":"code_inline"}]},{"text":" for search param validation — ","type":"text"},{"text":".catch()","type":"text","marks":[{"type":"code_inline"}]},{"text":" widens types to ","type":"text"},{"text":"unknown","type":"text","marks":[{"type":"code_inline"}]},{"text":", losing type inference, while ","type":"text"},{"text":"fallback(z.number(), 1).default(1)","type":"text","marks":[{"type":"code_inline"}]},{"text":" retains correct types and makes ","type":"text"},{"text":"search","type":"text","marks":[{"type":"code_inline"}]},{"text":" optional in ","type":"text"},{"text":"\u003cLink>","type":"text","marks":[{"type":"code_inline"}]},{"text":" props ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/search-params.md#zod","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"In ","type":"text"},{"text":"loaderDeps","type":"text","marks":[{"type":"code_inline"}]},{"text":", extract only the search params actually used in the loader — returning the entire ","type":"text"},{"text":"search","type":"text","marks":[{"type":"code_inline"}]},{"text":" object causes the loader to re-run on any search param change, even unrelated ones like ","type":"text"},{"text":"viewMode","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"sortDirection","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/data-loading.md#using-loaderdeps-to-access-search-params","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"getRouteApi('/your/path')","type":"text","marks":[{"type":"code_inline"}]},{"text":" to access route hooks (","type":"text"},{"text":"useLoaderData","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"useSearch","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"useParams","type":"text","marks":[{"type":"code_inline"}]},{"text":") in deeply nested components instead of importing the ","type":"text"},{"text":"Route","type":"text","marks":[{"type":"code_inline"}]},{"text":" object — direct ","type":"text"},{"text":"Route","type":"text","marks":[{"type":"code_inline"}]},{"text":" imports from child components create circular dependencies ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/data-loading.md#consuming-data-from-loaders","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enable ","type":"text"},{"text":"defaultStructuralSharing: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" on the router when using ","type":"text"},{"text":"select","type":"text","marks":[{"type":"code_inline"}]},{"text":" in hooks like ","type":"text"},{"text":"useSearch","type":"text","marks":[{"type":"code_inline"}]},{"text":" — without it, ","type":"text"},{"text":"select","type":"text","marks":[{"type":"code_inline"}]},{"text":" returning a new object on every call triggers unnecessary re-renders even when values are unchanged ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/render-optimizations.md#structural-sharing-with-fine-grained-selectors","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"createRootRouteWithContext\u003cYourContextType>()","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of ","type":"text"},{"text":"createRootRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":" when injecting shared dependencies (auth, query client, etc.) — this enforces the context type at router creation time and makes ","type":"text"},{"text":"context","type":"text","marks":[{"type":"code_inline"}]},{"text":" available with full type inference in all descendant ","type":"text"},{"text":"beforeLoad","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"loader","type":"text","marks":[{"type":"code_inline"}]},{"text":" functions ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/router-context.md#typed-router-context","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Property order inside ","type":"text"},{"text":"createFileRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"createRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"createRootRoute","type":"text","marks":[{"type":"code_inline"}]},{"text":" objects is inference-sensitive: ","type":"text"},{"text":"params","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"validateSearch","type":"text","marks":[{"type":"code_inline"}]},{"text":" must come before ","type":"text"},{"text":"loaderDeps","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"beforeLoad","type":"text","marks":[{"type":"code_inline"}]},{"text":" before ","type":"text"},{"text":"loader","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc. — wrong order causes type errors where ","type":"text"},{"text":"context","type":"text","marks":[{"type":"code_inline"}]},{"text":" from ","type":"text"},{"text":"beforeLoad","type":"text","marks":[{"type":"code_inline"}]},{"text":" isn't visible in ","type":"text"},{"text":"loader","type":"text","marks":[{"type":"code_inline"}]},{"text":". Install ","type":"text"},{"text":"@tanstack/eslint-plugin-router","type":"text","marks":[{"type":"code_inline"}]},{"text":" and enable the ","type":"text"},{"text":"create-route-property-order","type":"text","marks":[{"type":"code_inline"}]},{"text":" rule (it's fixable) ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/eslint/create-route-property-order.md#rule-details","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"retainSearchParams(['key'])","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"stripSearchParams(defaultValues)","type":"text","marks":[{"type":"code_inline"}]},{"text":" as ","type":"text"},{"text":"search.middlewares","type":"text","marks":[{"type":"code_inline"}]},{"text":" on a route rather than manually forwarding params in every ","type":"text"},{"text":"\u003cLink>","type":"text","marks":[{"type":"code_inline"}]},{"text":" — middlewares run automatically on all descendant links and on navigation, keeping the URL clean without repetitive spread patterns ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/search-params.md#transforming-search-with-search-middlewares","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When throwing ","type":"text"},{"text":"redirect()","type":"text","marks":[{"type":"code_inline"}]},{"text":" inside ","type":"text"},{"text":"beforeLoad","type":"text","marks":[{"type":"code_inline"}]},{"text":" error handlers, always re-throw errors identified by ","type":"text"},{"text":"isRedirect()","type":"text","marks":[{"type":"code_inline"}]},{"text":" before converting other errors — otherwise intentional redirects are swallowed as route errors ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/authenticated-routes.md#handling-auth-check-failures","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"linkOptions({ to, search, ... })","type":"text","marks":[{"type":"code_inline"}]},{"text":" to define reusable navigation targets instead of plain object literals — bare object literals infer ","type":"text"},{"text":"to","type":"text","marks":[{"type":"code_inline"}]},{"text":" as ","type":"text"},{"text":"string","type":"text","marks":[{"type":"code_inline"}]},{"text":" (matching every route) and defer type errors until the object is spread into ","type":"text"},{"text":"\u003cLink>","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"linkOptions","type":"text","marks":[{"type":"code_inline"}]},{"text":" validates the destination at definition time and the same value works in ","type":"text"},{"text":"\u003cLink>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"navigate()","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"redirect()","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/link-options.md#using-linkoptions-function-to-create-re-usable-options","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set ","type":"text"},{"text":"defaultPreload: 'intent'","type":"text","marks":[{"type":"code_inline"}]},{"text":" on the router to preload route data and code-split chunks on link hover — preloaded data is cached for 30 seconds (configurable via ","type":"text"},{"text":"defaultPreloadMaxAge","type":"text","marks":[{"type":"code_inline"}]},{"text":") and prevents loader waterfalls on navigation without any per-link configuration ","type":"text"},{"text":"source","type":"text","marks":[{"type":"link","attrs":{"href":"./references/docs/router/guide/preloading.md#supported-preloading-strategies","title":null}}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"tanstack-vue-router-skilld","author":"@skillopedia","source":{"stars":165,"repo_name":"vue-ecosystem-skills","origin_url":"https://github.com/harlan-zw/vue-ecosystem-skills/blob/HEAD/skills/tanstack-vue-router-skilld/SKILL.md","repo_owner":"harlan-zw","body_sha256":"7c310035cc4f118aac55c05bc2bc9526760b280d1bcf9dc2abde7c9d49fe20d5","cluster_key":"05bff187d2ab2e4c0e0be3cb750ae4539aa55b5106e81e2edc098f3e5fcc437b","clean_bundle":{"format":"clean-skill-bundle-v1","source":"harlan-zw/vue-ecosystem-skills/skills/tanstack-vue-router-skilld/SKILL.md","attachments":[{"id":"ec11c3c8-b96b-5023-92ae-d0dcd6c9e120","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ec11c3c8-b96b-5023-92ae-d0dcd6c9e120/attachment.md","path":"references/discussions/_INDEX.md","size":2107,"sha256":"5ed9ba8b7e69b1302ad90036f5cf39b7cf17600c7eb3e08e9e4705ea0ffedc07","contentType":"text/markdown; charset=utf-8"},{"id":"4606b9cd-b2d6-5e7f-8aac-09cefe46d0b8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4606b9cd-b2d6-5e7f-8aac-09cefe46d0b8/attachment.md","path":"references/discussions/discussion-6148.md","size":854,"sha256":"fe4c6a78299d0212d8cded135f4896e43dcb7d052c45b650b5143fae6698d67f","contentType":"text/markdown; charset=utf-8"},{"id":"643ffa90-4e49-5828-9d45-06892d5bcf07","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/643ffa90-4e49-5828-9d45-06892d5bcf07/attachment.md","path":"references/discussions/discussion-6174.md","size":2072,"sha256":"055d9413f2d9d0f118c08580d5810b52f13e71a37b168fe8666d1fbdf5751f41","contentType":"text/markdown; charset=utf-8"},{"id":"d26e7e30-2b62-52eb-9eeb-22306314f913","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d26e7e30-2b62-52eb-9eeb-22306314f913/attachment.md","path":"references/discussions/discussion-6176.md","size":1381,"sha256":"dc746bb307fb68b89d2bc86c4472b23fe46c6770665f59e2fe5e6cbf5507c18a","contentType":"text/markdown; charset=utf-8"},{"id":"53f717b7-d59b-5ce0-a9c2-a7399ccbe0a4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/53f717b7-d59b-5ce0-a9c2-a7399ccbe0a4/attachment.md","path":"references/discussions/discussion-6183.md","size":785,"sha256":"9ccfa219def08effb2c76a91f072608c10a768bd8f3b01a40b80fb00d85e7f4c","contentType":"text/markdown; charset=utf-8"},{"id":"e230bd18-db65-58c2-8f16-a3345baef589","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e230bd18-db65-58c2-8f16-a3345baef589/attachment.md","path":"references/discussions/discussion-6243.md","size":1037,"sha256":"1c5019bee4ea4dd33852dcae3b87acc1b9050d455b2d83c42ba7dc2ac644b53c","contentType":"text/markdown; charset=utf-8"},{"id":"eeeadbe2-6cf4-5e01-9c47-0fd1f2b4fac3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/eeeadbe2-6cf4-5e01-9c47-0fd1f2b4fac3/attachment.md","path":"references/discussions/discussion-6247.md","size":883,"sha256":"324e30c138259be2da5662ba3952d16a38f0bed17fe2fbd36e030b9c140709d6","contentType":"text/markdown; charset=utf-8"},{"id":"6ef8b077-7cd0-50bd-91a0-b415809910ac","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6ef8b077-7cd0-50bd-91a0-b415809910ac/attachment.md","path":"references/discussions/discussion-6289.md","size":1326,"sha256":"500fe65dd707f0179fc20e196cfcb4073fe854b49982cb1d10deae6bdfd3d52d","contentType":"text/markdown; charset=utf-8"},{"id":"96233489-7927-537e-8ecf-a37ae7e954b6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/96233489-7927-537e-8ecf-a37ae7e954b6/attachment.md","path":"references/discussions/discussion-6379.md","size":1018,"sha256":"626570c8e1ae0e6640e2bd8c0c58255aa04044265620b1eeb64f1dd85962043e","contentType":"text/markdown; charset=utf-8"},{"id":"b614547a-82fc-5f13-b681-05b4aa39429a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b614547a-82fc-5f13-b681-05b4aa39429a/attachment.md","path":"references/discussions/discussion-6380.md","size":1187,"sha256":"f03b38783ef18c5a1d1850d2eea17c6fa03faa9e4b3314e74aaf31f4abeea735","contentType":"text/markdown; charset=utf-8"},{"id":"2b0f690c-c2d4-5810-ac6e-4f8a72198064","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2b0f690c-c2d4-5810-ac6e-4f8a72198064/attachment.md","path":"references/discussions/discussion-6391.md","size":1070,"sha256":"01f2c3045eb6ec17303c5f331d75b0fb22b100338f31a468f8f218dfa928b2d9","contentType":"text/markdown; charset=utf-8"},{"id":"6a733cd8-282a-5154-9ae0-547cb4aaef14","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6a733cd8-282a-5154-9ae0-547cb4aaef14/attachment.md","path":"references/discussions/discussion-6402.md","size":908,"sha256":"7ac70e8bfd1ee4818d5e57ed5d08456314697eb831d0b7a3c754e2ba7470d0b8","contentType":"text/markdown; charset=utf-8"},{"id":"2255b082-fd4f-56bf-bed5-c29408a817c1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2255b082-fd4f-56bf-bed5-c29408a817c1/attachment.md","path":"references/discussions/discussion-6466.md","size":831,"sha256":"84bde1ab6a4b9b87144e961a009174082e00c2296b735306ea39913dba230284","contentType":"text/markdown; charset=utf-8"},{"id":"15ff4339-fc34-5857-9cb4-68cace1b02af","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/15ff4339-fc34-5857-9cb4-68cace1b02af/attachment.md","path":"references/discussions/discussion-6529.md","size":1262,"sha256":"502c82cdd1b45a2dccad6879c867ff6df8d784d483f22388cbd6251996b2651b","contentType":"text/markdown; charset=utf-8"},{"id":"fd17a969-485a-5afb-bfb1-d2eeddcb7608","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fd17a969-485a-5afb-bfb1-d2eeddcb7608/attachment.md","path":"references/discussions/discussion-6537.md","size":585,"sha256":"324df06884c9c064eb2d6095e5e126ec05264facd63bbac14ba90a1ddc69c046","contentType":"text/markdown; charset=utf-8"},{"id":"add6020c-bcd4-5a80-b90e-471802174043","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/add6020c-bcd4-5a80-b90e-471802174043/attachment.md","path":"references/discussions/discussion-6551.md","size":917,"sha256":"1e2020d80f6b8e0a895b3642c18230556e0aa9f9789ea76f3989472440a4ecf0","contentType":"text/markdown; charset=utf-8"},{"id":"fc8cd375-3698-567a-b5a1-9b5ece452af5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fc8cd375-3698-567a-b5a1-9b5ece452af5/attachment.md","path":"references/discussions/discussion-6552.md","size":837,"sha256":"7fa0ef2a43c425344cddfebcce87af6718b2c082a488ba57d95b09f7cefb0ac0","contentType":"text/markdown; charset=utf-8"},{"id":"f450ae78-b33b-54d0-857b-b151242f77d0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f450ae78-b33b-54d0-857b-b151242f77d0/attachment.md","path":"references/discussions/discussion-6572.md","size":1193,"sha256":"f67e2e54497ef37d269e25adb38714cfb9bb2c21e54f41f0cc950578843694d4","contentType":"text/markdown; charset=utf-8"},{"id":"f00dd0cd-7218-5d13-b52c-4eb4a9204313","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f00dd0cd-7218-5d13-b52c-4eb4a9204313/attachment.md","path":"references/discussions/discussion-6586.md","size":705,"sha256":"d510d1b1352d2be2f4da809863a97ca739c5f64588a44e64e446d46f5f7667bb","contentType":"text/markdown; charset=utf-8"},{"id":"61f6fdd2-2d39-567d-86ce-8ccc0645bfdc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/61f6fdd2-2d39-567d-86ce-8ccc0645bfdc/attachment.md","path":"references/discussions/discussion-6629.md","size":856,"sha256":"a881172ea0a7c1f273292c56b49a9a70d7467527579c2884246161bb35931d00","contentType":"text/markdown; charset=utf-8"},{"id":"60fb3d11-98e5-5ba8-bd09-6aa28af17140","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/60fb3d11-98e5-5ba8-bd09-6aa28af17140/attachment.md","path":"references/discussions/discussion-6646.md","size":1074,"sha256":"9e157415f5eca47287b43003c9544f295201f833b1e1047cb59aa71bf92f7aef","contentType":"text/markdown; charset=utf-8"},{"id":"706edd99-cdeb-515a-8004-237770f7fdfe","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/706edd99-cdeb-515a-8004-237770f7fdfe/attachment.md","path":"references/docs/_INDEX.md","size":31760,"sha256":"d8b413ab27b4f28f4c153133edf8c99931886f3e143647e346e2de57de9b642c","contentType":"text/markdown; charset=utf-8"},{"id":"f16840a2-14b8-5150-a0cc-16af610a97d1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f16840a2-14b8-5150-a0cc-16af610a97d1/attachment.md","path":"references/docs/router/api/file-based-routing.md","size":9611,"sha256":"9c0f3760341bfb2aff1c1e8dfd184237c7103c8989e98e5a57cf2d59632c8969","contentType":"text/markdown; charset=utf-8"},{"id":"3a1def6c-ce52-5e02-aa06-2f29ef86ebec","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3a1def6c-ce52-5e02-aa06-2f29ef86ebec/attachment.md","path":"references/docs/router/api/router.md","size":4463,"sha256":"3e65892423b8b2767d466fe673f0616f1c86ce14fc2dd58a5ef60aeba187384c","contentType":"text/markdown; charset=utf-8"},{"id":"2d1f25b4-c2a3-59d7-a08d-1e7717f94fad","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2d1f25b4-c2a3-59d7-a08d-1e7717f94fad/attachment.md","path":"references/docs/router/api/router/ActiveLinkOptionsType.md","size":1008,"sha256":"0a00b48e9852fb4fb33122d1e7a792de6888cce50771b9c513539c556fe4d6c1","contentType":"text/markdown; charset=utf-8"},{"id":"1b4ba442-6edb-5337-894e-4ba3cb4dee7d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1b4ba442-6edb-5337-894e-4ba3cb4dee7d/attachment.md","path":"references/docs/router/api/router/AsyncRouteComponentType.md","size":321,"sha256":"85ce9f1d704e62117ff136678f5d8cbd82796b8017d8bf22ad811a173131a46c","contentType":"text/markdown; charset=utf-8"},{"id":"945520d4-f674-5b45-898f-ab0c4d0bd34e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/945520d4-f674-5b45-898f-ab0c4d0bd34e/attachment.md","path":"references/docs/router/api/router/FileRouteClass.md","size":2151,"sha256":"36d2c7759bf958ad5024723c3c4f985541ef16aea419b0181bb6cadc91c3b884","contentType":"text/markdown; charset=utf-8"},{"id":"327367ef-1a3b-5f51-a302-b6492e50d481","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/327367ef-1a3b-5f51-a302-b6492e50d481/attachment.md","path":"references/docs/router/api/router/LinkOptionsType.md","size":1281,"sha256":"ca37d19e986f9178e3bf592ba24d6875f247192c454f0f1abf526d5962857826","contentType":"text/markdown; charset=utf-8"},{"id":"7892fcbc-5da2-5268-84c6-93a447a33fb3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7892fcbc-5da2-5268-84c6-93a447a33fb3/attachment.md","path":"references/docs/router/api/router/LinkPropsType.md","size":980,"sha256":"ca95d9e7dd7f4a6868dfc49fabddc39ebda79c9e24eacb7736839356e2cf6e24","contentType":"text/markdown; charset=utf-8"},{"id":"78cabb38-755c-5330-95b3-0635968a0b58","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/78cabb38-755c-5330-95b3-0635968a0b58/attachment.md","path":"references/docs/router/api/router/MatchRouteOptionsType.md","size":1251,"sha256":"cf504e9a66f810d39bf48fda7a42487322e1ce046351661467b67e63c516fe3b","contentType":"text/markdown; charset=utf-8"},{"id":"feb994cd-847b-5242-8f47-f82d1108894e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/feb994cd-847b-5242-8f47-f82d1108894e/attachment.md","path":"references/docs/router/api/router/NavigateOptionsType.md","size":2813,"sha256":"a85e443d0875f7f3375a6c90239aec4bfad5a31216997db7e98cf98e524ae03c","contentType":"text/markdown; charset=utf-8"},{"id":"4a1748c7-ee83-5e5b-a555-1d2fc0e3bf01","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4a1748c7-ee83-5e5b-a555-1d2fc0e3bf01/attachment.md","path":"references/docs/router/api/router/NotFoundErrorType.md","size":1745,"sha256":"dad74b0156beefe9017f916c9945990c2c540fa992907570c2775e5c07bdcda2","contentType":"text/markdown; charset=utf-8"},{"id":"49903fdd-5c1d-5309-aef6-794cb2e8af4f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/49903fdd-5c1d-5309-aef6-794cb2e8af4f/attachment.md","path":"references/docs/router/api/router/NotFoundRouteClass.md","size":1395,"sha256":"71a4fac255dff6c266bb07329fa073e7ad4188f10cdde5bd4376135801e1c010","contentType":"text/markdown; charset=utf-8"},{"id":"6525bae3-2282-58fc-8da1-607227f68773","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6525bae3-2282-58fc-8da1-607227f68773/attachment.md","path":"references/docs/router/api/router/ParsedHistoryStateType.md","size":386,"sha256":"d5a10cb7e2b533958cddfe0f80d635e7b91d60fd15421e8550cbc46547c68909","contentType":"text/markdown; charset=utf-8"},{"id":"1f00352c-c348-576d-9f41-c7198493dc59","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1f00352c-c348-576d-9f41-c7198493dc59/attachment.md","path":"references/docs/router/api/router/ParsedLocationType.md","size":521,"sha256":"ed574158fbd09aebd065ea466a77d155bc856afa46f536edb59d89c04c7e30d6","contentType":"text/markdown; charset=utf-8"},{"id":"9b2fff28-e62c-5a9c-9147-fd62459bb5b6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9b2fff28-e62c-5a9c-9147-fd62459bb5b6/attachment.md","path":"references/docs/router/api/router/RedirectType.md","size":1377,"sha256":"24d5b75d45873153242723b679523d1e51153087252a8ddb9164e65b30c98e79","contentType":"text/markdown; charset=utf-8"},{"id":"9e1a0f16-ae83-550f-915e-1172745665de","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9e1a0f16-ae83-550f-915e-1172745665de/attachment.md","path":"references/docs/router/api/router/RegisterType.md","size":653,"sha256":"916988271777be8c49df24ee545d3732160281eb24dd7396ed92c34b75c5eb7f","contentType":"text/markdown; charset=utf-8"},{"id":"af547cfb-7630-5444-8110-d87a5777a258","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/af547cfb-7630-5444-8110-d87a5777a258/attachment.md","path":"references/docs/router/api/router/RootRouteClass.md","size":1177,"sha256":"56fc508e04caa3624f1778533167c6aa49d424641caf8ce659e3d11bd413565e","contentType":"text/markdown; charset=utf-8"},{"id":"a7cf859e-8672-5023-9dc6-321cfb4a8826","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a7cf859e-8672-5023-9dc6-321cfb4a8826/attachment.md","path":"references/docs/router/api/router/RouteApiClass.md","size":1100,"sha256":"6fcd9875b2bb717f21b286e44fcfa76c3bf5c17a88beecf1b566eb5f87c4b34c","contentType":"text/markdown; charset=utf-8"},{"id":"8f16fac6-5106-5da7-8530-c12f16d452a6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8f16fac6-5106-5da7-8530-c12f16d452a6/attachment.md","path":"references/docs/router/api/router/RouteApiType.md","size":7978,"sha256":"ad3920ec18b733186346e2cc9e3a86f14f3e419382a65c472e9651daa01f62d3","contentType":"text/markdown; charset=utf-8"},{"id":"ed5bc245-6c1f-52ce-83e3-9ee2c6fc5662","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ed5bc245-6c1f-52ce-83e3-9ee2c6fc5662/attachment.md","path":"references/docs/router/api/router/RouteClass.md","size":1059,"sha256":"55feb9c4e4eca102ad31324149ad754fad323d2a28f62cd6eac9c21352b0db14","contentType":"text/markdown; charset=utf-8"},{"id":"f390490b-7d57-5bab-a0d7-f56d68c78d9b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f390490b-7d57-5bab-a0d7-f56d68c78d9b/attachment.md","path":"references/docs/router/api/router/RouteMaskType.md","size":653,"sha256":"a560b0000f4f9c75883b6f2b4062d7f6485bdb993f10586a9ef7f163c2040c53","contentType":"text/markdown; charset=utf-8"},{"id":"df46081b-8429-5a49-86ee-1abd4196e058","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/df46081b-8429-5a49-86ee-1abd4196e058/attachment.md","path":"references/docs/router/api/router/RouteMatchType.md","size":669,"sha256":"d3481c77a8ac8f472d57f530c2684049d172a3d8d2cf791de11c3a4e896ddbae","contentType":"text/markdown; charset=utf-8"},{"id":"9ce24271-f40d-55f2-a955-1ede15ba309e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9ce24271-f40d-55f2-a955-1ede15ba309e/attachment.md","path":"references/docs/router/api/router/RouteOptionsType.md","size":17328,"sha256":"1578da3c5f8b24cc3d408a525a79ce76b9c2a800333a5afb214ff7a6094150f8","contentType":"text/markdown; charset=utf-8"},{"id":"51441aac-01d3-5ec0-a1b7-ea5526ae3dae","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/51441aac-01d3-5ec0-a1b7-ea5526ae3dae/attachment.md","path":"references/docs/router/api/router/RouteType.md","size":1886,"sha256":"5a1a045cf83c28e953ba4246be2c99f803a719cc52d7e7f1e03ca7154b0563a8","contentType":"text/markdown; charset=utf-8"},{"id":"982de69f-0de9-56cf-9e65-ef42034290f7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/982de69f-0de9-56cf-9e65-ef42034290f7/attachment.md","path":"references/docs/router/api/router/RouterClass.md","size":960,"sha256":"9f4a02bd48b7226662aa0d0206f4afbdc008a2b9aa7e7e94bf4c260fe76dfbd7","contentType":"text/markdown; charset=utf-8"},{"id":"85fb0d53-36c9-57d9-b5e5-991a9319d98c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/85fb0d53-36c9-57d9-b5e5-991a9319d98c/attachment.md","path":"references/docs/router/api/router/RouterEventsType.md","size":2356,"sha256":"dce9d15278b208975f31eb4a7bd4cca3c27e84abd292b6d5cffac571a06c2ab7","contentType":"text/markdown; charset=utf-8"},{"id":"1ab4ecfe-0c99-5b42-bb82-44c0742bdde8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1ab4ecfe-0c99-5b42-bb82-44c0742bdde8/attachment.md","path":"references/docs/router/api/router/RouterOptionsType.md","size":13879,"sha256":"a10a2f9bee870c87377543480085d6851231a6dda1899f8bc66788eb58e3d9c4","contentType":"text/markdown; charset=utf-8"},{"id":"53f3bfb4-20f0-597c-913e-2a487dc35996","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/53f3bfb4-20f0-597c-913e-2a487dc35996/attachment.md","path":"references/docs/router/api/router/RouterStateType.md","size":1787,"sha256":"5a34ef8f2e195b823ddd362a05c816e08e176dc7885b750be437c3d43bc0f552","contentType":"text/markdown; charset=utf-8"},{"id":"d17c0352-e0d6-573e-809a-f736e34da651","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d17c0352-e0d6-573e-809a-f736e34da651/attachment.md","path":"references/docs/router/api/router/RouterType.md","size":9459,"sha256":"09ead9fd56001e6e1167509ec346cb3fe72bb817ad3df4427e2702813c601bb8","contentType":"text/markdown; charset=utf-8"},{"id":"5922383b-b5f4-5555-86bb-6ed3b70916d3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5922383b-b5f4-5555-86bb-6ed3b70916d3/attachment.md","path":"references/docs/router/api/router/ToMaskOptionsType.md","size":560,"sha256":"677d85ac455624380ce4de5a226f20c8c3b933224276c5eedd98d63ef7141cfa","contentType":"text/markdown; charset=utf-8"},{"id":"81ffdcf0-0c56-510f-a498-839226ef65c6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/81ffdcf0-0c56-510f-a498-839226ef65c6/attachment.md","path":"references/docs/router/api/router/ToOptionsType.md","size":761,"sha256":"6bb185471b4e522d4a999bafeb3346fab5758087d878a8d4a0d99eeec5a24e35","contentType":"text/markdown; charset=utf-8"},{"id":"3c9a0064-bca6-5bcb-9265-d0058681c944","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3c9a0064-bca6-5bcb-9265-d0058681c944/attachment.md","path":"references/docs/router/api/router/UseMatchRouteOptionsType.md","size":426,"sha256":"40105b19aa2c3eadf4413bfb856a3775a121e75caf2d0b74b9c69b4ceb0a86de","contentType":"text/markdown; charset=utf-8"},{"id":"8f199b81-547f-5a61-9691-458f76c905c9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8f199b81-547f-5a61-9691-458f76c905c9/attachment.md","path":"references/docs/router/api/router/ViewTransitionOptionsType.md","size":1155,"sha256":"9208ee8f62bbc3ff49aa87d7b66082438a773e726babe1120a43ec2ad3363e7f","contentType":"text/markdown; charset=utf-8"},{"id":"dfdc136f-327b-5a78-b153-cfa742d2a9ba","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dfdc136f-327b-5a78-b153-cfa742d2a9ba/attachment.md","path":"references/docs/router/api/router/awaitComponent.md","size":1076,"sha256":"aa07ba444469da0a8f921d0bc6789842c90cd2fda42161314a7e751f97a17055","contentType":"text/markdown; charset=utf-8"},{"id":"b1c88dd2-a498-5954-a223-ef29adf69dc4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b1c88dd2-a498-5954-a223-ef29adf69dc4/attachment.md","path":"references/docs/router/api/router/catchBoundaryComponent.md","size":1492,"sha256":"246a08f58f387b8c71152ff1c2851cd8e183e9a935a5359414485431d2cda621","contentType":"text/markdown; charset=utf-8"},{"id":"3cc00818-7e02-5bd2-80e6-ac37ab760d34","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3cc00818-7e02-5bd2-80e6-ac37ab760d34/attachment.md","path":"references/docs/router/api/router/catchNotFoundComponent.md","size":1212,"sha256":"12121f8dbf1a81867d37770106aa0a6e9045bc1fa83e3f31e5b02f5d79b1fbbe","contentType":"text/markdown; charset=utf-8"},{"id":"7f47e7b5-3ef4-5ba7-b5f1-306d19bebe1b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7f47e7b5-3ef4-5ba7-b5f1-306d19bebe1b/attachment.md","path":"references/docs/router/api/router/clientOnlyComponent.md","size":1212,"sha256":"4f5a918a560a7285855ecf3c8c0babfd434b8f59cf941f6a085b13df20ec9d2f","contentType":"text/markdown; charset=utf-8"},{"id":"171d608e-0640-5300-a4f6-b6d101850c6d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/171d608e-0640-5300-a4f6-b6d101850c6d/attachment.md","path":"references/docs/router/api/router/createFileRouteFunction.md","size":1338,"sha256":"7cb285ab5c0cefa55543d2e4cb7a5dd1c1c95c67f08ae79cac24b8a703d2ca33","contentType":"text/markdown; charset=utf-8"},{"id":"19063cb8-6378-5359-9bb7-1675eb411573","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/19063cb8-6378-5359-9bb7-1675eb411573/attachment.md","path":"references/docs/router/api/router/createLazyFileRouteFunction.md","size":1636,"sha256":"d13cbe23df3270e9780ea0b7c56198337b969dca82501205e8101e1bdb6834b1","contentType":"text/markdown; charset=utf-8"},{"id":"893f7da6-3c40-5d37-8ce9-442b71ae57d8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/893f7da6-3c40-5d37-8ce9-442b71ae57d8/attachment.md","path":"references/docs/router/api/router/createLazyRouteFunction.md","size":1950,"sha256":"20d842c0e4e4a64c22ca520085e3b2deb7e5905a579e5520f1a65281a6f8d7f2","contentType":"text/markdown; charset=utf-8"},{"id":"6cfadee5-1458-5e4d-ac0d-3152a04dd948","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6cfadee5-1458-5e4d-ac0d-3152a04dd948/attachment.md","path":"references/docs/router/api/router/createRootRouteFunction.md","size":884,"sha256":"2083b7269c26d71329e1bc9ae1ed5bf44684f035c4093d63bb9ed01d9e0cdf62","contentType":"text/markdown; charset=utf-8"},{"id":"d91e6145-c8f4-5d9f-bda5-3ac56b46f438","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d91e6145-c8f4-5d9f-bda5-3ac56b46f438/attachment.md","path":"references/docs/router/api/router/createRootRouteWithContextFunction.md","size":1369,"sha256":"1aa29aa86f8926fd21d61b0151e4bd8055197345d0cb9b92ca2705c317e482af","contentType":"text/markdown; charset=utf-8"},{"id":"fddc5a58-dd66-56b5-9722-d5dc4f659521","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fddc5a58-dd66-56b5-9722-d5dc4f659521/attachment.md","path":"references/docs/router/api/router/createRouteFunction.md","size":850,"sha256":"dd6cc2eafca27ae0901de7a6a6c9c49d2c183a6f9e6c78b906170ffb77fb2233","contentType":"text/markdown; charset=utf-8"},{"id":"c745ab27-5978-5196-8482-55b43c656c8d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c745ab27-5978-5196-8482-55b43c656c8d/attachment.md","path":"references/docs/router/api/router/createRouteMaskFunction.md","size":891,"sha256":"3d622f62536955ad8713363c1c693493bc0a3570fd805f1af6e104696a633c6a","contentType":"text/markdown; charset=utf-8"},{"id":"cd27037e-abc6-5882-89b5-def06ec2cc45","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cd27037e-abc6-5882-89b5-def06ec2cc45/attachment.md","path":"references/docs/router/api/router/createRouterFunction.md","size":729,"sha256":"586e9be19115defeec1ac0f2fe228b1555648ef1fad0c602bfd30b804c16ee9d","contentType":"text/markdown; charset=utf-8"},{"id":"439194c2-4b34-50b0-b6dc-43ba68ab365a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/439194c2-4b34-50b0-b6dc-43ba68ab365a/attachment.md","path":"references/docs/router/api/router/defaultGlobalNotFoundComponent.md","size":327,"sha256":"c8e2f3e8f41a691afd78e7e0e1d253e19e668e0ae39ee121bf22f30867d95963","contentType":"text/markdown; charset=utf-8"},{"id":"6f14918a-b6fa-57ad-a6aa-765884391135","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6f14918a-b6fa-57ad-a6aa-765884391135/attachment.md","path":"references/docs/router/api/router/deferFunction.md","size":1322,"sha256":"99c6b24a86d6043290231c251346af6955e333ed7b51f457971431e9bfb6c219","contentType":"text/markdown; charset=utf-8"},{"id":"9cf66f7d-f51a-5bf8-b7dd-43019e4b09f9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9cf66f7d-f51a-5bf8-b7dd-43019e4b09f9/attachment.md","path":"references/docs/router/api/router/errorComponentComponent.md","size":897,"sha256":"be582135c78fff61a6500c321103ba3fd103a01c8fd491bef7258f97fc850b43","contentType":"text/markdown; charset=utf-8"},{"id":"ef639383-3fa0-5c1c-a97c-cd4ed19aaa0f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ef639383-3fa0-5c1c-a97c-cd4ed19aaa0f/attachment.md","path":"references/docs/router/api/router/getRouteApiFunction.md","size":921,"sha256":"4951a80522f9796cf8dd426cd357a76c7225f6672b98ec573837faaf2825ceaa","contentType":"text/markdown; charset=utf-8"},{"id":"93ed0d67-2946-5ee9-b449-4f62b60efd45","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/93ed0d67-2946-5ee9-b449-4f62b60efd45/attachment.md","path":"references/docs/router/api/router/historyStateInterface.md","size":568,"sha256":"cf5aaf4234dc74f4a4453c319768ec6d61b98706059ab2980d5bb5b5eed9c33f","contentType":"text/markdown; charset=utf-8"},{"id":"a0402063-30c7-561d-a159-6a1f20d4010f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a0402063-30c7-561d-a159-6a1f20d4010f/attachment.md","path":"references/docs/router/api/router/isNotFoundFunction.md","size":736,"sha256":"c90b95407cd8b80fefb8634d5861493884224dd37b10f2ce41945db760ddb5a0","contentType":"text/markdown; charset=utf-8"},{"id":"e4367a0a-2501-5060-98ef-eee95885ce16","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e4367a0a-2501-5060-98ef-eee95885ce16/attachment.md","path":"references/docs/router/api/router/isRedirectFunction.md","size":616,"sha256":"bdc5aca1af1dd08515573688aac036e8a03d2cb01d16bd863c3ba3af4855dd0a","contentType":"text/markdown; charset=utf-8"},{"id":"00ef3893-3282-5b6e-be27-f3ff73f07826","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/00ef3893-3282-5b6e-be27-f3ff73f07826/attachment.md","path":"references/docs/router/api/router/lazyRouteComponentFunction.md","size":1266,"sha256":"0500dc33d5f541c4d1c597a52e72fa8e4fd6dfa8020270a02651aa9f5ed1e7ad","contentType":"text/markdown; charset=utf-8"},{"id":"1ebe77e9-ca41-59fb-9b46-4cc4b4998113","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1ebe77e9-ca41-59fb-9b46-4cc4b4998113/attachment.md","path":"references/docs/router/api/router/linkComponent.md","size":1261,"sha256":"01f4198af5a093f9227a9b79506638bfd1fd870291dbfaf4150a223151d079a6","contentType":"text/markdown; charset=utf-8"},{"id":"49a045a7-7622-5ac1-82cd-387281da9c58","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/49a045a7-7622-5ac1-82cd-387281da9c58/attachment.md","path":"references/docs/router/api/router/linkOptions.md","size":729,"sha256":"6b948b5c31d8fdeac3c23cca08f6435b109092650f5dbf59eef9f2bf5b8a9a29","contentType":"text/markdown; charset=utf-8"},{"id":"f00bdf66-1b10-56cc-b566-850f70802064","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f00bdf66-1b10-56cc-b566-850f70802064/attachment.md","path":"references/docs/router/api/router/matchRouteComponent.md","size":1247,"sha256":"d57784b308e304bdf93a4f7409875bdc99bd23972ad2ac41ef2e618cb31a7209","contentType":"text/markdown; charset=utf-8"},{"id":"cb5535c1-6714-5568-8100-42bf54f9740d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cb5535c1-6714-5568-8100-42bf54f9740d/attachment.md","path":"references/docs/router/api/router/navigateComponent.md","size":512,"sha256":"227dec25e5816b0d1b3f573c688aefcdaecea18706d889dc5e6518922ff89bcd","contentType":"text/markdown; charset=utf-8"},{"id":"796480ba-c8d4-5844-a7e9-d7df6514056b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/796480ba-c8d4-5844-a7e9-d7df6514056b/attachment.md","path":"references/docs/router/api/router/notFoundComponentComponent.md","size":1078,"sha256":"4c81f63bbeca8af6342b4e60ccbd2f39634c15886e687e6aca5f6b9d1e49237d","contentType":"text/markdown; charset=utf-8"},{"id":"9a9cfe70-cae7-54e1-8f19-f32ff25f8ae6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9a9cfe70-cae7-54e1-8f19-f32ff25f8ae6/attachment.md","path":"references/docs/router/api/router/notFoundFunction.md","size":1219,"sha256":"dd1a893589e6f33a4aeb236f167fe9dff4a2cf65acc612a6c553384d4a361d43","contentType":"text/markdown; charset=utf-8"},{"id":"0704772d-9b11-59ae-b72d-72e63384b88d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0704772d-9b11-59ae-b72d-72e63384b88d/attachment.md","path":"references/docs/router/api/router/outletComponent.md","size":379,"sha256":"df61e021460d2038f8d4d2ce5a1f945a84f1a97bad7fc9d0c8848d98afc3b9a4","contentType":"text/markdown; charset=utf-8"},{"id":"a6fa5e54-e583-5121-9ef7-54829d3d435e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a6fa5e54-e583-5121-9ef7-54829d3d435e/attachment.md","path":"references/docs/router/api/router/redirectFunction.md","size":3261,"sha256":"ed9b43900a844596549381f765dad9a8b523872abf1873c1f6191a49e5de91de","contentType":"text/markdown; charset=utf-8"},{"id":"e64183b8-d9a9-5daa-97e1-92edd81427d3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e64183b8-d9a9-5daa-97e1-92edd81427d3/attachment.md","path":"references/docs/router/api/router/retainSearchParamsFunction.md","size":1191,"sha256":"64dc22884446a0048db9780d06efcd1f655f079eadf75477e9e38dd7e4acf078","contentType":"text/markdown; charset=utf-8"},{"id":"b0edae09-6bce-5b88-9efe-78df63c8e1a4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b0edae09-6bce-5b88-9efe-78df63c8e1a4/attachment.md","path":"references/docs/router/api/router/rootRouteWithContextFunction.md","size":1533,"sha256":"bbc339d430bf70b5ba72bb4fe92f7e99eeba3888ce66c1f26eb8927d1a09be40","contentType":"text/markdown; charset=utf-8"},{"id":"42b5d014-92b9-5d3c-b552-b3b15a7195a6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/42b5d014-92b9-5d3c-b552-b3b15a7195a6/attachment.md","path":"references/docs/router/api/router/stripSearchParamsFunction.md","size":2160,"sha256":"d39c940b56725050b46c21a72e9aa8eb2d66d35126a6c938ea4b429b30771a4e","contentType":"text/markdown; charset=utf-8"},{"id":"e1c35d0e-19a2-589f-a001-4a8f16fe9e86","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e1c35d0e-19a2-589f-a001-4a8f16fe9e86/attachment.md","path":"references/docs/router/api/router/useAwaitedHook.md","size":770,"sha256":"fe214136c96a0f2614ffa5c1f7bb25e6d5a91b9b014d6b066a67b55dff3fe435","contentType":"text/markdown; charset=utf-8"},{"id":"de9d5493-26a6-5e8e-b19a-4c14863e0a87","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/de9d5493-26a6-5e8e-b19a-4c14863e0a87/attachment.md","path":"references/docs/router/api/router/useBlockerHook.md","size":5348,"sha256":"3e714ae28778bcad0f2baa8f7c0e51608f6530a15b4db3bef3ee774563ec31d3","contentType":"text/markdown; charset=utf-8"},{"id":"777784e3-a6ba-5b01-9df8-9a29400a5845","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/777784e3-a6ba-5b01-9df8-9a29400a5845/attachment.md","path":"references/docs/router/api/router/useCanGoBack.md","size":1028,"sha256":"0c785ca250d29391d61601bbdce110fa85d01faca5aa0c55f36564b41e7b2212","contentType":"text/markdown; charset=utf-8"},{"id":"67a1ec3a-5216-53c3-9dd9-198fa84ba04a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/67a1ec3a-5216-53c3-9dd9-198fa84ba04a/attachment.md","path":"references/docs/router/api/router/useChildMatchesHook.md","size":1546,"sha256":"54d0b1261b2a4be2482cab8306d798aa249ce6835e6bdd7044a1c36eee1d3eb4","contentType":"text/markdown; charset=utf-8"},{"id":"13f28c6b-f960-5663-a2c8-51188f04d012","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/13f28c6b-f960-5663-a2c8-51188f04d012/attachment.md","path":"references/docs/router/api/router/useLinkPropsHook.md","size":1081,"sha256":"035368c4aa7a203ee522552952e0448f6b7e7f2902eb0f0177394ccd77132659","contentType":"text/markdown; charset=utf-8"},{"id":"edbf87c3-cef8-55ac-91b3-f74c319b1eb2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/edbf87c3-cef8-55ac-91b3-f74c319b1eb2/attachment.md","path":"references/docs/router/api/router/useLoaderDataHook.md","size":1841,"sha256":"b0f6b428183c4a38f20d1b738935404eb0e2eb674e4f87e0ba53dd862953ae0d","contentType":"text/markdown; charset=utf-8"},{"id":"5ee9b80e-2f01-53d0-8c72-15a835e7c1c0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5ee9b80e-2f01-53d0-8c72-15a835e7c1c0/attachment.md","path":"references/docs/router/api/router/useLoaderDepsHook.md","size":1362,"sha256":"29b4d1b816b2e8581d02c5f0a660fc69e38f86493fe5856e99c4513c55c0d0d8","contentType":"text/markdown; charset=utf-8"},{"id":"8f4c9fbe-e68d-56b5-ab66-237101261555","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8f4c9fbe-e68d-56b5-ab66-237101261555/attachment.md","path":"references/docs/router/api/router/useLocationHook.md","size":1012,"sha256":"46aa98af85d12e7938fb4817564fd8fd81db67ca9e938a3bbed33f5de2c643ed","contentType":"text/markdown; charset=utf-8"},{"id":"11e09038-d4da-567c-9f84-8b0656761527","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/11e09038-d4da-567c-9f84-8b0656761527/attachment.md","path":"references/docs/router/api/router/useMatchHook.md","size":2900,"sha256":"7c2335e9ab9d2e763dbdaa028986bbd45a86b63b69f7baa715c7780b8d1f5a0c","contentType":"text/markdown; charset=utf-8"},{"id":"338169f2-686d-53a6-8917-021562af4d3f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/338169f2-686d-53a6-8917-021562af4d3f/attachment.md","path":"references/docs/router/api/router/useMatchRouteHook.md","size":2990,"sha256":"a9981c1c7739e0d020ee50df21b1a4d5a0c7c7f6aa9fc1310ba1abb2f87f3be2","contentType":"text/markdown; charset=utf-8"},{"id":"26584c55-94fe-552e-92cb-221f0d8be774","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/26584c55-94fe-552e-92cb-221f0d8be774/attachment.md","path":"references/docs/router/api/router/useMatchesHook.md","size":1502,"sha256":"eca5a7eab1e3c2d93f09c7f2a9d0b705470f152e42bb384aef444b8422fec1c8","contentType":"text/markdown; charset=utf-8"},{"id":"8aafa9ac-e572-5514-93e2-bb6e49960de5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8aafa9ac-e572-5514-93e2-bb6e49960de5/attachment.md","path":"references/docs/router/api/router/useNavigateHook.md","size":2044,"sha256":"026730af7dec16484bf7dc24ddd591d1d12acdd0fa141d5e3bc80fe28bb19af3","contentType":"text/markdown; charset=utf-8"},{"id":"79cd5446-82d7-5dfd-8619-d2e4adedc7a7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/79cd5446-82d7-5dfd-8619-d2e4adedc7a7/attachment.md","path":"references/docs/router/api/router/useParamsHook.md","size":1872,"sha256":"5da1560eb41b40c518bdccdf5483e8fbedb20502c84e68b8070a01075412b3dc","contentType":"text/markdown; charset=utf-8"},{"id":"6a2e1cc3-2173-5505-9470-75559d89b79e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6a2e1cc3-2173-5505-9470-75559d89b79e/attachment.md","path":"references/docs/router/api/router/useParentMatchesHook.md","size":1591,"sha256":"3c7d78ea27d148de44eb8d73f3cfe42a76fd4dcac4b214cc89935075c8338720","contentType":"text/markdown; charset=utf-8"},{"id":"18d50b43-a8df-5063-b5df-1f2a912bfb5b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/18d50b43-a8df-5063-b5df-1f2a912bfb5b/attachment.md","path":"references/docs/router/api/router/useRouteContextHook.md","size":1103,"sha256":"3ff5819021295dd65d4edbcb4b4c69d21513d54ecbc7cf3e2809c1ba19670dbe","contentType":"text/markdown; charset=utf-8"},{"id":"ae8e2a5e-5e6c-5e51-9140-d37039dbe211","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ae8e2a5e-5e6c-5e51-9140-d37039dbe211/attachment.md","path":"references/docs/router/api/router/useRouterHook.md","size":738,"sha256":"25cb5ae020a76b912c69803795d065d8c57589b7b51cd1baa4f6cbc881d29e8a","contentType":"text/markdown; charset=utf-8"},{"id":"96cddd51-7599-520f-8e4c-7a6e1c6d985b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/96cddd51-7599-520f-8e4c-7a6e1c6d985b/attachment.md","path":"references/docs/router/api/router/useRouterStateHook.md","size":1543,"sha256":"9948f3dfbb15cdf47535b8336cc21a294b62c6ee2e2e3a8121928415a27d63cb","contentType":"text/markdown; charset=utf-8"},{"id":"13b2b4d1-e20e-5080-8072-d2acdb7b062c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/13b2b4d1-e20e-5080-8072-d2acdb7b062c/attachment.md","path":"references/docs/router/api/router/useSearchHook.md","size":2152,"sha256":"c9a081cfea20127abbaa3d52572b90dab0bf99839e342ac520df855d483f8a14","contentType":"text/markdown; charset=utf-8"},{"id":"0fe4eeb6-cbfa-5cb5-9f30-2341ea320a7e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0fe4eeb6-cbfa-5cb5-9f30-2341ea320a7e/attachment.md","path":"references/docs/router/comparison.md","size":14657,"sha256":"9b3e9d04da4e6fef01d785dbbde02866748848228a53ea7628a4fc8f5efccec4","contentType":"text/markdown; charset=utf-8"},{"id":"608c255e-6407-5133-8709-5bc9f2d362b9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/608c255e-6407-5133-8709-5bc9f2d362b9/attachment.md","path":"references/docs/router/decisions-on-dx.md","size":13758,"sha256":"7cb7b25a49dc70c463f437cb70b3c47cc062e7485064d654125ff961e46f73ee","contentType":"text/markdown; charset=utf-8"},{"id":"fd6a7eb0-21ce-5852-83ae-1ca6985f15ad","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fd6a7eb0-21ce-5852-83ae-1ca6985f15ad/attachment.md","path":"references/docs/router/devtools.md","size":7487,"sha256":"4b1fac6a5fdc7d5661899c9d1374357e4f604e7aedf2c19c415fa347afba7d16","contentType":"text/markdown; charset=utf-8"},{"id":"68881bcf-3b2e-5f64-8185-110eb2926cbe","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/68881bcf-3b2e-5f64-8185-110eb2926cbe/attachment.md","path":"references/docs/router/eslint/create-route-property-order.md","size":2371,"sha256":"f6dd6fb9435351fb4f61bf2fa840699c46af860acf0711858072d07facbe185e","contentType":"text/markdown; charset=utf-8"},{"id":"95016784-3183-561b-b0f3-dbd938524867","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/95016784-3183-561b-b0f3-dbd938524867/attachment.md","path":"references/docs/router/eslint/eslint-plugin-router.md","size":3428,"sha256":"0ec4cca9e4ff345bb726005cccf12c87215b5ed2b8e4b2f8e404229c267617cd","contentType":"text/markdown; charset=utf-8"},{"id":"99aece47-f215-55c5-bf9d-dee396aebedf","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/99aece47-f215-55c5-bf9d-dee396aebedf/attachment.md","path":"references/docs/router/faq.md","size":4907,"sha256":"f5b5bea9cbe34ef432e62db74d2dd8c3aa434536732b8f0090b4451f59a1da19","contentType":"text/markdown; charset=utf-8"},{"id":"ca3ff6cc-4b36-5db7-bbd4-d162a113614e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ca3ff6cc-4b36-5db7-bbd4-d162a113614e/attachment.md","path":"references/docs/router/guide/authenticated-routes.md","size":10115,"sha256":"b5bf60fd7e26f2aad0582daabfd44f1c98489844db1a6bcb0569e15698a93dd5","contentType":"text/markdown; charset=utf-8"},{"id":"cecc9ade-a832-5011-83df-4267c2e606d9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cecc9ade-a832-5011-83df-4267c2e606d9/attachment.md","path":"references/docs/router/guide/automatic-code-splitting.md","size":10559,"sha256":"f3661e96c9330beeb027aa83388618cf8a8ac28c57ae1ad23c1ddd957e780c15","contentType":"text/markdown; charset=utf-8"},{"id":"75053f37-96d8-5ff1-8fe2-b5c5915bb1f6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/75053f37-96d8-5ff1-8fe2-b5c5915bb1f6/attachment.md","path":"references/docs/router/guide/code-splitting.md","size":12671,"sha256":"74d52331fc5c2d419d83a2d9787ea8d70547d4af58fb76c9829f2b61ea78121c","contentType":"text/markdown; charset=utf-8"},{"id":"9a44181c-b079-50b9-a538-54fcafc17b8f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9a44181c-b079-50b9-a538-54fcafc17b8f/attachment.md","path":"references/docs/router/guide/creating-a-router.md","size":3407,"sha256":"0b20d0b8bdcc6de0269fd760ffaeecd81fbe0929bd24e5b7774fb8a55e1f7608","contentType":"text/markdown; charset=utf-8"},{"id":"00fc26ad-b0cd-53f6-a550-02f11fac5dfc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/00fc26ad-b0cd-53f6-a550-02f11fac5dfc/attachment.md","path":"references/docs/router/guide/custom-link.md","size":7530,"sha256":"382ff1ff7c2c5858af91a4fe1d79789fecb7916a2ad6fc030d3e66769197de70","contentType":"text/markdown; charset=utf-8"},{"id":"ca69f9bc-01a2-5195-9eef-656258d72655","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ca69f9bc-01a2-5195-9eef-656258d72655/attachment.md","path":"references/docs/router/guide/custom-search-param-serialization.md","size":10022,"sha256":"e2d78435df2be05becbb0f84a4d409d44d42e6f7d7fc9441b43a8afd278b28f3","contentType":"text/markdown; charset=utf-8"},{"id":"fb85ad4b-854f-53b6-8d98-570f870ffd35","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fb85ad4b-854f-53b6-8d98-570f870ffd35/attachment.md","path":"references/docs/router/guide/data-loading.md","size":28274,"sha256":"4fbade1a14262cbfec27077455b5b079886ce44037d6a15f7763ebd0873f389e","contentType":"text/markdown; charset=utf-8"},{"id":"7bc3576f-2c3b-56c3-ae77-b1d76760961b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7bc3576f-2c3b-56c3-ae77-b1d76760961b/attachment.md","path":"references/docs/router/guide/data-mutations.md","size":5642,"sha256":"52924f67151e53e69548eee47d280ec5f9a54ed459fc8b1d9b6891b42adff2cb","contentType":"text/markdown; charset=utf-8"},{"id":"f3ddd30a-3c29-5e78-ab14-5c112c3dc42b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f3ddd30a-3c29-5e78-ab14-5c112c3dc42b/attachment.md","path":"references/docs/router/guide/deferred-data-loading.md","size":7641,"sha256":"47614b8d0afe1b830dd5324142815a1a258bccb3e653ff580eb4106ae0bab82b","contentType":"text/markdown; charset=utf-8"},{"id":"e8b3d573-a52f-5d93-bd74-e81f045fb91e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e8b3d573-a52f-5d93-bd74-e81f045fb91e/attachment.md","path":"references/docs/router/guide/document-head-management.md","size":7528,"sha256":"4eb1fc4814ae99e15095adb72f198c4e15ba05bf02dfd1444e42a9f2bc92acf6","contentType":"text/markdown; charset=utf-8"},{"id":"c6a5a0c6-4782-5c0a-9979-421d84cbe65d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c6a5a0c6-4782-5c0a-9979-421d84cbe65d/attachment.md","path":"references/docs/router/guide/external-data-loading.md","size":6842,"sha256":"eeea0f91a75e858758bbbf8a2ea39be1a19828b4d6be61c6e2ea70a7e7f7dab5","contentType":"text/markdown; charset=utf-8"},{"id":"79816582-0862-5ca2-a904-773a2cffab4d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/79816582-0862-5ca2-a904-773a2cffab4d/attachment.md","path":"references/docs/router/guide/history-types.md","size":2813,"sha256":"bda3ab2993db49047d055dc5fc1df103af0e87f4046cde76fe220664e6440cea","contentType":"text/markdown; charset=utf-8"},{"id":"454a2568-ecd1-576f-b515-b4ba4b68f64c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/454a2568-ecd1-576f-b515-b4ba4b68f64c/attachment.md","path":"references/docs/router/guide/internationalization-i18n.md","size":5685,"sha256":"206a7ca82fe17b91495d25f89a54d85db69ecfc725279af1e0387e7b0c1a316c","contentType":"text/markdown; charset=utf-8"},{"id":"ffa47970-c2ee-5475-b2dc-1bdada1d2994","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ffa47970-c2ee-5475-b2dc-1bdada1d2994/attachment.md","path":"references/docs/router/guide/link-options.md","size":3331,"sha256":"88e1cea274ea28093bc6ff2a170e9c583cc0a2c1f2a9d3ec8c0225c74c88becb","contentType":"text/markdown; charset=utf-8"},{"id":"582105f8-fe8b-5fcb-a651-2d18bdcc2eb2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/582105f8-fe8b-5fcb-a651-2d18bdcc2eb2/attachment.md","path":"references/docs/router/guide/navigation-blocking.md","size":10705,"sha256":"437ef93d6ca8731b5811f89d1ad7cd37b8e61488d592ba01e9fcb5a060e725e4","contentType":"text/markdown; charset=utf-8"},{"id":"f2bdbc4a-b509-5396-b01e-adc51dc2e291","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f2bdbc4a-b509-5396-b01e-adc51dc2e291/attachment.md","path":"references/docs/router/guide/navigation.md","size":29831,"sha256":"ad929b80abc3b06b24c4580bad1dc0738a5c3ad6a89d7e7d3e04f84e4c337aba","contentType":"text/markdown; charset=utf-8"},{"id":"3b68bbe3-653c-57eb-8b02-f2996ee517a3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3b68bbe3-653c-57eb-8b02-f2996ee517a3/attachment.md","path":"references/docs/router/guide/not-found-errors.md","size":13203,"sha256":"66bf2169f2284386322e5025103e58873f0fcf7c57bc5536073188b7828364dc","contentType":"text/markdown; charset=utf-8"},{"id":"5f86dce6-488f-5fb8-8dd8-c53211a6d629","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5f86dce6-488f-5fb8-8dd8-c53211a6d629/attachment.md","path":"references/docs/router/guide/outlets.md","size":1378,"sha256":"a1bcc43372e6654799e7f86181fcfe40b3a0e1a9d12852a3d8ca1bc46876b26b","contentType":"text/markdown; charset=utf-8"},{"id":"637663d8-969e-5c18-ba2a-bd1f7cc2b5d5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/637663d8-969e-5c18-ba2a-bd1f7cc2b5d5/attachment.md","path":"references/docs/router/guide/parallel-routes.md","size":73,"sha256":"6562e421b0f3cfa9f58fe9d6422f38a1368fdd9f31f88ef2a5d136bab6b49e97","contentType":"text/markdown; charset=utf-8"},{"id":"ea6a35cd-8055-5fbf-87fa-f36db1535000","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ea6a35cd-8055-5fbf-87fa-f36db1535000/attachment.md","path":"references/docs/router/guide/path-params.md","size":33211,"sha256":"e4c0d6560e140b5c7de692affa95fb8c86430e7faf6e7a21ee2338c3976ac8d9","contentType":"text/markdown; charset=utf-8"},{"id":"8af3c13c-eabc-5711-93f6-60f3e151b156","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8af3c13c-eabc-5711-93f6-60f3e151b156/attachment.md","path":"references/docs/router/guide/preloading.md","size":7611,"sha256":"cfed6acd7a65518997211063834a23208daa2e3d4bd3387dc5da929efc09e2d8","contentType":"text/markdown; charset=utf-8"},{"id":"36dfb24b-a330-5ab6-a824-686f8a7cc3d7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/36dfb24b-a330-5ab6-a824-686f8a7cc3d7/attachment.md","path":"references/docs/router/guide/render-optimizations.md","size":2938,"sha256":"97eb6b4e715873cd3cb30a67efe7696b1a792f197f0a8d362c32252f2fe07da6","contentType":"text/markdown; charset=utf-8"},{"id":"b99de2ee-72c5-5291-8fcc-e65bf9634847","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b99de2ee-72c5-5291-8fcc-e65bf9634847/attachment.md","path":"references/docs/router/guide/route-masking.md","size":6785,"sha256":"49952446bacf1271998b327d2a95a895d6dad716bde581dc2335f7a3c4870d5a","contentType":"text/markdown; charset=utf-8"},{"id":"ab91ee0a-9f01-5b75-be84-6fa9ff9b63fb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ab91ee0a-9f01-5b75-be84-6fa9ff9b63fb/attachment.md","path":"references/docs/router/guide/router-context.md","size":12303,"sha256":"2bdf4bab3d60e8972856db61f81e303309919c76b370ef2358ba3c7931ac5e82","contentType":"text/markdown; charset=utf-8"},{"id":"923d45e5-7636-532f-8341-b53c9916c3d9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/923d45e5-7636-532f-8341-b53c9916c3d9/attachment.md","path":"references/docs/router/guide/scroll-restoration.md","size":8800,"sha256":"775fea4b267092cc6534924b12a60c11c7553f85d4de283f3804ed586d47488b","contentType":"text/markdown; charset=utf-8"},{"id":"40bac8a9-94d7-5fd2-9d00-37e18a935f52","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/40bac8a9-94d7-5fd2-9d00-37e18a935f52/attachment.md","path":"references/docs/router/guide/search-params.md","size":24831,"sha256":"6f4bbeb7a4859a1c23e3b79dd3704a9c23250d98a3ebfcecef963573bf583264","contentType":"text/markdown; charset=utf-8"},{"id":"ba6ca3bc-4212-522a-8927-6deb4b6e4c13","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ba6ca3bc-4212-522a-8927-6deb4b6e4c13/attachment.md","path":"references/docs/router/guide/ssr.md","size":12770,"sha256":"5cea2ccf7822fcff73eedf2db38f5215394471bcc5451f8e301c24b64611e5b3","contentType":"text/markdown; charset=utf-8"},{"id":"bc9b7e2d-684c-5135-8565-cb5dd9080010","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bc9b7e2d-684c-5135-8565-cb5dd9080010/attachment.md","path":"references/docs/router/guide/static-route-data.md","size":5266,"sha256":"2aa92fd924d7c11899addb6deb4f85cf64e134df8c6c70e2af075b7109cd05ce","contentType":"text/markdown; charset=utf-8"},{"id":"9b849994-7a45-5a70-8214-f9f05ef5f620","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9b849994-7a45-5a70-8214-f9f05ef5f620/attachment.md","path":"references/docs/router/guide/type-safety.md","size":12468,"sha256":"fc0e380d3b50cfc8574b21204337c898d3330a2e290921e9a9f71c1c79205de4","contentType":"text/markdown; charset=utf-8"},{"id":"e96b1660-8e0d-542f-80be-984ccf713dc0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e96b1660-8e0d-542f-80be-984ccf713dc0/attachment.md","path":"references/docs/router/guide/type-utilities.md","size":9576,"sha256":"a4d46210b5fb0dc2de207c0bf3d82f7c88ce51b050afe1261745c528d19d3c79","contentType":"text/markdown; charset=utf-8"},{"id":"5cd28d5e-d741-574f-b1c9-516ad0e194ae","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5cd28d5e-d741-574f-b1c9-516ad0e194ae/attachment.md","path":"references/docs/router/guide/url-rewrites.md","size":14962,"sha256":"74ffd300743f3f3696490ace7b010a9eb359de85b3a5ceeb6c43523469e6c015","contentType":"text/markdown; charset=utf-8"},{"id":"8488d9d4-a4d2-5943-b6c2-ca4398674fde","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8488d9d4-a4d2-5943-b6c2-ca4398674fde/attachment.md","path":"references/docs/router/how-to/README.md","size":4557,"sha256":"0b3245cd18ff2bdbea54291ef8baad2f97a1f5c2307ff9a43ea161f9e58059ad","contentType":"text/markdown; charset=utf-8"},{"id":"fbbd00f2-31f9-5c31-a07e-67b3d715fa4c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fbbd00f2-31f9-5c31-a07e-67b3d715fa4c/attachment.md","path":"references/docs/router/how-to/arrays-objects-dates-search-params.md","size":18349,"sha256":"752048632e21aa1cf9bb4f4ec2c5c9867ad834cf1cee7ece47c1019d70114c0a","contentType":"text/markdown; charset=utf-8"},{"id":"d645140c-57ed-529c-ba2a-c7ccf09f8690","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d645140c-57ed-529c-ba2a-c7ccf09f8690/attachment.md","path":"references/docs/router/how-to/debug-router-issues.md","size":15445,"sha256":"67f12d37187180d75130751b0064392beef3242559edca2fb3a059b4e10a6154","contentType":"text/markdown; charset=utf-8"},{"id":"800ab925-ba51-58ac-bfc4-b19b6cf5df75","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/800ab925-ba51-58ac-bfc4-b19b6cf5df75/attachment.md","path":"references/docs/router/how-to/deploy-to-production.md","size":7930,"sha256":"740f268646a94ab896e06880ffbb17c0bd8c0b285478637de034c7080307e264","contentType":"text/markdown; charset=utf-8"},{"id":"fd9a7b7a-67be-5a9a-b2a6-88771358ca4b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fd9a7b7a-67be-5a9a-b2a6-88771358ca4b/attachment.md","path":"references/docs/router/how-to/drafts/README.md","size":2375,"sha256":"161574f035bc734c98d607dec05613d04bdce39296adc586bf22dea1a25e138f","contentType":"text/markdown; charset=utf-8"},{"id":"23214baf-6567-5b96-9e05-a559287072a8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/23214baf-6567-5b96-9e05-a559287072a8/attachment.md","path":"references/docs/router/how-to/drafts/build-search-filtering-systems.draft.md","size":5110,"sha256":"581671e8c7815f6efcc73da41b462d3444fec3c19903c9779aa11669b17462db","contentType":"text/markdown; charset=utf-8"},{"id":"62af2708-3d7d-51da-b1d2-1454c4d9ed24","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/62af2708-3d7d-51da-b1d2-1454c4d9ed24/attachment.md","path":"references/docs/router/how-to/drafts/optimize-search-param-performance.draft.md","size":2496,"sha256":"8c1c3ae0a386794cd0ddbd7c0d3ea7a9e31d0dd8f20b13813d0efa11c5f8961e","contentType":"text/markdown; charset=utf-8"},{"id":"cc3f2ce1-7488-5cad-995b-58c0e1b30695","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cc3f2ce1-7488-5cad-995b-58c0e1b30695/attachment.md","path":"references/docs/router/how-to/drafts/search-params-in-forms.draft.md","size":4131,"sha256":"f9ecfc7504e3db95f4aaf5e96bc6b50985a1a6554c49411547344ed7a9897292","contentType":"text/markdown; charset=utf-8"},{"id":"a91b55e8-f90d-52d6-b243-699eb471674a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a91b55e8-f90d-52d6-b243-699eb471674a/attachment.md","path":"references/docs/router/how-to/install.md","size":725,"sha256":"62ae43c31b8f7df7954af14502fd2c8c90d8ceb7a101583c3c232f8ac8622d72","contentType":"text/markdown; charset=utf-8"},{"id":"dd2e242c-abaf-50c3-b173-8f43e8bd22be","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dd2e242c-abaf-50c3-b173-8f43e8bd22be/attachment.md","path":"references/docs/router/how-to/integrate-chakra-ui.md","size":19732,"sha256":"058df15fb4afa5c62d8d898fdc50173727cadce14350026685bcf0c97faaad72","contentType":"text/markdown; charset=utf-8"},{"id":"689f34a4-9801-584b-a0ec-d9f14f54d741","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/689f34a4-9801-584b-a0ec-d9f14f54d741/attachment.md","path":"references/docs/router/how-to/integrate-framer-motion.md","size":18681,"sha256":"a99ef52f3ac8ffee475b960ab0e5af248a3ed2e02b360539b169e97c145770ac","contentType":"text/markdown; charset=utf-8"},{"id":"af3847d9-0c06-5e98-bdbd-74b08c16dd7c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/af3847d9-0c06-5e98-bdbd-74b08c16dd7c/attachment.md","path":"references/docs/router/how-to/integrate-material-ui.md","size":17264,"sha256":"0086197ae02d3fb01788603d20f809d7ae969a5acc852addb3e89dc2c261ebf1","contentType":"text/markdown; charset=utf-8"},{"id":"a0fb3bc2-f4dc-5f84-8992-2938d4b67596","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a0fb3bc2-f4dc-5f84-8992-2938d4b67596/attachment.md","path":"references/docs/router/how-to/integrate-shadcn-ui.md","size":11955,"sha256":"0c34295070d3afc5f14b69ca27ee3e00b5a3f30bc35c34b3ddc42f3cfce52fec","contentType":"text/markdown; charset=utf-8"},{"id":"7f771773-57f1-5cb3-bb92-6c50cfec07ec","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7f771773-57f1-5cb3-bb92-6c50cfec07ec/attachment.md","path":"references/docs/router/how-to/navigate-with-search-params.md","size":7983,"sha256":"dbbe6e9dedeaea30059fdfe150b64a4332de99ab9f47bbea1111aaaa6142225d","contentType":"text/markdown; charset=utf-8"},{"id":"d8299b23-b750-56a3-acd4-a32cde4b276d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d8299b23-b750-56a3-acd4-a32cde4b276d/attachment.md","path":"references/docs/router/how-to/setup-auth-providers.md","size":14449,"sha256":"1084df7335171e6fb90bbd898c8564c47403d9b2b7bee0499d3bdcf5495f03e9","contentType":"text/markdown; charset=utf-8"},{"id":"70da2e55-7bf3-5f6a-a2b8-6adf3cb4dd09","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/70da2e55-7bf3-5f6a-a2b8-6adf3cb4dd09/attachment.md","path":"references/docs/router/how-to/setup-authentication.md","size":12449,"sha256":"9ed0593fbff2e0ab84472a7af7f8fd51e2f72ca21713ab88a8beeaf2ba5da7c4","contentType":"text/markdown; charset=utf-8"},{"id":"fe6cb53c-2aba-56b9-98b8-92081b2a88f5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fe6cb53c-2aba-56b9-98b8-92081b2a88f5/attachment.md","path":"references/docs/router/how-to/setup-basic-search-params.md","size":11975,"sha256":"fea1f60ed38069db5b1305e93c74dca2620901481e088d6af5ed01e70b559b13","contentType":"text/markdown; charset=utf-8"},{"id":"8d0fa28b-8859-5396-af14-c255d97e6542","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8d0fa28b-8859-5396-af14-c255d97e6542/attachment.md","path":"references/docs/router/how-to/setup-rbac.md","size":19513,"sha256":"5d1a6de1863d22ba7353c6a6c265740f8b87c4f4500b9afb1752f8c9ee0c59e5","contentType":"text/markdown; charset=utf-8"},{"id":"7272e1e5-e3f4-59ff-a530-c8a6549b2eb0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7272e1e5-e3f4-59ff-a530-c8a6549b2eb0/attachment.md","path":"references/docs/router/how-to/setup-ssr.md","size":13029,"sha256":"dc1f74da54043541cf552f93a455c5d219ce1231a7cae8801bf76ea6cae6da5e","contentType":"text/markdown; charset=utf-8"},{"id":"a590a1ed-cc82-5ba6-9954-235ad3be64b5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a590a1ed-cc82-5ba6-9954-235ad3be64b5/attachment.md","path":"references/docs/router/how-to/setup-testing.md","size":23745,"sha256":"a0597ee018147384bbe1d19c307204433520cdc72ec039666ddd8ca918b55832","contentType":"text/markdown; charset=utf-8"},{"id":"b5c55d3b-bbc3-5c10-a374-9d0d0f3c0158","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b5c55d3b-bbc3-5c10-a374-9d0d0f3c0158/attachment.md","path":"references/docs/router/how-to/share-search-params-across-routes.md","size":6523,"sha256":"4f6841af67c6037892cd17eedbff35901a02a88965fd258a7521d80371a1fb4d","contentType":"text/markdown; charset=utf-8"},{"id":"9b8a3e53-271e-5dbd-a3fd-a53568822065","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9b8a3e53-271e-5dbd-a3fd-a53568822065/attachment.md","path":"references/docs/router/how-to/test-file-based-routing.md","size":25558,"sha256":"157e418659aca5eb23b85681a538cda8bbf2a370aad4ed17ab255b0468bb831a","contentType":"text/markdown; charset=utf-8"},{"id":"d3c854f1-d2be-5ebe-b884-ca2b873226f8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d3c854f1-d2be-5ebe-b884-ca2b873226f8/attachment.md","path":"references/docs/router/how-to/use-environment-variables.md","size":18468,"sha256":"0f41baa97a6086f7b3a4a7d4b064aff105073a024ea2399bc74c1f839c3c832a","contentType":"text/markdown; charset=utf-8"},{"id":"e0a373a7-ac12-5cd1-aee3-d423993ac751","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e0a373a7-ac12-5cd1-aee3-d423993ac751/attachment.md","path":"references/docs/router/how-to/validate-search-params.md","size":16558,"sha256":"ddec983e0b26bc3c171ca4e4da06f5b06cee588d5a90d2498974d26b415418eb","contentType":"text/markdown; charset=utf-8"},{"id":"8b6c3441-bb2d-5bf7-bedb-bb4849339556","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8b6c3441-bb2d-5bf7-bedb-bb4849339556/attachment.md","path":"references/docs/router/installation/manual.md","size":8840,"sha256":"211bfb6388754843cbb89da61caad187a04f1c3e9e4b3090347d48881f93b5d8","contentType":"text/markdown; charset=utf-8"},{"id":"e2ba9cef-fd2e-50dc-bd61-3be9614fdf32","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e2ba9cef-fd2e-50dc-bd61-3be9614fdf32/attachment.md","path":"references/docs/router/installation/with-esbuild.md","size":3502,"sha256":"e3bd2328d1b5855c4ffd6de7498a20790cc554404eb58fe961f53bfdec84afef","contentType":"text/markdown; charset=utf-8"},{"id":"267b65c7-c7f2-5a77-9d52-09a7e95ed389","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/267b65c7-c7f2-5a77-9d52-09a7e95ed389/attachment.md","path":"references/docs/router/installation/with-router-cli.md","size":4004,"sha256":"04746d4d8c78deb0732c0827afd2877fb09cf218ed8e6ad895c62d674c5bb551","contentType":"text/markdown; charset=utf-8"},{"id":"b06f482b-0ef8-5bf4-9944-71f0f70c84ca","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b06f482b-0ef8-5bf4-9944-71f0f70c84ca/attachment.md","path":"references/docs/router/installation/with-rspack.md","size":3480,"sha256":"40d500d966a6db31016c4e1fd9f55054d958d4b0608b8282f85ce1615be3bd3c","contentType":"text/markdown; charset=utf-8"},{"id":"7833d931-baac-517d-94aa-40d829b080ed","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7833d931-baac-517d-94aa-40d829b080ed/attachment.md","path":"references/docs/router/installation/with-vite.md","size":3319,"sha256":"c752b8e6083cb4409401220bc41c81f94cfaf4bc160d3b4a80115b279cfe6ebf","contentType":"text/markdown; charset=utf-8"},{"id":"8b77c11f-455d-505e-a293-678b606e12e6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8b77c11f-455d-505e-a293-678b606e12e6/attachment.md","path":"references/docs/router/installation/with-webpack.md","size":3168,"sha256":"2f944d3d4a3badfc88f81d4d6c7c24afda4272806992c76d46e94ae5be2e0cb9","contentType":"text/markdown; charset=utf-8"},{"id":"a44d7674-ec1a-520c-929e-ee0a28cdfb36","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a44d7674-ec1a-520c-929e-ee0a28cdfb36/attachment.md","path":"references/docs/router/integrations/query.md","size":5329,"sha256":"4753f62d0d2cdb4440a9d6e464cbbf6b864db334c395e71d93ba90c937ac18c3","contentType":"text/markdown; charset=utf-8"},{"id":"dbd74ab1-1f6f-5a25-b131-b7566ae308ee","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dbd74ab1-1f6f-5a25-b131-b7566ae308ee/attachment.md","path":"references/docs/router/latest.md","size":812,"sha256":"a864c8639b25b38251ac5abeea7aed94e51cbce084e1e970329bc4ba77e519ca","contentType":"text/markdown; charset=utf-8"},{"id":"15bf911c-3c8b-56c6-91e2-6345a0031b3e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/15bf911c-3c8b-56c6-91e2-6345a0031b3e/attachment.md","path":"references/docs/router/llm-support.md","size":1039,"sha256":"b814c1afdc315af101c0b6f65c45eca124391a7f491b368947ffba8d029bc816","contentType":"text/markdown; charset=utf-8"},{"id":"ee8ebb24-193c-5c88-804c-67707459f6bb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ee8ebb24-193c-5c88-804c-67707459f6bb/attachment.md","path":"references/docs/router/overview.md","size":8485,"sha256":"22606357eda0161d107b7a29f6b50c152b7ac3266754ae229a9bd67c7ba27121","contentType":"text/markdown; charset=utf-8"},{"id":"ef73bf52-0096-5b3f-bee8-68ca90e4a766","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ef73bf52-0096-5b3f-bee8-68ca90e4a766/attachment.md","path":"references/docs/router/quick-start.md","size":2983,"sha256":"73aeacb0aad009b01b7a7ee732ae5717c7be7c131af167d0941a2d22692cfc49","contentType":"text/markdown; charset=utf-8"},{"id":"a1b479a8-a5b0-59cd-9478-bd1473bafebc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a1b479a8-a5b0-59cd-9478-bd1473bafebc/attachment.md","path":"references/docs/router/routing/code-based-routing.md","size":14664,"sha256":"9f3b3b1f89dd510a5e24e5e949aac6f3be0f6d77cd33602758d968ed6d61426a","contentType":"text/markdown; charset=utf-8"},{"id":"b34f34f3-9a69-5791-a332-8e0aeb81b2b2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b34f34f3-9a69-5791-a332-8e0aeb81b2b2/attachment.md","path":"references/docs/router/routing/file-based-routing.md","size":10096,"sha256":"9d7372077bddaf51d633dbd22405aaaa01fb972de6ef39fa635a66bbb7b3d70c","contentType":"text/markdown; charset=utf-8"},{"id":"4b494532-5c6f-500d-9fae-c4c896ae5478","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4b494532-5c6f-500d-9fae-c4c896ae5478/attachment.md","path":"references/docs/router/routing/file-naming-conventions.md","size":6969,"sha256":"63c075dab4128d18713e025f53cbc4cefc28378f6fcfb3b9756b7401b7d6df74","contentType":"text/markdown; charset=utf-8"},{"id":"b70664a7-f8dd-5303-ae96-790e3463338a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b70664a7-f8dd-5303-ae96-790e3463338a/attachment.md","path":"references/docs/router/routing/route-matching.md","size":1496,"sha256":"c77c3398f8bf11c86e98f64ff48f7c290cd9519a104486d48350831e7075888b","contentType":"text/markdown; charset=utf-8"},{"id":"77e37d1e-6cb7-5c77-8577-8127b48feceb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/77e37d1e-6cb7-5c77-8577-8127b48feceb/attachment.md","path":"references/docs/router/routing/route-trees.md","size":2279,"sha256":"a2ace762274d8917274cecfe74832834b4ebaef78b3ffbf58303b20c4c415c1a","contentType":"text/markdown; charset=utf-8"},{"id":"d7aa213d-52fc-5c67-8a05-3a23f03e75c0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d7aa213d-52fc-5c67-8a05-3a23f03e75c0/attachment.md","path":"references/docs/router/routing/routing-concepts.md","size":18569,"sha256":"2506f7e12b455ade79452716e1d055710a3c14748b079ddae4d17cdd36220f40","contentType":"text/markdown; charset=utf-8"},{"id":"dd2312ea-93c8-5095-af9e-3a23810bfa6b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dd2312ea-93c8-5095-af9e-3a23810bfa6b/attachment.md","path":"references/docs/router/routing/virtual-file-routes.md","size":13760,"sha256":"757dabf02bb5b12b67f5baf328745639295574b589d432aea39dd4c3c20d1599","contentType":"text/markdown; charset=utf-8"},{"id":"a5fd3efe-57e3-5c43-8ca5-328788112b98","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a5fd3efe-57e3-5c43-8ca5-328788112b98/attachment.md","path":"references/issues/_INDEX.md","size":3499,"sha256":"d61471b1869fe98501144ad74226022fcfd96531d5bd71e5a59a023923682f19","contentType":"text/markdown; charset=utf-8"},{"id":"d76c505d-fde2-56d8-9f3e-8b0b7ad317f9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d76c505d-fde2-56d8-9f3e-8b0b7ad317f9/attachment.md","path":"references/issues/issue-1120.md","size":1115,"sha256":"9743a89e6518bbe2b953728c773a61e6bbc66ae49a0ddb663849f4c8ed2dfcf2","contentType":"text/markdown; charset=utf-8"},{"id":"f62b28cf-9018-5049-8275-1a137a72b3e1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f62b28cf-9018-5049-8275-1a137a72b3e1/attachment.md","path":"references/issues/issue-1992.md","size":2308,"sha256":"6b763631683593003894e2757915b457a748272472c0379f3ce5f6b4e574453f","contentType":"text/markdown; charset=utf-8"},{"id":"aa5ad32f-2fd4-584a-acda-db33d0c2b282","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/aa5ad32f-2fd4-584a-acda-db33d0c2b282/attachment.md","path":"references/issues/issue-2072.md","size":1501,"sha256":"6ca34f6cd0153e39cacd937005bd21f000cc4d662cc412a4a501f3b1c007c396","contentType":"text/markdown; charset=utf-8"},{"id":"a587ff7b-88eb-5f09-8062-96c5faa196a7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a587ff7b-88eb-5f09-8062-96c5faa196a7/attachment.md","path":"references/issues/issue-2783.md","size":1912,"sha256":"2b25036665570093c6d7cf9dd407bf48b5bb1b3ec110a0b0e70cb1db85874fc6","contentType":"text/markdown; charset=utf-8"},{"id":"4bd6a502-ec37-5dcb-bbbb-868a5ffaed4d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4bd6a502-ec37-5dcb-bbbb-868a5ffaed4d/attachment.md","path":"references/issues/issue-2845.md","size":1737,"sha256":"ad76fd924f2b21d206e9a798b83ba75980de1cc9a98cefd621041853fdb15f49","contentType":"text/markdown; charset=utf-8"},{"id":"e7d967d1-480a-5b26-8b9e-b2f0d209c622","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e7d967d1-480a-5b26-8b9e-b2f0d209c622/attachment.md","path":"references/issues/issue-3023.md","size":3043,"sha256":"d7e55c6d9f154243f646db044c91700bb6dfa9464718af225461fbaf29757112","contentType":"text/markdown; charset=utf-8"},{"id":"f3bca29a-a0bd-5697-98cb-8f79aa72d899","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f3bca29a-a0bd-5697-98cb-8f79aa72d899/attachment.md","path":"references/issues/issue-3110.md","size":2200,"sha256":"90d6cd78024610ab04e1ab7a8bafbc9d573ae1fb8bcd611f312c3441bc6773fc","contentType":"text/markdown; charset=utf-8"},{"id":"786b7bb0-7b9c-5b8a-b08c-fdbb3c8ff5f8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/786b7bb0-7b9c-5b8a-b08c-fdbb3c8ff5f8/attachment.md","path":"references/issues/issue-3462.md","size":3497,"sha256":"32517eda69f5f5c73d239d0fda864e78e9d378309fb1c46a510aa7a86080e38a","contentType":"text/markdown; charset=utf-8"},{"id":"d4b5b2d6-95de-52cc-a2e7-799f40bfc343","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d4b5b2d6-95de-52cc-a2e7-799f40bfc343/attachment.md","path":"references/issues/issue-3468.md","size":3419,"sha256":"be5e3e497b429c5c25bbd198389d0ebdc9861147c795c0b46f258567e97aec80","contentType":"text/markdown; charset=utf-8"},{"id":"b29e967e-9a16-5829-b12d-3b744b1e9d7f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b29e967e-9a16-5829-b12d-3b744b1e9d7f/attachment.md","path":"references/issues/issue-3804.md","size":507,"sha256":"3366c4d35dca2830125b414d703d198808cfb52755cce1c2f5e7ccd01a2ba201","contentType":"text/markdown; charset=utf-8"},{"id":"93f4d6db-f1a9-5df1-9093-03658f662f14","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/93f4d6db-f1a9-5df1-9093-03658f662f14/attachment.md","path":"references/issues/issue-3997.md","size":3060,"sha256":"8d838b2f198f2b72cefb501a53717db9a6fd08d2d4c03ea0f780e07c487dcf88","contentType":"text/markdown; charset=utf-8"},{"id":"36a30410-b9a9-5bc8-a377-cff2f0e0ff29","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/36a30410-b9a9-5bc8-a377-cff2f0e0ff29/attachment.md","path":"references/issues/issue-4010.md","size":2258,"sha256":"b66cb9029ed666b9619d8c2412a096154c3ea50cac174a067387bf439dba16f3","contentType":"text/markdown; charset=utf-8"},{"id":"fb92c67d-68d1-595c-80ef-3f9935a8fece","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fb92c67d-68d1-595c-80ef-3f9935a8fece/attachment.md","path":"references/issues/issue-4473.md","size":2217,"sha256":"b6e45eacaf218913ec3c0a35e475fa30833274ae2aadbf473b3639203125e354","contentType":"text/markdown; charset=utf-8"},{"id":"19e7950f-6c98-59c0-bc73-6a170cc9fa19","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/19e7950f-6c98-59c0-bc73-6a170cc9fa19/attachment.md","path":"references/issues/issue-4476.md","size":2074,"sha256":"93e336d6b6b8eedf577fb2f015dfb9ca40e73fbbbd1704a84c9c69e468889806","contentType":"text/markdown; charset=utf-8"},{"id":"e33131ab-4127-5876-83b7-db82e069329a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e33131ab-4127-5876-83b7-db82e069329a/attachment.md","path":"references/issues/issue-4499.md","size":1167,"sha256":"30a5705a6a71a2bd1f39a7e58774bac67454b474c11d267b06329b985d850378","contentType":"text/markdown; charset=utf-8"},{"id":"89bd546d-131c-5d12-a192-d49ceb72fa76","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/89bd546d-131c-5d12-a192-d49ceb72fa76/attachment.md","path":"references/issues/issue-4729.md","size":2276,"sha256":"b1384e97c3e5f6a1df8188e7117ebf1870a7305bdef6d30b6c09a02c3efc0f9f","contentType":"text/markdown; charset=utf-8"},{"id":"7f00fde8-8773-5165-b31e-9a8830e0a891","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7f00fde8-8773-5165-b31e-9a8830e0a891/attachment.md","path":"references/issues/issue-4901.md","size":2855,"sha256":"c271b3f04eba4bc3552c95ec8a475e6a5dc4d18f0002bcc211190bcec3871719","contentType":"text/markdown; charset=utf-8"},{"id":"63b9b1ee-e4b2-50c3-a2fd-9ff8a5edf8a5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/63b9b1ee-e4b2-50c3-a2fd-9ff8a5edf8a5/attachment.md","path":"references/issues/issue-4973.md","size":1883,"sha256":"7f2da5336df95b0c118071fcbb8b72a0acfccb6bdc41b6ccbe6ee55b5b5216a9","contentType":"text/markdown; charset=utf-8"},{"id":"0b19fd60-c55c-5757-9431-6b279d2981b8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0b19fd60-c55c-5757-9431-6b279d2981b8/attachment.md","path":"references/issues/issue-4988.md","size":1890,"sha256":"a2151220103739f6b70c4cbc46e24ae0d518c6ca5779f6567b39b31e311cb916","contentType":"text/markdown; charset=utf-8"},{"id":"5ab69f1e-bb5a-5b8f-bd19-05a54da71396","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5ab69f1e-bb5a-5b8f-bd19-05a54da71396/attachment.md","path":"references/issues/issue-5217.md","size":3458,"sha256":"578891f27f2c1c6026cb1e4e776b5869bd33a62d439574595ccce9ed74deb1e6","contentType":"text/markdown; charset=utf-8"},{"id":"3a8ca8ff-6fd6-590f-b4a6-3000f81bc2a3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3a8ca8ff-6fd6-590f-b4a6-3000f81bc2a3/attachment.md","path":"references/issues/issue-5469.md","size":3184,"sha256":"52cc12a11a39054608ada5b9f7f1110131ce568c5986f09bcbdf19954b1125f3","contentType":"text/markdown; charset=utf-8"},{"id":"e1557b21-37eb-53e8-a97b-c499f2a3fee4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e1557b21-37eb-53e8-a97b-c499f2a3fee4/attachment.md","path":"references/issues/issue-5673.md","size":2759,"sha256":"cadb39a2e105b831cf68708840edd1ca69f8a46914f668627a974b5512680ae4","contentType":"text/markdown; charset=utf-8"},{"id":"52d519a4-2316-5821-ba0e-459d82067957","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/52d519a4-2316-5821-ba0e-459d82067957/attachment.md","path":"references/issues/issue-5704.md","size":1582,"sha256":"af9a8b2611997604db7efa0541209bd50783ded0538fe6ca80ced908ba5c111f","contentType":"text/markdown; charset=utf-8"},{"id":"c1cb0fc4-824b-5c3c-b307-9f5d12d041a5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c1cb0fc4-824b-5c3c-b307-9f5d12d041a5/attachment.md","path":"references/issues/issue-5738.md","size":1511,"sha256":"9106dc32cf3e450ff553c8a5df1152277a7c10d035a005aa2e0ef53edc8a432a","contentType":"text/markdown; charset=utf-8"},{"id":"3d3da95b-b009-58fc-a5e3-551ecde00703","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3d3da95b-b009-58fc-a5e3-551ecde00703/attachment.md","path":"references/issues/issue-5774.md","size":2181,"sha256":"04cfc92099ae5e3b0ada885dc3883a5c80769b6ceb0eff4c220dfac404ab927f","contentType":"text/markdown; charset=utf-8"},{"id":"4b288121-a6d2-5152-a08c-3cdf030b45f3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4b288121-a6d2-5152-a08c-3cdf030b45f3/attachment.md","path":"references/issues/issue-5795.md","size":1550,"sha256":"2165bcfb3ffbd20bddcdc312ba2d7d0b483d960b44f71ef4a711270dafecf2ae","contentType":"text/markdown; charset=utf-8"},{"id":"29801c3d-2fec-5bac-8ba3-0001accf8fa7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/29801c3d-2fec-5bac-8ba3-0001accf8fa7/attachment.md","path":"references/issues/issue-5967.md","size":3548,"sha256":"ae4239f111fd1bc761143c421690680379b1f123d94002728ebffe8e15fbc21e","contentType":"text/markdown; charset=utf-8"},{"id":"1bca30ea-4150-5a8b-86ec-4790b1633d80","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1bca30ea-4150-5a8b-86ec-4790b1633d80/attachment.md","path":"references/issues/issue-597.md","size":1749,"sha256":"f753be41ce8946d6ccf36efbb7513cfb19e10403e97b647fcb5a8cce09407f64","contentType":"text/markdown; charset=utf-8"},{"id":"f211b3e0-fcbb-55e3-8487-1a43cd71601b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f211b3e0-fcbb-55e3-8487-1a43cd71601b/attachment.md","path":"references/issues/issue-6483.md","size":1284,"sha256":"3e895ba86cb870add2edfe344c47657f154fe0788602698098ff603dedaf297f","contentType":"text/markdown; charset=utf-8"},{"id":"f6912ee5-4b0e-565b-9af7-95044de352d5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f6912ee5-4b0e-565b-9af7-95044de352d5/attachment.md","path":"references/issues/issue-918.md","size":3753,"sha256":"4180de7ac18c8d083ac39846cda9fcdc8cda19b2ff35be261587e9cd485d88c7","contentType":"text/markdown; charset=utf-8"},{"id":"02284dd7-0905-5c45-9dd1-53bdd826c1ac","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/02284dd7-0905-5c45-9dd1-53bdd826c1ac/attachment.md","path":"references/releases/_INDEX.md","size":1113,"sha256":"e40d310ddec0f578f0e66510b9ac65ab5d33bc4ed801d9a167af0001cbc73b9d","contentType":"text/markdown; charset=utf-8"},{"id":"03a01c92-f89c-5402-a4e8-5f350b85783b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/03a01c92-f89c-5402-a4e8-5f350b85783b/attachment.md","path":"references/releases/v1.158.1.md","size":1861,"sha256":"e1a1d475a5961c4151351a7323928556a463b4f5b52b08808a163c52c419e514","contentType":"text/markdown; charset=utf-8"},{"id":"a797652b-f988-5317-8191-665e5a07115a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a797652b-f988-5317-8191-665e5a07115a/attachment.md","path":"references/releases/v1.158.2.md","size":423,"sha256":"ab3db6f3425d7d179ae7ff2778f795dfb48b4b3c8703620fd1bed8e773baaa0d","contentType":"text/markdown; charset=utf-8"},{"id":"ea14b02d-af68-5483-b654-ca2589bf9aef","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ea14b02d-af68-5483-b654-ca2589bf9aef/attachment.md","path":"references/releases/v1.158.3.md","size":803,"sha256":"ed88e07c3c401946b2611023ef884dbd8d01c94439353eba5f06a0fd842534c7","contentType":"text/markdown; charset=utf-8"},{"id":"288c2ddb-da4f-59ab-8f66-cafe4181a48a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/288c2ddb-da4f-59ab-8f66-cafe4181a48a/attachment.md","path":"references/releases/v1.158.4.md","size":1631,"sha256":"6d88edf85473a185ddda234fa55fdca0c2f12129d83810aba664ecf45543cf62","contentType":"text/markdown; charset=utf-8"},{"id":"617973cf-9363-520f-9108-4a4c34a28d86","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/617973cf-9363-520f-9108-4a4c34a28d86/attachment.md","path":"references/releases/v1.159.0.md","size":541,"sha256":"2bd90240b2b431eeb2591808038c3a83f7e9037e4ae0d7e2839f01f55b18df5d","contentType":"text/markdown; charset=utf-8"},{"id":"8f0f7075-9c45-5e66-b473-8d889b0a435f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8f0f7075-9c45-5e66-b473-8d889b0a435f/attachment.md","path":"references/releases/v1.159.1.md","size":519,"sha256":"e87eb20a5521b8581efde70fcb248eb60a48ffade9288a6e6ee64ab87cfd3381","contentType":"text/markdown; charset=utf-8"},{"id":"5577decb-8521-592a-ab85-3c3796b13816","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5577decb-8521-592a-ab85-3c3796b13816/attachment.md","path":"references/releases/v1.159.10.md","size":1334,"sha256":"92b4f242a0420725f704809d2140d388421c62bae4117b6739c4ea964e71ac48","contentType":"text/markdown; charset=utf-8"},{"id":"c03f58eb-ae99-50e0-bc4d-56f34ad4159d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c03f58eb-ae99-50e0-bc4d-56f34ad4159d/attachment.md","path":"references/releases/v1.159.11.md","size":493,"sha256":"b8af39e8fd43f0ed002fb528f68ff289e96d6fff5a22afdf8907d70818bd0f03","contentType":"text/markdown; charset=utf-8"},{"id":"7778ee4d-8d3f-5ce2-a580-4d414ce6af25","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7778ee4d-8d3f-5ce2-a580-4d414ce6af25/attachment.md","path":"references/releases/v1.159.12.md","size":494,"sha256":"644634fc1a9799fec3d087cb9c57e34341b4840d479a364bc4f6b7d2d86f0806","contentType":"text/markdown; charset=utf-8"},{"id":"a31fee31-4d4d-551d-beed-918cdfd54b92","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a31fee31-4d4d-551d-beed-918cdfd54b92/attachment.md","path":"references/releases/v1.159.13.md","size":605,"sha256":"b987f9e16020ee3b64cb82fd094a85e234ae3a5801357a5e36fb503616852154","contentType":"text/markdown; charset=utf-8"},{"id":"d72055fc-3a2f-5561-98f4-71caf3b44251","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d72055fc-3a2f-5561-98f4-71caf3b44251/attachment.md","path":"references/releases/v1.159.14.md","size":1113,"sha256":"873b703b02a8ae1c12726327a8fabff01fdba118ada7693990057798bf2b2bf9","contentType":"text/markdown; charset=utf-8"},{"id":"9b14a5cb-e04c-5d65-b5a0-94529105512f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9b14a5cb-e04c-5d65-b5a0-94529105512f/attachment.md","path":"references/releases/v1.159.2.md","size":790,"sha256":"c8fc753e6a5ebd36c8bcea9e3eb4779e3d672481fd5013babc39160bd28b65ca","contentType":"text/markdown; charset=utf-8"},{"id":"59ef6d4d-a5c8-538f-bcd8-3785c754e827","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/59ef6d4d-a5c8-538f-bcd8-3785c754e827/attachment.md","path":"references/releases/v1.159.3.md","size":1185,"sha256":"6c73eceac64778700ed36d376bdbcfd2fc00163eea372984edae695c92664967","contentType":"text/markdown; charset=utf-8"},{"id":"885bbd5b-9680-5334-be5b-f966fc6f615a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/885bbd5b-9680-5334-be5b-f966fc6f615a/attachment.md","path":"references/releases/v1.159.4.md","size":1524,"sha256":"0891ec3878e103343387d4c464a9a4cf5bf9acab8769418221ee232e547dd211","contentType":"text/markdown; charset=utf-8"},{"id":"516d1cb2-7775-5905-88cb-81438c6c3fa3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/516d1cb2-7775-5905-88cb-81438c6c3fa3/attachment.md","path":"references/releases/v1.159.5.md","size":1211,"sha256":"f8580bf5bee0ad93251e3e6d905950e03c608546f819745d245d00276edf1788","contentType":"text/markdown; charset=utf-8"},{"id":"361d76d6-7e8c-540e-b112-ffe091c2e530","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/361d76d6-7e8c-540e-b112-ffe091c2e530/attachment.md","path":"references/releases/v1.159.6.md","size":1760,"sha256":"9f578d888c529c930140abb3c9cbf701715d719d6a7f65d5c6b511c684befaa7","contentType":"text/markdown; charset=utf-8"},{"id":"b827eb57-7324-5361-8511-5ac8e816501e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b827eb57-7324-5361-8511-5ac8e816501e/attachment.md","path":"references/releases/v1.159.7.md","size":418,"sha256":"b47f5f0c6ff468ac55b5897f5f41c03d9cdff5048ab9e4ea154d44b50dd7cea2","contentType":"text/markdown; charset=utf-8"},{"id":"dd2d07cc-dc36-5894-8e3a-a3a40e0e3735","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dd2d07cc-dc36-5894-8e3a-a3a40e0e3735/attachment.md","path":"references/releases/v1.159.8.md","size":420,"sha256":"a9de1e537e800ed9e5b5971920dc5ada123ede5c7285d6db8bf085ec29874c5f","contentType":"text/markdown; charset=utf-8"},{"id":"794aee1c-1a1e-582d-822a-67550a0feae7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/794aee1c-1a1e-582d-822a-67550a0feae7/attachment.md","path":"references/releases/v1.159.9.md","size":1517,"sha256":"3d331a5d63034fceb9d7471ec819e1001506bcedc98d023065a63d1a4c7a3432","contentType":"text/markdown; charset=utf-8"},{"id":"f3913c46-091c-52c1-9a2d-167d25d086ad","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f3913c46-091c-52c1-9a2d-167d25d086ad/attachment.md","path":"references/releases/v1.160.0.md","size":1552,"sha256":"d99658beefce7df235165e7ec4ebf12b8f1340fa5934c903e44f4a78d6ba288e","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"69d6b63c2023cb89fb21e5bccbff530590c930b26f0a3f5316e4339921f32aef","attachment_count":234,"text_attachments":234,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/tanstack-vue-router-skilld/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"software-engineering","category_label":"Engineering"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"software-engineering","metadata":{"version":"1.166.7","generated_at":"2026-03-11T00:00:00.000Z","references_synced_at":"2026-05-05T00:00:00.000Z"},"import_tag":"clean-skills-v1","description":"Modern and scalable routing for Vue applications. ALWAYS use when writing code importing \"@tanstack/vue-router\". Consult for debugging, best practices, or modifying @tanstack/vue-router, tanstack/vue-router, tanstack vue-router, tanstack vue router, router."}},"renderedAt":1782987707054}

TanStack/router Modern and scalable routing for Vue applications Version: 1.166.7 (Mar 2026) Deps: @tanstack/vue-store@^0.9.1, @vue/runtime-dom@^3.5.25, isbot@^5.1.22, jsesc@^3.0.2, tiny-invariant@^1.3.3, tiny-warning@^1.0.3, @tanstack/[email protected], @tanstack/[email protected] Tags: latest: 1.166.7 (Mar 2026) References: Docs — API reference, guides API Changes This section documents version-specific API changes — prioritize recent major/minor releases. - BREAKING: & — deprecated in v1.x; use in route options or in instead source - DEPRECATED: Router Classes ( , , , ) — all class-based A…