diff --git a/.gitignore b/.gitignore index 5cc3e94113..b0ecaffa4b 100644 --- a/.gitignore +++ b/.gitignore @@ -76,4 +76,6 @@ app.config.ts.timestamp_* # Handling VSCode settings /.vscode/ -!/examples/react/**/.vscode/settings.json \ No newline at end of file +!/examples/react/**/.vscode/settings.json + +**/llms diff --git a/docs/router/eslint/eslint-plugin-router.md b/docs/router/eslint/eslint-plugin-router.md index a9b5cac93e..54036cb304 100644 --- a/docs/router/eslint/eslint-plugin-router.md +++ b/docs/router/eslint/eslint-plugin-router.md @@ -102,4 +102,4 @@ Alternatively, add `@tanstack/eslint-plugin-router` to the plugins section, and The following rules are available in the TanStack Router ESLint Plugin: -- [@tanstack/router/create-route-property-order](./create-route-property-order.md) +- [@tanstack/router/create-route-property-order](../create-route-property-order.md) diff --git a/docs/router/framework/react/api/router.md b/docs/router/framework/react/api/router.md index ff999e0c5d..ad3a58f851 100644 --- a/docs/router/framework/react/api/router.md +++ b/docs/router/framework/react/api/router.md @@ -4,85 +4,85 @@ title: Router API --- - Functions - - [`createFileRoute`](./router/createFileRouteFunction.md) - - [`createLazyFileRoute`](./router/createLazyFileRouteFunction.md) - - [`createRootRoute`](./router/createRootRouteFunction.md) - - [`createRootRouteWithContext`](./router/createRootRouteWithContextFunction.md) - - [`createRoute`](./router/createRouteFunction.md) - - [`createLazyRoute`](./router/createLazyRouteFunction.md) - - [`createRouteMask`](./router/createRouteMaskFunction.md) - - [`createRouter`](./router/createRouterFunction.md) - - [`defer`](./router/deferFunction.md) - - [`getRouteApi`](./router/getRouteApiFunction.md) - - [`isNotFound`](./router/isNotFoundFunction.md) - - [`isRedirect`](./router/isRedirectFunction.md) - - [`lazyRouteComponent`](./router/lazyRouteComponentFunction.md) - - [`linkOptions`](./router/linkOptions.md) - - [`notFound`](./router/notFoundFunction.md) - - [`redirect`](./router/redirectFunction.md) - - [`retainSearchParams`](./router/retainSearchParamsFunction.md) - - [`stripSearchParams`](./router/stripSearchParamsFunction.md) + - [`createFileRoute`](../router/createFileRouteFunction.md) + - [`createLazyFileRoute`](../router/createLazyFileRouteFunction.md) + - [`createRootRoute`](../router/createRootRouteFunction.md) + - [`createRootRouteWithContext`](../router/createRootRouteWithContextFunction.md) + - [`createRoute`](../router/createRouteFunction.md) + - [`createLazyRoute`](../router/createLazyRouteFunction.md) + - [`createRouteMask`](../router/createRouteMaskFunction.md) + - [`createRouter`](../router/createRouterFunction.md) + - [`defer`](../router/deferFunction.md) + - [`getRouteApi`](../router/getRouteApiFunction.md) + - [`isNotFound`](../router/isNotFoundFunction.md) + - [`isRedirect`](../router/isRedirectFunction.md) + - [`lazyRouteComponent`](../router/lazyRouteComponentFunction.md) + - [`linkOptions`](../router/linkOptions.md) + - [`notFound`](../router/notFoundFunction.md) + - [`redirect`](../router/redirectFunction.md) + - [`retainSearchParams`](../router/retainSearchParamsFunction.md) + - [`stripSearchParams`](../router/stripSearchParamsFunction.md) - Components - - [``](./router/awaitComponent.md) - - [``](./router/catchBoundaryComponent.md) - - [``](./router/catchNotFoundComponent.md) - - [``](./router/clientOnlyComponent.md) - - [``](./router/defaultGlobalNotFoundComponent.md) - - [``](./router/errorComponentComponent.md) - - [``](./router/linkComponent.md) - - [``](./router/matchRouteComponent.md) - - [``](./router/navigateComponent.md) - - [``](./router/outletComponent.md) + - [``](../router/awaitComponent.md) + - [``](../router/catchBoundaryComponent.md) + - [``](../router/catchNotFoundComponent.md) + - [``](../router/clientOnlyComponent.md) + - [``](../router/defaultGlobalNotFoundComponent.md) + - [``](../router/errorComponentComponent.md) + - [``](../router/linkComponent.md) + - [``](../router/matchRouteComponent.md) + - [``](../router/navigateComponent.md) + - [``](../router/outletComponent.md) - Hooks - - [`useAwaited`](./router/useAwaitedHook.md) - - [`useBlocker`](./router/useBlockerHook.md) - - [`useCanGoBack`](./router//useCanGoBack.md) - - [`useChildMatches`](./router/useChildMatchesHook.md) - - [`useLinkProps`](./router/useLinkPropsHook.md) - - [`useLoaderData`](./router/useLoaderDataHook.md) - - [`useLoaderDeps`](./router/useLoaderDepsHook.md) - - [`useLocation`](./router/useLocationHook.md) - - [`useMatch`](./router/useMatchHook.md) - - [`useMatchRoute`](./router/useMatchRouteHook.md) - - [`useMatches`](./router/useMatchesHook.md) - - [`useNavigate`](./router/useNavigateHook.md) - - [`useParentMatches`](./router/useParentMatchesHook.md) - - [`useParams`](./router/useParamsHook.md) - - [`useRouteContext`](./router/useRouteContextHook.md) - - [`useRouter`](./router/useRouterHook.md) - - [`useRouterState`](./router/useRouterStateHook.md) - - [`useSearch`](./router/useSearchHook.md) + - [`useAwaited`](../router/useAwaitedHook.md) + - [`useBlocker`](../router/useBlockerHook.md) + - [`useCanGoBack`](../router//useCanGoBack.md) + - [`useChildMatches`](../router/useChildMatchesHook.md) + - [`useLinkProps`](../router/useLinkPropsHook.md) + - [`useLoaderData`](../router/useLoaderDataHook.md) + - [`useLoaderDeps`](../router/useLoaderDepsHook.md) + - [`useLocation`](../router/useLocationHook.md) + - [`useMatch`](../router/useMatchHook.md) + - [`useMatchRoute`](../router/useMatchRouteHook.md) + - [`useMatches`](../router/useMatchesHook.md) + - [`useNavigate`](../router/useNavigateHook.md) + - [`useParentMatches`](../router/useParentMatchesHook.md) + - [`useParams`](../router/useParamsHook.md) + - [`useRouteContext`](../router/useRouteContextHook.md) + - [`useRouter`](../router/useRouterHook.md) + - [`useRouterState`](../router/useRouterStateHook.md) + - [`useSearch`](../router/useSearchHook.md) - Types - - [`ActiveLinkOptions Type`](./router/ActiveLinkOptionsType.md) - - [`AsyncRouteComponent Type`](./router/AsyncRouteComponentType.md) - - [`HistoryState Interface`](./router/historyStateInterface.md) - - [`LinkOptions Type`](./router/LinkOptionsType.md) - - [`LinkProps Type`](./router/LinkPropsType.md) - - [`MatchRouteOptions Type`](./router/MatchRouteOptionsType.md) - - [`NavigateOptions Type`](./router/NavigateOptionsType.md) - - [`NotFoundError Type`](./router/NotFoundErrorType.md) - - [`ParsedHistoryState Type`](./router/ParsedHistoryStateType.md) - - [`ParsedLocation Type`](./router/ParsedLocationType.md) - - [`Redirect Type`](./router/RedirectType.md) - - [`Register Type`](./router/RegisterType.md) - - [`Route Type`](./router/RouteType.md) - - [`RouteApi Type`](./router/RouteApiType.md) - - [`RouteMask Type`](./router/RouteMaskType.md) - - [`RouteMatch Type`](./router/RouteMatchType.md) - - [`RouteOptions Type`](./router/RouteOptionsType.md) - - [`Router Type`](./router/RouterType.md) - - [`RouterEvents Type`](./router/RouterEventsType.md) - - [`RouterOptions Type`](./router/RouterOptionsType.md) - - [`RouterState Type`](./router/RouterStateType.md) - - [`ToMaskOptions Type`](./router/ToMaskOptionsType.md) - - [`ToOptions Type`](./router/ToOptionsType.md) - - [`UseMatchRouteOptions Type`](./router/UseMatchRouteOptionsType.md) - - [`ViewTransitionOptions Type`](./router/ViewTransitionOptionsType.md) + - [`ActiveLinkOptions Type`](../router/ActiveLinkOptionsType.md) + - [`AsyncRouteComponent Type`](../router/AsyncRouteComponentType.md) + - [`HistoryState Interface`](../router/historyStateInterface.md) + - [`LinkOptions Type`](../router/LinkOptionsType.md) + - [`LinkProps Type`](../router/LinkPropsType.md) + - [`MatchRouteOptions Type`](../router/MatchRouteOptionsType.md) + - [`NavigateOptions Type`](../router/NavigateOptionsType.md) + - [`NotFoundError Type`](../router/NotFoundErrorType.md) + - [`ParsedHistoryState Type`](../router/ParsedHistoryStateType.md) + - [`ParsedLocation Type`](../router/ParsedLocationType.md) + - [`Redirect Type`](../router/RedirectType.md) + - [`Register Type`](../router/RegisterType.md) + - [`Route Type`](../router/RouteType.md) + - [`RouteApi Type`](../router/RouteApiType.md) + - [`RouteMask Type`](../router/RouteMaskType.md) + - [`RouteMatch Type`](../router/RouteMatchType.md) + - [`RouteOptions Type`](../router/RouteOptionsType.md) + - [`Router Type`](../router/RouterType.md) + - [`RouterEvents Type`](../router/RouterEventsType.md) + - [`RouterOptions Type`](../router/RouterOptionsType.md) + - [`RouterState Type`](../router/RouterStateType.md) + - [`ToMaskOptions Type`](../router/ToMaskOptionsType.md) + - [`ToOptions Type`](../router/ToOptionsType.md) + - [`UseMatchRouteOptions Type`](../router/UseMatchRouteOptionsType.md) + - [`ViewTransitionOptions Type`](../router/ViewTransitionOptionsType.md) - ⚠️ Deprecated - - [`FileRoute Class`](./router/FileRouteClass.md) - - [`Route Class`](./router/RouteClass.md) - - [`Router Class`](./router/RouterClass.md) - - [`RouteApi Class`](./router/RouteApiClass.md) - - [`RootRoute Class`](./router/RootRouteClass.md) - - [`NotFoundRoute Class`](./router/NotFoundRouteClass.md) - - [`rootRouteWithContext Function`](./router/rootRouteWithContextFunction.md) + - [`FileRoute Class`](../router/FileRouteClass.md) + - [`Route Class`](../router/RouteClass.md) + - [`Router Class`](../router/RouterClass.md) + - [`RouteApi Class`](../router/RouteApiClass.md) + - [`RootRoute Class`](../router/RootRouteClass.md) + - [`NotFoundRoute Class`](../router/NotFoundRouteClass.md) + - [`rootRouteWithContext Function`](../router/rootRouteWithContextFunction.md) diff --git a/docs/router/framework/react/api/router/ActiveLinkOptionsType.md b/docs/router/framework/react/api/router/ActiveLinkOptionsType.md index 01434bf41a..8f673799e0 100644 --- a/docs/router/framework/react/api/router/ActiveLinkOptionsType.md +++ b/docs/router/framework/react/api/router/ActiveLinkOptionsType.md @@ -3,7 +3,7 @@ id: ActiveLinkOptionsType title: ActiveLinkOptions type --- -The `ActiveLinkOptions` type extends the [`LinkOptions`](./LinkOptionsType.md) type and contains additional options that can be used to describe how a link should be styled when it is active. +The `ActiveLinkOptions` type extends the [`LinkOptions`](../LinkOptionsType.md) type and contains additional options that can be used to describe how a link should be styled when it is active. ```tsx type ActiveLinkOptions = LinkOptions & { diff --git a/docs/router/framework/react/api/router/FileRouteClass.md b/docs/router/framework/react/api/router/FileRouteClass.md index d7745b4580..47f565137f 100644 --- a/docs/router/framework/react/api/router/FileRouteClass.md +++ b/docs/router/framework/react/api/router/FileRouteClass.md @@ -5,7 +5,7 @@ title: FileRoute class > [!CAUTION] > This class has been deprecated and will be removed in the next major version of TanStack Router. -> Please use the [`createFileRoute`](./createFileRouteFunction.md) function instead. +> Please use the [`createFileRoute`](../createFileRouteFunction.md) function instead. The `FileRoute` class is a factory that can be used to create a file-based route instance. This route instance can then be used to automatically generate a route tree with the `tsr generate` and `tsr watch` commands. @@ -34,13 +34,13 @@ The `createRoute` method is a method that can be used to configure the file rout #### .createRoute options - Type: `Omit` -- [`RouteOptions`](./RouteOptionsType.md) +- [`RouteOptions`](../RouteOptionsType.md) - Optional - The same options that are available to the `Route` class, but with the `getParentRoute`, `path`, and `id` options omitted since they are unnecessary for file-based routing. #### .createRoute returns -A [`Route`](./RouteType.md) instance that can be used to configure the route to be inserted into the route-tree. +A [`Route`](../RouteType.md) instance that can be used to configure the route to be inserted into the route-tree. > ⚠️ Note: For `tsr generate` and `tsr watch` to work properly, the file route instance must be exported from the file using the `Route` identifier. diff --git a/docs/router/framework/react/api/router/LinkOptionsType.md b/docs/router/framework/react/api/router/LinkOptionsType.md index 481a5b4a3d..5f81578140 100644 --- a/docs/router/framework/react/api/router/LinkOptionsType.md +++ b/docs/router/framework/react/api/router/LinkOptionsType.md @@ -3,7 +3,7 @@ id: LinkOptionsType title: LinkOptions type --- -The `LinkOptions` type extends the [`NavigateOptions`](./NavigateOptionsType.md) type and contains additional options that can be used by TanStack Router when handling actual anchor element attributes. +The `LinkOptions` type extends the [`NavigateOptions`](../NavigateOptionsType.md) type and contains additional options that can be used by TanStack Router when handling actual anchor element attributes. ```tsx type LinkOptions = NavigateOptions & { @@ -36,7 +36,7 @@ The `LinkOptions` object accepts/contains the following properties: - Type: `false | 'intent' | 'viewport' | 'render'` - Optional - If set, the link's preloading strategy will be set to this value. -- See the [Preloading guide](../../guide/preloading.md) for more information. +- See the [Preloading guide](../../../guide/preloading.md) for more information. ### `preloadDelay` diff --git a/docs/router/framework/react/api/router/LinkPropsType.md b/docs/router/framework/react/api/router/LinkPropsType.md index e8df561a97..4866cb400b 100644 --- a/docs/router/framework/react/api/router/LinkPropsType.md +++ b/docs/router/framework/react/api/router/LinkPropsType.md @@ -3,7 +3,7 @@ id: LinkPropsType title: LinkProps type --- -The `LinkProps` type extends the [`ActiveLinkOptions`](./ActiveLinkOptionsType.md) and `React.AnchorHTMLAttributes` types and contains additional props specific to the `Link` component. +The `LinkProps` type extends the [`ActiveLinkOptions`](../ActiveLinkOptionsType.md) and `React.AnchorHTMLAttributes` types and contains additional props specific to the `Link` component. ```tsx type LinkProps = ActiveLinkOptions & @@ -16,7 +16,7 @@ type LinkProps = ActiveLinkOptions & ## LinkProps properties -- All of the props from [`ActiveLinkOptions`](./ActiveLinkOptionsType.md) +- All of the props from [`ActiveLinkOptions`](../ActiveLinkOptionsType.md) - All of the props from `React.AnchorHTMLAttributes` #### `children` diff --git a/docs/router/framework/react/api/router/NavigateOptionsType.md b/docs/router/framework/react/api/router/NavigateOptionsType.md index c4068d0fc2..4a855774e9 100644 --- a/docs/router/framework/react/api/router/NavigateOptionsType.md +++ b/docs/router/framework/react/api/router/NavigateOptionsType.md @@ -50,7 +50,7 @@ The `NavigateOptions` object accepts the following properties: - Optional - Defaults to `false`. - If `true`, navigation will be called using `document.startViewTransition()`. -- If [`ViewTransitionOptions`](./ViewTransitionOptionsType.md), route navigations will be called using `document.startViewTransition({update, types})` where `types` will be the strings array passed with `ViewTransitionOptions["types"]`. If the browser does not support viewTransition types, the navigation will fall back to normal `document.startTransition()`, same as if `true` was passed. +- If [`ViewTransitionOptions`](../ViewTransitionOptionsType.md), route navigations will be called using `document.startViewTransition({update, types})` where `types` will be the strings array passed with `ViewTransitionOptions["types"]`. If the browser does not support viewTransition types, the navigation will fall back to normal `document.startTransition()`, same as if `true` was passed. - If the browser does not support this api, this option will be ignored. - See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) for more information on how this function works. - See [Google](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#view-transition-types) for more information on viewTransition types @@ -75,4 +75,4 @@ The `NavigateOptions` object accepts the following properties: - Optional - This can be used instead of `to` to navigate to a fully built href, e.g. pointing to an external target. -- [`ToOptions`](./ToOptionsType.md) +- [`ToOptions`](../ToOptionsType.md) diff --git a/docs/router/framework/react/api/router/NotFoundRouteClass.md b/docs/router/framework/react/api/router/NotFoundRouteClass.md index 1efe0e5dae..bfe954ba00 100644 --- a/docs/router/framework/react/api/router/NotFoundRouteClass.md +++ b/docs/router/framework/react/api/router/NotFoundRouteClass.md @@ -6,7 +6,7 @@ title: NotFoundRoute class > [!CAUTION] > This class has been deprecated and will be removed in the next major version of TanStack Router. > Please use the `notFoundComponent` route option that is present during route configuration. -> See the [Not Found Errors guide](../../guide/not-found-errors.md) for more information. +> See the [Not Found Errors guide](../../../guide/not-found-errors.md) for more information. The `NotFoundRoute` class extends the `Route` class and can be used to create a not found route instance. A not found route instance can be passed to the `routerOptions.notFoundRoute` option to configure a default not-found/404 route for every branch of the route tree. @@ -28,7 +28,7 @@ Omit< > ``` -- [RouteOptions](./RouteOptionsType.md) +- [RouteOptions](../RouteOptionsType.md) - Required - The options that will be used to configure the not found route instance. diff --git a/docs/router/framework/react/api/router/RedirectType.md b/docs/router/framework/react/api/router/RedirectType.md index b1727a0689..4955d06cd0 100644 --- a/docs/router/framework/react/api/router/RedirectType.md +++ b/docs/router/framework/react/api/router/RedirectType.md @@ -13,7 +13,7 @@ export type Redirect = { } & NavigateOptions ``` -- [`NavigateOptions`](./NavigateOptionsType.md) +- [`NavigateOptions`](../NavigateOptionsType.md) ## Redirect properties diff --git a/docs/router/framework/react/api/router/RootRouteClass.md b/docs/router/framework/react/api/router/RootRouteClass.md index 1553dd2550..c0e7e2e6f3 100644 --- a/docs/router/framework/react/api/router/RootRouteClass.md +++ b/docs/router/framework/react/api/router/RootRouteClass.md @@ -5,7 +5,7 @@ title: RootRoute class > [!CAUTION] > This class has been deprecated and will be removed in the next major version of TanStack Router. -> Please use the [`createRootRoute`](./createRootRouteFunction.md) function instead. +> Please use the [`createRootRoute`](../createRootRouteFunction.md) function instead. The `RootRoute` class extends the `Route` class and can be used to create a root route instance. A root route instance can then be used to create a route tree. @@ -31,12 +31,12 @@ Omit< > ``` -- [`RouteOptions`](./RouteOptionsType.md) +- [`RouteOptions`](../RouteOptionsType.md) - Optional ## Constructor returns -A new [`Route`](./RouteType.md) instance. +A new [`Route`](../RouteType.md) instance. ## Examples diff --git a/docs/router/framework/react/api/router/RouteApiClass.md b/docs/router/framework/react/api/router/RouteApiClass.md index 387089cdf3..60fa514405 100644 --- a/docs/router/framework/react/api/router/RouteApiClass.md +++ b/docs/router/framework/react/api/router/RouteApiClass.md @@ -5,7 +5,7 @@ title: RouteApi class > [!CAUTION] > This class has been deprecated and will be removed in the next major version of TanStack Router. -> Please use the [`getRouteApi`](./getRouteApiFunction.md) function instead. +> Please use the [`getRouteApi`](../getRouteApiFunction.md) function instead. The `RouteApi` class provides type-safe version of common hooks like `useParams`, `useSearch`, `useRouteContext`, `useNavigate`, `useLoaderData`, and `useLoaderDeps` that are pre-bound to a specific route ID and corresponding registered route types. @@ -21,7 +21,7 @@ The `RouteApi` constructor accepts a single argument: the `options` that will be ## Constructor returns -- An instance of the [`RouteApi`](./RouteApiType.md) that is pre-bound to the route ID that it was called with. +- An instance of the [`RouteApi`](../RouteApiType.md) that is pre-bound to the route ID that it was called with. ## Examples diff --git a/docs/router/framework/react/api/router/RouteApiType.md b/docs/router/framework/react/api/router/RouteApiType.md index a5d806397b..36e1f4c78e 100644 --- a/docs/router/framework/react/api/router/RouteApiType.md +++ b/docs/router/framework/react/api/router/RouteApiType.md @@ -17,7 +17,7 @@ The `RouteApi` has the following properties and methods: }): TSelected ``` -- A type-safe version of the [`useMatch`](./useMatchHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of the [`useMatch`](../useMatchHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. - Options - `opts.select` - Optional @@ -27,7 +27,7 @@ The `RouteApi` has the following properties and methods: - Optional - `boolean` - Configures whether structural sharing is enabled for the value returned by `select`. - - See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. + - See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `RouteMatch` object or a loosened version of the `RouteMatch` object if `opts.strict` is `false`. @@ -40,7 +40,7 @@ The `RouteApi` has the following properties and methods: }): TSelected ``` -- A type-safe version of the [`useRouteContext`](./useRouteContextHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of the [`useRouteContext`](../useRouteContextHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. - Options - `opts.select` - Optional @@ -58,7 +58,7 @@ The `RouteApi` has the following properties and methods: }): TSelected ``` -- A type-safe version of the [`useSearch`](./useSearchHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of the [`useSearch`](../useSearchHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. - Options - `opts.select` - Optional @@ -68,7 +68,7 @@ The `RouteApi` has the following properties and methods: - Optional - `boolean` - Configures whether structural sharing is enabled for the value returned by `select`. - - See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. + - See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `TFullSearchSchema` object or a loosened version of the `TFullSearchSchema` object if `opts.strict` is `false`. @@ -81,7 +81,7 @@ The `RouteApi` has the following properties and methods: }): TSelected ``` -- A type-safe version of the [`useParams`](./useParamsHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of the [`useParams`](../useParamsHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. - Options - `opts.select` - Optional @@ -91,7 +91,7 @@ The `RouteApi` has the following properties and methods: - Optional - `boolean` - Configures whether structural sharing is enabled for the value returned by `select`. - - See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. + - See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `TAllParams` object or a loosened version of the `TAllParams` object if `opts.strict` is `false`. @@ -104,7 +104,7 @@ The `RouteApi` has the following properties and methods: }): TSelected ``` -- A type-safe version of the [`useLoaderData`](./useLoaderDataHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of the [`useLoaderData`](../useLoaderDataHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. - Options - `opts.select` - Optional @@ -114,7 +114,7 @@ The `RouteApi` has the following properties and methods: - Optional - `boolean` - Configures whether structural sharing is enabled for the value returned by `select`. - - See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. + - See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `TLoaderData` object or a loosened version of the `TLoaderData` object if `opts.strict` is `false`. @@ -127,7 +127,7 @@ The `RouteApi` has the following properties and methods: }): TSelected ``` -- A type-safe version of the [`useLoaderDeps`](./useLoaderDepsHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of the [`useLoaderDeps`](../useLoaderDepsHook.md) hook that is pre-bound to the route ID that the `RouteApi` instance was created with. - Options - `opts.select` - Optional @@ -137,7 +137,7 @@ The `RouteApi` has the following properties and methods: - Optional - `boolean` - Configures whether structural sharing is enabled for the value returned by `select`. - - See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. + - See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `TLoaderDeps` object. @@ -148,4 +148,4 @@ The `RouteApi` has the following properties and methods: useNavigate(): // navigate function ``` -- A type-safe version of [`useNavigate`](./useNavigateHook.md) that is pre-bound to the route ID that the `RouteApi` instance was created with. +- A type-safe version of [`useNavigate`](../useNavigateHook.md) that is pre-bound to the route ID that the `RouteApi` instance was created with. diff --git a/docs/router/framework/react/api/router/RouteClass.md b/docs/router/framework/react/api/router/RouteClass.md index 514a58cd41..7658092ca4 100644 --- a/docs/router/framework/react/api/router/RouteClass.md +++ b/docs/router/framework/react/api/router/RouteClass.md @@ -5,7 +5,7 @@ title: Route class > [!CAUTION] > This class has been deprecated and will be removed in the next major version of TanStack Router. -> Please use the [`createRoute`](./createRouteFunction.md) function instead. +> Please use the [`createRoute`](../createRouteFunction.md) function instead. The `Route` class implements the `RouteApi` class and can be used to create route instances. A route instance can then be used to create a route tree. @@ -15,13 +15,13 @@ The `Route` constructor accepts an object as its only argument. ### Constructor options -- Type: [`RouteOptions`](./RouteOptionsType.md) +- Type: [`RouteOptions`](../RouteOptionsType.md) - Required - The options that will be used to configure the route instance ### Constructor returns -A new [`Route`](./RouteType.md) instance. +A new [`Route`](../RouteType.md) instance. ## Examples diff --git a/docs/router/framework/react/api/router/RouteMaskType.md b/docs/router/framework/react/api/router/RouteMaskType.md index d3e22c0c0d..9ce6e42e36 100644 --- a/docs/router/framework/react/api/router/RouteMaskType.md +++ b/docs/router/framework/react/api/router/RouteMaskType.md @@ -3,7 +3,7 @@ id: RouteMaskType title: RouteMask type --- -The `RouteMask` type extends the [`ToOptions`](./ToOptionsType.md) type and has other the necessary properties to create a route mask. +The `RouteMask` type extends the [`ToOptions`](../ToOptionsType.md) type and has other the necessary properties to create a route mask. ## RouteMask properties @@ -11,7 +11,7 @@ The `RouteMask` type accepts an object with the following properties: ### `...ToOptions` -- Type: [`ToOptions`](./ToOptionsType.md) +- Type: [`ToOptions`](../ToOptionsType.md) - Required - The options that will be used to configure the route mask diff --git a/docs/router/framework/react/api/router/RouteOptionsType.md b/docs/router/framework/react/api/router/RouteOptionsType.md index 92510684ea..9c429554b8 100644 --- a/docs/router/framework/react/api/router/RouteOptionsType.md +++ b/docs/router/framework/react/api/router/RouteOptionsType.md @@ -65,13 +65,13 @@ The `RouteOptions` type accepts an object with the following properties: - Search middlewares are functions that transform the search parameters when generating new links for a route or its descendants. - A search middleware is passed in the current search (if it is the first middleware to run) or is invoked by the previous middleware calling `next`. -### `parseParams` method (⚠️ deprecated) +### `parseParams` method (⚠️ deprecated, use `params.parse` instead) - Type: `(rawParams: Record) => TParams` - Optional - A function that will be called when this route is matched and passed the raw params from the current location and return valid parsed params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function does not throw, its return value will be used as the route's params and the return type will be inferred into the rest of the router. -### `stringifyParams` method (⚠️ deprecated) +### `stringifyParams` method (⚠️ deprecated, use `params.stringify` instead) - Type: `(params: TParams) => Record` - Required if `parseParams` is provided @@ -109,13 +109,13 @@ type beforeLoad = ( ``` - Optional -- [`ParsedLocation`](./ParsedLocationType.md) +- [`ParsedLocation`](../ParsedLocationType.md) - This async function is called before a route is loaded. If an error is thrown here, the route's loader will not be called and the route will not render. If thrown during a navigation, the navigation will be canceled and the error will be passed to the `onError` function. If thrown during a preload event, the error will be logged to the console and the preload will fail. - If this function returns a promise, the route will be put into a pending state and cause rendering to suspend until the promise resolves. If this route's pendingMs threshold is reached, the `pendingComponent` will be shown until it resolves. If the promise rejects, the route will be put into an error state and the error will be thrown during render. - If this function returns a `TRouteContext` object, that object will be merged into the route's context and be made available in the `loader` and other related route components/methods. - It's common to use this function to check if a user is authenticated and redirect them to a login page if they are not. To do this, you can either return or throw a `redirect` object from this function. -> 🚧 `opts.navigate` has been deprecated and will be removed in the next major release. Use `throw redirect({ to: '/somewhere' })` instead. Read more about the `redirect` function [here](./redirectFunction.md). +> 🚧 `opts.navigate` has been deprecated and will be removed in the next major release. Use `throw redirect({ to: '/somewhere' })` instead. Read more about the `redirect` function [here](../redirectFunction.md). ### `loader` method @@ -124,26 +124,27 @@ type beforeLoad = ( ```tsx type loader = ( opts: RouteMatch & { - search: TFullSearchSchema abortController: AbortController - preload: boolean - params: TAllParams + cause: 'enter' | 'stay' context: TAllContext + deps: TLoaderDeps location: ParsedLocation + params: TAllParams + preload: boolean + parentMatchPromise: Promise> navigate: NavigateFn // @deprecated - buildLocation: BuildLocationFn - cause: 'enter' | 'stay' }, ) => Promise | TLoaderData | void ``` - Optional -- [`ParsedLocation`](./ParsedLocationType.md) +- [`ParsedLocation`](../ParsedLocationType.md) - This async function is called when a route is matched and passed the route's match object. If an error is thrown here, the route will be put into an error state and the error will be thrown during render. If thrown during a navigation, the navigation will be canceled and the error will be passed to the `onError` function. If thrown during a preload event, the error will be logged to the console and the preload will fail. - If this function returns a promise, the route will be put into a pending state and cause rendering to suspend until the promise resolves. If this route's pendingMs threshold is reached, the `pendingComponent` will be shown until it resolves. If the promise rejects, the route will be put into an error state and the error will be thrown during render. - If this function returns a `TLoaderData` object, that object will be stored on the route match until the route match is no longer active. It can be accessed using the `useLoaderData` hook in any component that is a child of the route match before another `` is rendered. +- Deps must be returned by your `loaderDeps` function in order to appear. -> 🚧 `opts.navigate` has been deprecated and will be removed in the next major release. Use `throw redirect({ to: '/somewhere' })` instead. Read more about the `redirect` function [here](./redirectFunction.md). +> 🚧 `opts.navigate` has been deprecated and will be removed in the next major release. Use `throw redirect({ to: '/somewhere' })` instead. Read more about the `redirect` function [here](../redirectFunction.md). ### `loaderDeps` method @@ -241,7 +242,7 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Type: `(error: any) => void` - Optional - A function that will be called when an error is thrown during a navigation or preload event. -- If this function throws a [`redirect`](./redirectFunction.md), then the router will process and apply the redirect immediately. +- If this function throws a [`redirect`](../redirectFunction.md), then the router will process and apply the redirect immediately. ### `onEnter` property @@ -292,7 +293,7 @@ interface RemountDepsOptions< - The return value needs to be JSON serializable. - By default, a route component will not be remounted if it stays active after a navigation. -Example: +Example: If you want to configure to remount a route component upon `params` change, use: ```tsx diff --git a/docs/router/framework/react/api/router/RouteType.md b/docs/router/framework/react/api/router/RouteType.md index 57e23ecc8b..6e2c675c9a 100644 --- a/docs/router/framework/react/api/router/RouteType.md +++ b/docs/router/framework/react/api/router/RouteType.md @@ -28,4 +28,4 @@ An instance of the `Route` has the following properties and methods: ### ...`RouteApi` methods -- All of the methods from [`RouteApi`](./RouteApiType.md) are available. +- All of the methods from [`RouteApi`](../RouteApiType.md) are available. diff --git a/docs/router/framework/react/api/router/RouterClass.md b/docs/router/framework/react/api/router/RouterClass.md index dfb4589b77..24fa525afc 100644 --- a/docs/router/framework/react/api/router/RouterClass.md +++ b/docs/router/framework/react/api/router/RouterClass.md @@ -5,7 +5,7 @@ title: Router Class > [!CAUTION] > This class has been deprecated and will be removed in the next major version of TanStack Router. -> Please use the [`createRouter`](./createRouterFunction.md) function instead. +> Please use the [`createRouter`](../createRouterFunction.md) function instead. The `Router` class is used to instantiate a new router instance. @@ -15,13 +15,13 @@ The `Router` constructor accepts a single argument: the `options` that will be u ### Constructor options -- Type: [`RouterOptions`](./RouterOptionsType.md) +- Type: [`RouterOptions`](../RouterOptionsType.md) - Required - The options that will be used to configure the router instance. ### Constructor returns -- An instance of the [`Router`](./RouterType.md). +- An instance of the [`Router`](../RouterType.md). ## Examples diff --git a/docs/router/framework/react/api/router/RouterEventsType.md b/docs/router/framework/react/api/router/RouterEventsType.md index fd1b6dfb3d..0d31f05ce3 100644 --- a/docs/router/framework/react/api/router/RouterEventsType.md +++ b/docs/router/framework/react/api/router/RouterEventsType.md @@ -66,12 +66,12 @@ Once an event is emitted, the following properties will be present on the event ### `fromLocation` property -- Type: [`ParsedLocation`](./ParsedLocationType.md) +- Type: [`ParsedLocation`](../ParsedLocationType.md) - The location that the router is transitioning from. ### `toLocation` property -- Type: [`ParsedLocation`](./ParsedLocationType.md) +- Type: [`ParsedLocation`](../ParsedLocationType.md) - The location that the router is transitioning to. ### `pathChanged` property diff --git a/docs/router/framework/react/api/router/RouterOptionsType.md b/docs/router/framework/react/api/router/RouterOptionsType.md index 8d78f2719a..2bd2ecea9a 100644 --- a/docs/router/framework/react/api/router/RouterOptionsType.md +++ b/docs/router/framework/react/api/router/RouterOptionsType.md @@ -141,7 +141,7 @@ The `RouterOptions` type accepts an object with the following properties and met - Type: `boolean | ViewTransitionOptions` - Optional - If `true`, route navigations will be called using `document.startViewTransition()`. -- If [`ViewTransitionOptions`](./ViewTransitionOptionsType.md), route navigations will be called using `document.startViewTransition({update, types})` +- If [`ViewTransitionOptions`](../ViewTransitionOptionsType.md), route navigations will be called using `document.startViewTransition({update, types})` where `types` will be the strings array passed with `ViewTransitionOptions["types"]`. If the browser does not support viewTransition types, the navigation will fall back to normal `document.startTransition()`, same as if `true` was passed. - If the browser does not support this api, this option will be ignored. @@ -174,7 +174,7 @@ The `RouterOptions` type accepts an object with the following properties and met ### `context` property - Type: `any` -- Optional or required if the root route was created with [`createRootRouteWithContext()`](./createRootRouteWithContextFunction.md). +- Optional or required if the root route was created with [`createRootRouteWithContext()`](../createRootRouteWithContextFunction.md). - The root context that will be provided to all routes in the route tree. This can be used to provide a context to all routes in the tree without having to provide it to each route individually. ### `dehydrate` method @@ -251,7 +251,7 @@ const router = createRouter({ - Type: `'root' | 'fuzzy'` - Optional - Defaults to `'fuzzy'` -- This property controls how TanStack Router will handle scenarios where it cannot find a route to match the current location. See the [Not Found Errors guide](../../guide/not-found-errors.md) for more information. +- This property controls how TanStack Router will handle scenarios where it cannot find a route to match the current location. See the [Not Found Errors guide](../../../guide/not-found-errors.md) for more information. ### `notFoundRoute` property @@ -295,7 +295,7 @@ const router = createRouter({ - Optional - Defaults to `false` - Configures whether structural sharing is enabled by default for fine-grained selectors. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ### `defaultRemountDeps` property diff --git a/docs/router/framework/react/api/router/RouterStateType.md b/docs/router/framework/react/api/router/RouterStateType.md index 6c4d2aca93..401ffdfef4 100644 --- a/docs/router/framework/react/api/router/RouterStateType.md +++ b/docs/router/framework/react/api/router/RouterStateType.md @@ -38,20 +38,20 @@ The `RouterState` type contains all of the properties that are available on the ### `matches` property -- Type: [`Array`](./RouteMatchType.md) +- Type: [`Array`](../RouteMatchType.md) - An array of all of the route matches that have been resolved and are currently active. ### `pendingMatches` property -- Type: [`Array`](./RouteMatchType.md) +- Type: [`Array`](../RouteMatchType.md) - An array of all of the route matches that are currently pending. ### `location` property -- Type: [`ParsedLocation`](./ParsedLocationType.md) +- Type: [`ParsedLocation`](../ParsedLocationType.md) - The latest location that the router has parsed from the browser history. This location may not be resolved and loaded yet. ### `resolvedLocation` property -- Type: [`ParsedLocation`](./ParsedLocationType.md) +- Type: [`ParsedLocation`](../ParsedLocationType.md) - The location that the router has resolved and loaded. diff --git a/docs/router/framework/react/api/router/RouterType.md b/docs/router/framework/react/api/router/RouterType.md index cd4f4d8504..7f44c8c582 100644 --- a/docs/router/framework/react/api/router/RouterType.md +++ b/docs/router/framework/react/api/router/RouterType.md @@ -16,15 +16,15 @@ An instance of the `Router` has the following properties and methods: ### `state` property -- Type: [`RouterState`](./RouterStateType.md) +- Type: [`RouterState`](../RouterStateType.md) - The current state of the router. -> ⚠️⚠️⚠️ **`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.** +> ⚠️⚠️⚠️ **`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.** ### `.subscribe` method - Type: `(eventType: TType, fn: ListenerFn) => (event: RouterEvent) => void` -- Subscribes to a [`RouterEvent`](./RouterEventsType.md). +- Subscribes to a [`RouterEvent`](../RouterEventsType.md). - Returns a function that can be used to unsubscribe from the event. - The callback provided to the returned function will be called with the event that was emitted. @@ -100,7 +100,7 @@ Commits a new location object to the browser history. ``` - Properties - `location` - - Type: [`ParsedLocation`](./ParsedLocationType.md) + - Type: [`ParsedLocation`](../ParsedLocationType.md) - Required - The location to commit to the browser history. - `replace` diff --git a/docs/router/framework/react/api/router/ToMaskOptionsType.md b/docs/router/framework/react/api/router/ToMaskOptionsType.md index fa0140fec2..3c725af540 100644 --- a/docs/router/framework/react/api/router/ToMaskOptionsType.md +++ b/docs/router/framework/react/api/router/ToMaskOptionsType.md @@ -3,7 +3,7 @@ id: ToMaskOptionsType title: ToMaskOptions type --- -The `ToMaskOptions` type extends the [`ToOptions`](./ToOptionsType.md) type and describes additional options available when using route masks. +The `ToMaskOptions` type extends the [`ToOptions`](../ToOptionsType.md) type and describes additional options available when using route masks. ```tsx type ToMaskOptions = ToOptions & { @@ -11,4 +11,4 @@ type ToMaskOptions = ToOptions & { } ``` -- [`ToOptions`](./ToOptionsType.md) +- [`ToOptions`](../ToOptionsType.md) diff --git a/docs/router/framework/react/api/router/UseMatchRouteOptionsType.md b/docs/router/framework/react/api/router/UseMatchRouteOptionsType.md index 885b7d26d3..a7e622a319 100644 --- a/docs/router/framework/react/api/router/UseMatchRouteOptionsType.md +++ b/docs/router/framework/react/api/router/UseMatchRouteOptionsType.md @@ -3,11 +3,11 @@ id: UseMatchRouteOptionsType title: UseMatchRouteOptions type --- -The `UseMatchRouteOptions` type extends the [`ToOptions`](./ToOptionsType.md) type and describes additional options available when using the [`useMatchRoute`](./useMatchRouteHook.md) hook. +The `UseMatchRouteOptions` type extends the [`ToOptions`](../ToOptionsType.md) type and describes additional options available when using the [`useMatchRoute`](../useMatchRouteHook.md) hook. ```tsx export type UseMatchRouteOptions = ToOptions & MatchRouteOptions ``` -- [`ToOptions`](./ToOptionsType.md) -- [`MatchRouteOptions`](./MatchRouteOptionsType.md) +- [`ToOptions`](../ToOptionsType.md) +- [`MatchRouteOptions`](../MatchRouteOptionsType.md) diff --git a/docs/router/framework/react/api/router/catchBoundaryComponent.md b/docs/router/framework/react/api/router/catchBoundaryComponent.md index deef0ffa22..ac3871cb21 100644 --- a/docs/router/framework/react/api/router/catchBoundaryComponent.md +++ b/docs/router/framework/react/api/router/catchBoundaryComponent.md @@ -24,7 +24,7 @@ The `CatchBoundary` component accepts the following props: ### `props.errorComponent` prop - Type: `React.ReactNode` -- Optional - [`default: ErrorComponent`](./errorComponentComponent.md) +- Optional - [`default: ErrorComponent`](../errorComponentComponent.md) - The component to render when there is an error. ### `props.onCatch` prop diff --git a/docs/router/framework/react/api/router/createFileRouteFunction.md b/docs/router/framework/react/api/router/createFileRouteFunction.md index 945e713075..e6330da51e 100644 --- a/docs/router/framework/react/api/router/createFileRouteFunction.md +++ b/docs/router/framework/react/api/router/createFileRouteFunction.md @@ -17,7 +17,7 @@ The `createFileRoute` function accepts a single argument of type `string` that r ## createFileRoute returns -A new function that accepts a single argument of type [`RouteOptions`](./RouteOptionsType.md) that will be used to configure the file [`Route`](./RouteType.md) instance. +A new function that accepts a single argument of type [`RouteOptions`](../RouteOptionsType.md) that will be used to configure the file [`Route`](../RouteType.md) instance. > ⚠️ Note: For `tsr generate` and `tsr watch` to work properly, the file route instance must be exported from the file using the `Route` identifier. diff --git a/docs/router/framework/react/api/router/createLazyFileRouteFunction.md b/docs/router/framework/react/api/router/createLazyFileRouteFunction.md index 87e1743ed1..5d00fb7458 100644 --- a/docs/router/framework/react/api/router/createLazyFileRouteFunction.md +++ b/docs/router/framework/react/api/router/createLazyFileRouteFunction.md @@ -3,7 +3,7 @@ id: createLazyFileRouteFunction title: createLazyFileRoute function --- -The `createLazyFileRoute` function is used for creating a partial file-based route route instance that is lazily loaded when matched. This route instance can only be used to configure the [non-critical properties](../../guide/code-splitting.md#how-does-tanstack-router-split-code) of the route, such as `component`, `pendingComponent`, `errorComponent`, and the `notFoundComponent`. +The `createLazyFileRoute` function is used for creating a partial file-based route route instance that is lazily loaded when matched. This route instance can only be used to configure the [non-critical properties](../../../guide/code-splitting.md#how-does-tanstack-router-split-code) of the route, such as `component`, `pendingComponent`, `errorComponent`, and the `notFoundComponent`. ## createLazyFileRoute options @@ -17,7 +17,7 @@ The `createLazyFileRoute` function accepts a single argument of type `string` th ### createLazyFileRoute returns -A new function that accepts a single argument of partial of the type [`RouteOptions`](./RouteOptionsType.md) that will be used to configure the file [`Route`](./RouteType.md) instance. +A new function that accepts a single argument of partial of the type [`RouteOptions`](../RouteOptionsType.md) that will be used to configure the file [`Route`](../RouteType.md) instance. - Type: @@ -28,7 +28,7 @@ Pick< > ``` -- [`RouteOptions`](./RouteOptionsType.md) +- [`RouteOptions`](../RouteOptionsType.md) > ⚠️ Note: For `tsr generate` and `tsr watch` to work properly, the file route instance must be exported from the file using the `Route` identifier. diff --git a/docs/router/framework/react/api/router/createLazyRouteFunction.md b/docs/router/framework/react/api/router/createLazyRouteFunction.md index 48c100c352..57e9fcdc71 100644 --- a/docs/router/framework/react/api/router/createLazyRouteFunction.md +++ b/docs/router/framework/react/api/router/createLazyRouteFunction.md @@ -3,7 +3,7 @@ id: createLazyRouteFunction title: createLazyRoute function --- -The `createLazyRoute` function is used for creating a partial code-based route route instance that is lazily loaded when matched. This route instance can only be used to configure the [non-critical properties](../../guide/code-splitting.md#how-does-tanstack-router-split-code) of the route, such as `component`, `pendingComponent`, `errorComponent`, and the `notFoundComponent`. +The `createLazyRoute` function is used for creating a partial code-based route route instance that is lazily loaded when matched. This route instance can only be used to configure the [non-critical properties](../../../guide/code-splitting.md#how-does-tanstack-router-split-code) of the route, such as `component`, `pendingComponent`, `errorComponent`, and the `notFoundComponent`. ## createLazyRoute options @@ -17,7 +17,7 @@ The `createLazyRoute` function accepts a single argument of type `string` that r ### createLazyRoute returns -A new function that accepts a single argument of partial of the type [`RouteOptions`](./RouteOptionsType.md) that will be used to configure the file [`Route`](./RouteType.md) instance. +A new function that accepts a single argument of partial of the type [`RouteOptions`](../RouteOptionsType.md) that will be used to configure the file [`Route`](../RouteType.md) instance. - Type: @@ -28,7 +28,7 @@ Pick< > ``` -- [`RouteOptions`](./RouteOptionsType.md) +- [`RouteOptions`](../RouteOptionsType.md) > ⚠️ Note: This route instance must be manually lazily loaded against its critical route instance using the `lazy` method returned by the `createRoute` function. diff --git a/docs/router/framework/react/api/router/createRootRouteFunction.md b/docs/router/framework/react/api/router/createRootRouteFunction.md index 27cff72032..6494924a70 100644 --- a/docs/router/framework/react/api/router/createRootRouteFunction.md +++ b/docs/router/framework/react/api/router/createRootRouteFunction.md @@ -23,12 +23,12 @@ Omit< > ``` -- [`RouteOptions`](./RouteOptionsType.md) +- [`RouteOptions`](../RouteOptionsType.md) - Optional ## createRootRoute returns -A new [`Route`](./RouteType.md) instance. +A new [`Route`](../RouteType.md) instance. ## Examples diff --git a/docs/router/framework/react/api/router/createRootRouteWithContextFunction.md b/docs/router/framework/react/api/router/createRootRouteWithContextFunction.md index 7e15737539..38d5c39496 100644 --- a/docs/router/framework/react/api/router/createRootRouteWithContextFunction.md +++ b/docs/router/framework/react/api/router/createRootRouteWithContextFunction.md @@ -17,8 +17,8 @@ The `createRootRouteWithContext` function accepts a single generic argument: ## createRootRouteWithContext returns -- A factory function that can be used to create a new [`createRootRoute`](./createRootRouteFunction.md) instance. -- It accepts a single argument, the same as the [`createRootRoute`](./createRootRouteFunction.md) function. +- A factory function that can be used to create a new [`createRootRoute`](../createRootRouteFunction.md) instance. +- It accepts a single argument, the same as the [`createRootRoute`](../createRootRouteFunction.md) function. ## Examples diff --git a/docs/router/framework/react/api/router/createRouteFunction.md b/docs/router/framework/react/api/router/createRouteFunction.md index 2ca9bdcb2d..7294e6abc7 100644 --- a/docs/router/framework/react/api/router/createRouteFunction.md +++ b/docs/router/framework/react/api/router/createRouteFunction.md @@ -3,17 +3,17 @@ id: createRouteFunction title: createRoute function --- -The `createRoute` function implements returns a [`Route`](./RouteType.md) instance. A route instance can then be passed to a root route's children to create a route tree, which is then passed to the router. +The `createRoute` function implements returns a [`Route`](../RouteType.md) instance. A route instance can then be passed to a root route's children to create a route tree, which is then passed to the router. ## createRoute options -- Type: [`RouteOptions`](./RouteOptionsType.md) +- Type: [`RouteOptions`](../RouteOptionsType.md) - Required - The options that will be used to configure the route instance ## createRoute returns -A new [`Route`](./RouteType.md) instance. +A new [`Route`](../RouteType.md) instance. ## Examples diff --git a/docs/router/framework/react/api/router/createRouteMaskFunction.md b/docs/router/framework/react/api/router/createRouteMaskFunction.md index 64722c57b3..45210ff31d 100644 --- a/docs/router/framework/react/api/router/createRouteMaskFunction.md +++ b/docs/router/framework/react/api/router/createRouteMaskFunction.md @@ -7,13 +7,13 @@ The `createRouteMask` function is a helper function that can be used to create a ## createRouteMask options -- Type: [`RouteMask`](./RouteMaskType.md) +- Type: [`RouteMask`](../RouteMaskType.md) - Required - The options that will be used to configure the route mask ## createRouteMask returns -- A object with the type signature of [`RouteMask`](./RouteMaskType.md) that can be passed to the `RouterOptions.routeMasks` option. +- A object with the type signature of [`RouteMask`](../RouteMaskType.md) that can be passed to the `RouterOptions.routeMasks` option. ## Examples diff --git a/docs/router/framework/react/api/router/createRouterFunction.md b/docs/router/framework/react/api/router/createRouterFunction.md index 7fe5779f7d..d82df5e5a7 100644 --- a/docs/router/framework/react/api/router/createRouterFunction.md +++ b/docs/router/framework/react/api/router/createRouterFunction.md @@ -3,17 +3,17 @@ id: createRouterFunction title: createRouter function --- -The `createRouter` function accepts a [`RouterOptions`](./RouterOptionsType.md) object and creates a new [`Router`](./RouterClass.md) instance. +The `createRouter` function accepts a [`RouterOptions`](../RouterOptionsType.md) object and creates a new [`Router`](../RouterClass.md) instance. ## createRouter options -- Type: [`RouterOptions`](./RouterOptionsType.md) +- Type: [`RouterOptions`](../RouterOptionsType.md) - Required - The options that will be used to configure the router instance. ## createRouter returns -- An instance of the [`Router`](./RouterType.md). +- An instance of the [`Router`](../RouterType.md). ## Examples diff --git a/docs/router/framework/react/api/router/deferFunction.md b/docs/router/framework/react/api/router/deferFunction.md index 929b9d7c63..7c1c1130c1 100644 --- a/docs/router/framework/react/api/router/deferFunction.md +++ b/docs/router/framework/react/api/router/deferFunction.md @@ -6,7 +6,7 @@ title: defer function > [!CAUTION] > You don't need to call `defer` manually anymore, Promises are handled automatically now. -The `defer` function wraps a promise with a deferred state object that can be used to inspect the promise's state. This deferred promise can then be passed to the [`useAwaited`](./useAwaitedHook.md) hook or the [``](./awaitComponent.md) component for suspending until the promise is resolved or rejected. +The `defer` function wraps a promise with a deferred state object that can be used to inspect the promise's state. This deferred promise can then be passed to the [`useAwaited`](../useAwaitedHook.md) hook or the [``](../awaitComponent.md) component for suspending until the promise is resolved or rejected. The `defer` function accepts a single argument, the `promise` to wrap with a deferred state object. @@ -18,7 +18,7 @@ The `defer` function accepts a single argument, the `promise` to wrap with a def ## defer returns -- A promise that can be passed to the [`useAwaited`](./useAwaitedHook.md) hook or the [``](./awaitComponent.md) component. +- A promise that can be passed to the [`useAwaited`](../useAwaitedHook.md) hook or the [``](../awaitComponent.md) component. ## Examples diff --git a/docs/router/framework/react/api/router/getRouteApiFunction.md b/docs/router/framework/react/api/router/getRouteApiFunction.md index cd78075a3a..6aa13b74a9 100644 --- a/docs/router/framework/react/api/router/getRouteApiFunction.md +++ b/docs/router/framework/react/api/router/getRouteApiFunction.md @@ -13,11 +13,11 @@ The `getRouteApi` function accepts a single argument, a `routeId` string literal - Type: `string` - Required -- The route ID to which the [`RouteApi`](./RouteApiClass.md) instance will be bound +- The route ID to which the [`RouteApi`](../RouteApiClass.md) instance will be bound ## getRouteApi returns -- An instance of the [`RouteApi`](./RouteApiType.md) that is pre-bound to the route ID that the `getRouteApi` function was called with. +- An instance of the [`RouteApi`](../RouteApiType.md) that is pre-bound to the route ID that the `getRouteApi` function was called with. ## Examples diff --git a/docs/router/framework/react/api/router/isNotFoundFunction.md b/docs/router/framework/react/api/router/isNotFoundFunction.md index 17411b7bbe..8ace56b353 100644 --- a/docs/router/framework/react/api/router/isNotFoundFunction.md +++ b/docs/router/framework/react/api/router/isNotFoundFunction.md @@ -3,7 +3,7 @@ id: isNotFoundFunction title: isNotFound function --- -The `isNotFound` function can be used to determine if an object is a [`NotFoundError`](./NotFoundErrorType.md) object. +The `isNotFound` function can be used to determine if an object is a [`NotFoundError`](../NotFoundErrorType.md) object. ## isNotFound options @@ -13,13 +13,13 @@ The `isNotFound` function accepts a single argument, an `input`. - Type: `unknown` - Required -- An object to check if it is a [`NotFoundError`](./NotFoundErrorType.md). +- An object to check if it is a [`NotFoundError`](../NotFoundErrorType.md). ## isNotFound returns - Type: `boolean` -- `true` if the object is a [`NotFoundError`](./NotFoundErrorType.md). -- `false` if the object is not a [`NotFoundError`](./NotFoundErrorType.md). +- `true` if the object is a [`NotFoundError`](../NotFoundErrorType.md). +- `false` if the object is not a [`NotFoundError`](../NotFoundErrorType.md). ## Examples diff --git a/docs/router/framework/react/api/router/linkComponent.md b/docs/router/framework/react/api/router/linkComponent.md index 6c973d80df..dd990f1425 100644 --- a/docs/router/framework/react/api/router/linkComponent.md +++ b/docs/router/framework/react/api/router/linkComponent.md @@ -12,7 +12,7 @@ The `Link` component accepts the following props: ### `...props` - Type: `LinkProps & React.RefAttributes` -- [`LinkProps`](./LinkPropsType.md) +- [`LinkProps`](../LinkPropsType.md) ## Link returns diff --git a/docs/router/framework/react/api/router/linkOptions.md b/docs/router/framework/react/api/router/linkOptions.md index 2c828500be..7226afaf8a 100644 --- a/docs/router/framework/react/api/router/linkOptions.md +++ b/docs/router/framework/react/api/router/linkOptions.md @@ -12,7 +12,7 @@ The `linkOptions` accepts the following option: ### `...props` - Type: `LinkProps & React.RefAttributes` -- [`LinkProps`](./LinkPropsType.md) +- [`LinkProps`](../LinkPropsType.md) ## `linkOptions` returns diff --git a/docs/router/framework/react/api/router/matchRouteComponent.md b/docs/router/framework/react/api/router/matchRouteComponent.md index 2deba6762e..be23a0b18e 100644 --- a/docs/router/framework/react/api/router/matchRouteComponent.md +++ b/docs/router/framework/react/api/router/matchRouteComponent.md @@ -11,7 +11,7 @@ The `MatchRoute` component accepts the same options as the `useMatchRoute` hook ### `...props` prop -- Type: [`UseMatchRouteOptions`](./UseMatchRouteOptionsType.md) +- Type: [`UseMatchRouteOptions`](../UseMatchRouteOptionsType.md) ### `children` prop diff --git a/docs/router/framework/react/api/router/navigateComponent.md b/docs/router/framework/react/api/router/navigateComponent.md index a4431eaf87..c66e9dd6ac 100644 --- a/docs/router/framework/react/api/router/navigateComponent.md +++ b/docs/router/framework/react/api/router/navigateComponent.md @@ -11,7 +11,7 @@ The `Navigate` component accepts the following props: ### `...options` -- Type: [`NavigateOptions`](./NavigateOptionsType.md) +- Type: [`NavigateOptions`](../NavigateOptionsType.md) ## Navigate returns diff --git a/docs/router/framework/react/api/router/notFoundFunction.md b/docs/router/framework/react/api/router/notFoundFunction.md index 02bb26bfbc..53848b4af8 100644 --- a/docs/router/framework/react/api/router/notFoundFunction.md +++ b/docs/router/framework/react/api/router/notFoundFunction.md @@ -9,7 +9,7 @@ The `notFound` function returns a new `NotFoundError` object that can be either The `notFound` function accepts a single optional argument, the `options` to create the not-found error object. -- Type: [`Partial`](./NotFoundErrorType.md) +- Type: [`Partial`](../NotFoundErrorType.md) - Optional ## notFound returns diff --git a/docs/router/framework/react/api/router/redirectFunction.md b/docs/router/framework/react/api/router/redirectFunction.md index 1e1c83dfb3..14d70225b6 100644 --- a/docs/router/framework/react/api/router/redirectFunction.md +++ b/docs/router/framework/react/api/router/redirectFunction.md @@ -9,7 +9,7 @@ The `redirect` function returns a new `Redirect` object that can be either retur The `redirect` function accepts a single argument, the `options` to determine the redirect behavior. -- Type: [`Redirect`](./RedirectType.md) +- Type: [`Redirect`](../RedirectType.md) - Required ## redirect returns diff --git a/docs/router/framework/react/api/router/rootRouteWithContextFunction.md b/docs/router/framework/react/api/router/rootRouteWithContextFunction.md index 30cf6d69a6..d2db4f3712 100644 --- a/docs/router/framework/react/api/router/rootRouteWithContextFunction.md +++ b/docs/router/framework/react/api/router/rootRouteWithContextFunction.md @@ -5,7 +5,7 @@ title: rootRouteWithContext function > [!CAUTION] > This function is deprecated and will be removed in the next major version of TanStack Router. -> Please use the [`createRootRouteWithContext`](./createRootRouteWithContextFunction.md) function instead. +> Please use the [`createRootRouteWithContext`](../createRootRouteWithContextFunction.md) function instead. The `rootRouteWithContext` function is a helper function that can be used to create a root route instance that requires a context type to be fulfilled when the router is created. @@ -21,8 +21,8 @@ The `rootRouteWithContext` function accepts a single generic argument: ## rootRouteWithContext returns -- A factory function that can be used to create a new [`createRootRoute`](./createRootRouteFunction.md) instance. -- It accepts a single argument, the same as the [`createRootRoute`](./createRootRouteFunction.md) function. +- A factory function that can be used to create a new [`createRootRoute`](../createRootRouteFunction.md) instance. +- It accepts a single argument, the same as the [`createRootRoute`](../createRootRouteFunction.md) function. ## Examples diff --git a/docs/router/framework/react/api/router/useBlockerHook.md b/docs/router/framework/react/api/router/useBlockerHook.md index 98d4a06c3b..218efc6b4a 100644 --- a/docs/router/framework/react/api/router/useBlockerHook.md +++ b/docs/router/framework/react/api/router/useBlockerHook.md @@ -3,7 +3,7 @@ id: useBlockerHook title: useBlocker hook --- -The `useBlocker` method is a hook that [blocks navigation](../../guide/navigation-blocking.md) when a condition is met. +The `useBlocker` method is a hook that [blocks navigation](../../../guide/navigation-blocking.md) when a condition is met. > ⚠️ The following new `useBlocker` API is currently _experimental_. diff --git a/docs/router/framework/react/api/router/useCanGoBack.md b/docs/router/framework/react/api/router/useCanGoBack.md index 39b84fbdf9..cfae538055 100644 --- a/docs/router/framework/react/api/router/useCanGoBack.md +++ b/docs/router/framework/react/api/router/useCanGoBack.md @@ -14,7 +14,7 @@ The `useCanGoBack` hook returns a boolean representing if the router history can ## Limitations -The 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`. +The 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`. ## Examples diff --git a/docs/router/framework/react/api/router/useChildMatchesHook.md b/docs/router/framework/react/api/router/useChildMatchesHook.md index cf31d21016..ae4480d822 100644 --- a/docs/router/framework/react/api/router/useChildMatchesHook.md +++ b/docs/router/framework/react/api/router/useChildMatchesHook.md @@ -3,7 +3,7 @@ id: useChildMatchesHook title: useChildMatches hook --- -The `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.** +The `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.** > [!IMPORTANT] > If the router has pending matches and they are showing their pending component fallbacks, `router.state.pendingMatches` will used instead of `router.state.matches`. @@ -23,12 +23,12 @@ The `useChildMatches` hook accepts a single _optional_ argument, an `options` ob - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useChildMatches returns - If a `select` function is provided, the return value of the `select` function. -- If no `select` function is provided, an array of [`RouteMatch`](./RouteMatchType.md) objects. +- If no `select` function is provided, an array of [`RouteMatch`](../RouteMatchType.md) objects. ## Examples diff --git a/docs/router/framework/react/api/router/useLinkPropsHook.md b/docs/router/framework/react/api/router/useLinkPropsHook.md index 3c29881ba1..d3f70ba0c7 100644 --- a/docs/router/framework/react/api/router/useLinkPropsHook.md +++ b/docs/router/framework/react/api/router/useLinkPropsHook.md @@ -12,9 +12,9 @@ type UseLinkPropsOptions = ActiveLinkOptions & React.AnchorHTMLAttributes ``` -- [`ActiveLinkOptions`](./ActiveLinkOptionsType.md) -- The `useLinkProps` options are used to build a [`LinkProps`](./LinkPropsType.md) object. -- It also extends the `React.AnchorHTMLAttributes` type, so that any additional props that are passed to the `useLinkProps` hook will be merged with the [`LinkProps`](./LinkPropsType.md) object. +- [`ActiveLinkOptions`](../ActiveLinkOptionsType.md) +- The `useLinkProps` options are used to build a [`LinkProps`](../LinkPropsType.md) object. +- It also extends the `React.AnchorHTMLAttributes` type, so that any additional props that are passed to the `useLinkProps` hook will be merged with the [`LinkProps`](../LinkPropsType.md) object. ## useLinkProps returns diff --git a/docs/router/framework/react/api/router/useLoaderDataHook.md b/docs/router/framework/react/api/router/useLoaderDataHook.md index dfcf2d3758..809fd3785e 100644 --- a/docs/router/framework/react/api/router/useLoaderDataHook.md +++ b/docs/router/framework/react/api/router/useLoaderDataHook.md @@ -3,7 +3,7 @@ id: useLoaderDataHook title: useLoaderData hook --- -The `useLoaderData` hook returns the loader data from the closest [`RouteMatch`](./RouteMatchType.md) in the component tree. +The `useLoaderData` hook returns the loader data from the closest [`RouteMatch`](../RouteMatchType.md) in the component tree. ## useLoaderData options @@ -34,7 +34,7 @@ The `useLoaderData` hook accepts an `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useLoaderData returns diff --git a/docs/router/framework/react/api/router/useLoaderDepsHook.md b/docs/router/framework/react/api/router/useLoaderDepsHook.md index bf9a3a35ec..7e3d43e2ca 100644 --- a/docs/router/framework/react/api/router/useLoaderDepsHook.md +++ b/docs/router/framework/react/api/router/useLoaderDepsHook.md @@ -26,7 +26,7 @@ The `useLoaderDepsHook` hook accepts an `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useLoaderDeps returns diff --git a/docs/router/framework/react/api/router/useLocationHook.md b/docs/router/framework/react/api/router/useLocationHook.md index 7bbccad150..d568095852 100644 --- a/docs/router/framework/react/api/router/useLocationHook.md +++ b/docs/router/framework/react/api/router/useLocationHook.md @@ -3,7 +3,7 @@ id: useLocationHook title: useLocation hook --- -The `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. +The `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. ## useLocation options @@ -13,11 +13,11 @@ The `useLocation` hook accepts an optional `options` object. - Type: `(state: ParsedLocationType) => TSelected` - Optional -- If supplied, this function will be called with the [`location`](./ParsedLocationType.md) object and the return value will be returned from `useLocation`. +- If supplied, this function will be called with the [`location`](../ParsedLocationType.md) object and the return value will be returned from `useLocation`. ## useLocation returns -- The current [`location`](./ParsedLocationType.md) object or `TSelected` if a `select` function is provided. +- The current [`location`](../ParsedLocationType.md) object or `TSelected` if a `select` function is provided. ## Examples diff --git a/docs/router/framework/react/api/router/useMatchHook.md b/docs/router/framework/react/api/router/useMatchHook.md index 966fd1d7c1..5c43705b04 100644 --- a/docs/router/framework/react/api/router/useMatchHook.md +++ b/docs/router/framework/react/api/router/useMatchHook.md @@ -3,7 +3,7 @@ id: useMatchHook title: useMatch hook --- -The `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`. +The `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`. ## useMatch options @@ -15,7 +15,7 @@ The `useMatch` hook accepts a single argument, an `options` object. - The route id of a match - Optional, but recommended for full type safety. - If `opts.strict` is `true`, `from` is required and TypeScript will warn for this option if it is not provided. -- If `opts.strict` is `false`, `from` must not be set and TypeScript will provided loosened types for the returned [`RouteMatch`](./RouteMatchType.md). +- If `opts.strict` is `false`, `from` must not be set and TypeScript will provided loosened types for the returned [`RouteMatch`](../RouteMatchType.md). ### `opts.strict` option @@ -35,7 +35,7 @@ The `useMatch` hook accepts a single argument, an `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ### `opts.shouldThrow` option @@ -47,7 +47,7 @@ The `useMatch` hook accepts a single argument, an `options` object. ## useMatch returns - If a `select` function is provided, the return value of the `select` function. -- If no `select` function is provided, the [`RouteMatch`](./RouteMatchType.md) object or a loosened version of the `RouteMatch` object if `opts.strict` is `false`. +- If no `select` function is provided, the [`RouteMatch`](../RouteMatchType.md) object or a loosened version of the `RouteMatch` object if `opts.strict` is `false`. ## Examples diff --git a/docs/router/framework/react/api/router/useMatchRouteHook.md b/docs/router/framework/react/api/router/useMatchRouteHook.md index 01c26d07c6..ab597abef8 100644 --- a/docs/router/framework/react/api/router/useMatchRouteHook.md +++ b/docs/router/framework/react/api/router/useMatchRouteHook.md @@ -17,7 +17,7 @@ The `matchRoute` function is a function that can be used to match a route agains The `matchRoute` function accepts a single argument, an `options` object. -- Type: [`UseMatchRouteOptions`](./UseMatchRouteOptionsType.md) +- Type: [`UseMatchRouteOptions`](../UseMatchRouteOptionsType.md) ### matchRoute function returns diff --git a/docs/router/framework/react/api/router/useMatchesHook.md b/docs/router/framework/react/api/router/useMatchesHook.md index d7a1be8dc3..c8cc3ccaf8 100644 --- a/docs/router/framework/react/api/router/useMatchesHook.md +++ b/docs/router/framework/react/api/router/useMatchesHook.md @@ -3,10 +3,10 @@ id: useMatchesHook title: useMatches hook --- -The `useMatches` hook returns all of the [`RouteMatch`](./RouteMatchType.md) objects from the router **regardless of its callers position in the React component tree**. +The `useMatches` hook returns all of the [`RouteMatch`](../RouteMatchType.md) objects from the router **regardless of its callers position in the React component tree**. > [!TIP] -> 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. +> 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. ## useMatches options @@ -23,12 +23,12 @@ The `useMatches` hook accepts a single _optional_ argument, an `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useMatches returns - If a `select` function is provided, the return value of the `select` function. -- If no `select` function is provided, an array of [`RouteMatch`](./RouteMatchType.md) objects. +- If no `select` function is provided, an array of [`RouteMatch`](../RouteMatchType.md) objects. ## Examples diff --git a/docs/router/framework/react/api/router/useNavigateHook.md b/docs/router/framework/react/api/router/useNavigateHook.md index 916d2cd237..5c28804ce6 100644 --- a/docs/router/framework/react/api/router/useNavigateHook.md +++ b/docs/router/framework/react/api/router/useNavigateHook.md @@ -27,7 +27,7 @@ The `navigate` function is a function that can be used to navigate to a new loca The `navigate` function accepts a single argument, an `options` object. -- Type: [`NavigateOptions`](./NavigateOptionsType.md) +- Type: [`NavigateOptions`](../NavigateOptionsType.md) ### navigate function returns diff --git a/docs/router/framework/react/api/router/useParamsHook.md b/docs/router/framework/react/api/router/useParamsHook.md index 0f3ca88fca..b0624c7129 100644 --- a/docs/router/framework/react/api/router/useParamsHook.md +++ b/docs/router/framework/react/api/router/useParamsHook.md @@ -33,7 +33,7 @@ The `useParams` hook accepts an optional `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useParams returns diff --git a/docs/router/framework/react/api/router/useParentMatchesHook.md b/docs/router/framework/react/api/router/useParentMatchesHook.md index fe4068f397..c6c577fbe0 100644 --- a/docs/router/framework/react/api/router/useParentMatchesHook.md +++ b/docs/router/framework/react/api/router/useParentMatchesHook.md @@ -3,7 +3,7 @@ id: useParentMatchesHook title: useParentMatches hook --- -The `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.** +The `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.** > [!IMPORTANT] > If the router has pending matches and they are showing their pending component fallbacks, `router.state.pendingMatches` will used instead of `router.state.matches`. @@ -23,12 +23,12 @@ The `useParentMatches` hook accepts an optional `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useParentMatches returns - If a `select` function is provided, the return value of the `select` function. -- If no `select` function is provided, an array of [`RouteMatch`](./RouteMatchType.md) objects. +- If no `select` function is provided, an array of [`RouteMatch`](../RouteMatchType.md) objects. ## Examples diff --git a/docs/router/framework/react/api/router/useRouterHook.md b/docs/router/framework/react/api/router/useRouterHook.md index 174829c256..14d29a4955 100644 --- a/docs/router/framework/react/api/router/useRouterHook.md +++ b/docs/router/framework/react/api/router/useRouterHook.md @@ -3,13 +3,13 @@ id: useRouterHook title: useRouter hook --- -The `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. +The `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. ## useRouter returns -- The current [`Router`](./RouterType.md) instance. +- The current [`Router`](../RouterType.md) instance. -> ⚠️⚠️⚠️ **`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.** +> ⚠️⚠️⚠️ **`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.** ## Examples diff --git a/docs/router/framework/react/api/router/useRouterStateHook.md b/docs/router/framework/react/api/router/useRouterStateHook.md index bcded1d415..f83527cc50 100644 --- a/docs/router/framework/react/api/router/useRouterStateHook.md +++ b/docs/router/framework/react/api/router/useRouterStateHook.md @@ -6,7 +6,7 @@ title: useRouterState hook The `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. > [!TIP] -> 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. +> 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. ## useRouterState options @@ -16,18 +16,18 @@ The `useRouterState` hook accepts an optional `options` object. - Type: `(state: RouterState) => TSelected` - Optional -- If supplied, this function will be called with the [`RouterState`](./RouterStateType.md) object and the return value will be returned from `useRouterState`. +- If supplied, this function will be called with the [`RouterState`](../RouterStateType.md) object and the return value will be returned from `useRouterState`. ### `opts.structuralSharing` option - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ## useRouterState returns -- The current [`RouterState`](./RouterStateType.md) object or `TSelected` if a `select` function is provided. +- The current [`RouterState`](../RouterStateType.md) object or `TSelected` if a `select` function is provided. ## Examples diff --git a/docs/router/framework/react/api/router/useSearchHook.md b/docs/router/framework/react/api/router/useSearchHook.md index 39c8ed5d9e..985b42b206 100644 --- a/docs/router/framework/react/api/router/useSearchHook.md +++ b/docs/router/framework/react/api/router/useSearchHook.md @@ -33,7 +33,7 @@ The `useSearch` hook accepts an `options` object. - Type: `boolean` - Optional - Configures whether structural sharing is enabled for the value returned by `select`. -- See the [Render Optimizations guide](../../guide/render-optimizations.md) for more information. +- See the [Render Optimizations guide](../../../guide/render-optimizations.md) for more information. ### `opts.strict` option diff --git a/docs/router/framework/react/comparison.md b/docs/router/framework/react/comparison.md index 4ef92b2667..62e10a054e 100644 --- a/docs/router/framework/react/comparison.md +++ b/docs/router/framework/react/comparison.md @@ -10,8 +10,8 @@ Before you commit to a new tool, it's always nice to know how it stacks up again Feature/Capability Key: - ✅ 1st-class, built-in, and ready to use with no added configuration or code -- 🔵 Supported via addon package - 🟡 Partial Support (on a scale of 5) +- 🟠 Supported via addon/community package - 🔶 Possible, but requires custom code/implementation/casting - 🛑 Not officially supported @@ -39,9 +39,9 @@ Feature/Capability Key: | Ranked Routes | ✅ | ✅ | ✅ | | Active Link Customization | ✅ | ✅ | ✅ | | Optimistic UI | ✅ | ✅ | 🔶 | -| Typesafe Absolute + Relative Navigation | ✅ | 🛑 | 🛑 | +| Typesafe Absolute + Relative Navigation | ✅ | 🟡 (1/5 via `buildHref` util) | 🟠 (IDE plugin) | | Route Mount/Transition/Unmount Events | ✅ | 🛑 | 🛑 | -| Devtools | ✅ | 🛑 | 🛑 | +| Devtools | ✅ | 🟠 | 🛑 | | Basic Search Params | ✅ | ✅ | ✅ | | Search Param Hooks | ✅ | ✅ | ✅ | | ``/`useNavigate` Search Param API | ✅ | 🟡 (search-string only via the `to`/`search` options) | 🟡 (search-string only via the `to`/`search` options) | @@ -54,7 +54,7 @@ Feature/Capability Key: | Suspense Route Elements | ✅ | ✅ | ✅ | | Route Error Elements | ✅ | ✅ | ✅ | | Route Pending Elements | ✅ | ✅ | ✅ | -| ``/`useBlocker` | ✅ | 🔶 | ❓ | +| ``/`useBlocker` | ✅ | 🔶 (no hard reloads or cross-origin navigation) | 🛑 | | Deferred Primitives | ✅ | ✅ | ✅ | | Navigation Scroll Restoration | ✅ | ✅ | ❓ | | ElementScroll Restoration | ✅ | 🛑 | 🛑 | @@ -72,7 +72,7 @@ Feature/Capability Key: | React Server Function Middleware | ✅ | 🛑 | 🛑 | | API Routes | ✅ | ✅ | ✅ | | API Middleware | ✅ | 🛑 | ✅ | -| React Server Components | 🛑 | 🛑 | ✅ | +| React Server Components | 🛑 | 🟡 (Experimental) | ✅ | | `
` API | 🛑 | ✅ | ✅ | [bp-tanstack-router]: https://badgen.net/bundlephobia/minzip/@tanstack/react-router diff --git a/docs/router/framework/react/decisions-on-dx.md b/docs/router/framework/react/decisions-on-dx.md index 405db4c6eb..f2a4af5ed8 100644 --- a/docs/router/framework/react/decisions-on-dx.md +++ b/docs/router/framework/react/decisions-on-dx.md @@ -90,7 +90,7 @@ This only gets worse as you begin to use more features of the router, such as ne What we found to be the best way to define your routes is to abstract the definition of the route configuration outside of the route-tree. Then stitch together your route configurations into a single cohesive route-tree that is then passed into the `createRouter` function. -You can read more about [code-based routing](./routing/code-based-routing.md) to see how to define your routes in this way. +You can read more about [code-based routing](../routing/code-based-routing.md) to see how to define your routes in this way. > [!TIP] > Finding Code-based routing to be a bit too cumbersome? See why [file-based routing](#3-why-is-file-based-routing-the-preferred-way-to-define-routes) is the preferred way to define your routes. @@ -160,7 +160,7 @@ We went with **module declaration**, as it is what we found to be the most scala Something you'll notice (quite soon) in the TanStack Router documentation is that we push for **file-based routing** as the preferred method for defining your routes. This is because we've found that file-based routing is the most scalable and maintainable way to define your routes. > [!TIP] -> 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). +> 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). As mentioned in the beginning, TanStack Router was designed for complex applications that require a high degree of type-safety and maintainability. And to achieve this, the configuration of the router has been done in a precise way that allows TypeScript to infer the types of your routes as much as possible. @@ -234,4 +234,4 @@ That's it! No need to worry about defining the `getParentRoute` function, stitch At no point does the TanStack Router Bundler Plugin take away your control over your route configurations. It's designed to be as flexible as possible, allowing you to define your routes in a way that suits your application whilst reducing the boilerplate and complexity of the route configuration. -Check out the guides for [file-based routing](./routing/file-based-routing.md) and [code-splitting](./routing/code-splitting.md) for a more in-depth explanation of how they work in TanStack Router. +Check out the guides for [file-based routing](../routing/file-based-routing.md) and [code-splitting](../guide/code-splitting.md) for a more in-depth explanation of how they work in TanStack Router. diff --git a/docs/router/framework/react/faq.md b/docs/router/framework/react/faq.md index 01169d3b13..09fccf1dd7 100644 --- a/docs/router/framework/react/faq.md +++ b/docs/router/framework/react/faq.md @@ -14,7 +14,7 @@ You should commit this file into git so that other developers can use it to buil No, the root route is always rendered as it is the entry point of your application. -If you need to conditionally render a route's component, this usually means that the page content needs to be different based on some condition (e.g. user authentication). For this use case, you should use a [Layout Route](./routing/routing-concepts.md#layout-routes) or a [Pathless Layout Route](./routing/routing-concepts.md#pathless-layout-routes) to conditionally render the content. +If you need to conditionally render a route's component, this usually means that the page content needs to be different based on some condition (e.g. user authentication). For this use case, you should use a [Layout Route](../routing/routing-concepts.md#layout-routes) or a [Pathless Layout Route](../routing/routing-concepts.md#pathless-layout-routes) to conditionally render the content. You can restrict access to these routes using a conditional check in the `beforeLoad` function of the route. diff --git a/docs/router/framework/react/guide/authenticated-routes.md b/docs/router/framework/react/guide/authenticated-routes.md index 007ec5cfe8..f80a18377f 100644 --- a/docs/router/framework/react/guide/authenticated-routes.md +++ b/docs/router/framework/react/guide/authenticated-routes.md @@ -83,9 +83,9 @@ If your authentication flow relies on interactions with React context and/or hoo > [!IMPORTANT] > 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 `` and then pass the returned value down to TanStack Router. -We'll cover the `router.context` options in-detail in the [Router Context](./router-context.md) section. +We'll cover the `router.context` options in-detail in the [Router Context](../router-context.md) section. -Here's an example that uses React context and hooks for protecting authenticated routes in TanStack Router. See the entire working setup in the [Authenticated Routes example](../examples/authenticated-routes). +Here's an example that uses React context and hooks for protecting authenticated routes in TanStack Router. See the entire working setup in the [Authenticated Routes example](https://github.com/TanStack/router/tree/main/examples/react/authenticated-routes). - `src/routes/__root.tsx` diff --git a/docs/router/framework/react/guide/code-splitting.md b/docs/router/framework/react/guide/code-splitting.md index e2646e402c..fd47e8b277 100644 --- a/docs/router/framework/react/guide/code-splitting.md +++ b/docs/router/framework/react/guide/code-splitting.md @@ -72,7 +72,7 @@ This is the easiest and most powerful way to code split your route files. When using the `autoCodeSplitting` feature, TanStack Router will automatically code split your route files based on the non-critical route configuration mentioned above. > [!IMPORTANT] -> 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). +> 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). > This will **NOT** work if you are **only** using the CLI (`@tanstack/router-cli`). To enable automatic code-splitting, you just need to add the following to the configuration of your TanStack Router Bundler Plugin: @@ -96,7 +96,7 @@ export default defineConfig({ That's it! TanStack Router will automatically code-split all your route files by their critical and non-critical route configurations. -If 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. +If 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. ## Using the `.lazy.tsx` suffix @@ -217,7 +217,7 @@ If you are using code-based routing, you can still code-split your routes using Create a lazy route using the `createLazyRoute` function. ```tsx -// src/posts.tsx +// src/posts.lazy.tsx export const Route = createLazyRoute('/posts')({ component: MyComponent, }) diff --git a/docs/router/framework/react/guide/creating-a-router.md b/docs/router/framework/react/guide/creating-a-router.md index d84815bd7c..d18d6329ca 100644 --- a/docs/router/framework/react/guide/creating-a-router.md +++ b/docs/router/framework/react/guide/creating-a-router.md @@ -18,7 +18,7 @@ const router = createRouter({ You'll probably notice quickly that the `Router` constructor requires a `routeTree` option. This is the route tree that the router will use to match routes and render components. -Whether 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: +Whether 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: ### Filesystem Route Tree @@ -73,4 +73,4 @@ export const Route = createRootRoute({ ## Other Options -There 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). +There 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). diff --git a/docs/router/framework/react/guide/custom-link.md b/docs/router/framework/react/guide/custom-link.md index 6131527d59..133a1b7e13 100644 --- a/docs/router/framework/react/guide/custom-link.md +++ b/docs/router/framework/react/guide/custom-link.md @@ -2,7 +2,7 @@ title: Custom Link --- -While 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. +While 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. ## `createLink` for cross-cutting concerns @@ -53,9 +53,9 @@ Here are some examples of how you can use `createLink` with third-party librarie ### React Aria Components example -React Aria Components’ +React Aria Components' [Link](https://react-spectrum.adobe.com/react-aria/Link.html) component does not support the standard `onMouseEnter` and `onMouseLeave` events. -Therefore, you cannot use it directly with TanStack Router’s `preload (intent)` prop. +Therefore, you cannot use it directly with TanStack Router's `preload (intent)` prop. Explanation for this can be found here: @@ -146,7 +146,7 @@ export const CustomLink: LinkComponent = ( ### MUI example -There is an [example](../examples/start-material-ui) available which uses these patterns. +There is an [example](https://github.com/TanStack/router/tree/main/examples/react/start-material-ui) available which uses these patterns. #### `Link` diff --git a/docs/router/framework/react/guide/custom-search-param-serialization.md b/docs/router/framework/react/guide/custom-search-param-serialization.md index 33124513e8..11f3329edc 100644 --- a/docs/router/framework/react/guide/custom-search-param-serialization.md +++ b/docs/router/framework/react/guide/custom-search-param-serialization.md @@ -38,7 +38,7 @@ const router = createRouter({ However, this default behavior may not be suitable for all use cases. For example, you may want to use a different serialization format, such as base64 encoding, or you may want to use a purpose-built serialization/deserialization library, like [query-string](https://github.com/sindresorhus/query-string), [JSURL2](https://github.com/wmertens/jsurl2), or [Zipson](https://jgranstrom.github.io/zipson/). -This can be achieved by providing your own serialization and deserialization functions to the `parseSearch` and `stringifySearch` options in the [`Router`](../api/router/RouterOptionsType.md#stringifysearch-method) configuration. When doing this, you can utilize TanStack Router's built-in helper functions, `parseSearchWith` and `stringifySearchWith`, to simplify the process. +This can be achieved by providing your own serialization and deserialization functions to the `parseSearch` and `stringifySearch` options in the [`Router`](../../api/router/RouterOptionsType.md#stringifysearch-method) configuration. When doing this, you can utilize TanStack Router's built-in helper functions, `parseSearchWith` and `stringifySearchWith`, to simplify the process. > [!TIP] > 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. diff --git a/docs/router/framework/react/guide/data-loading.md b/docs/router/framework/react/guide/data-loading.md index 02fc39d670..cd02b10a20 100644 --- a/docs/router/framework/react/guide/data-loading.md +++ b/docs/router/framework/react/guide/data-loading.md @@ -49,7 +49,7 @@ TanStack Router Cache Cons: - 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) > [!TIP] -> 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. +> 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. ## Using the Router Cache @@ -219,7 +219,7 @@ To opt out of preloading, don't turn it on via the `routerOptions.defaultPreload ## Passing all loader events to an external cache -We 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`: +We 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`: ```tsx const router = createRouter({ @@ -401,7 +401,7 @@ export const Route = createFileRoute('/posts')({ Ideally 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: -- 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). +- 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). - Show a pending component after an optimistic suspense threshold until all of the data is ready (See below). ## Showing a pending component diff --git a/docs/router/framework/react/guide/data-mutations.md b/docs/router/framework/react/guide/data-mutations.md index 64fba2a4cd..53899f51d4 100644 --- a/docs/router/framework/react/guide/data-mutations.md +++ b/docs/router/framework/react/guide/data-mutations.md @@ -87,9 +87,9 @@ Without notifying your mutation management library about the route change, it's Hopefully 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: ```tsx -const routeApi = getRouteApi('/posts/$postId/edit') +const routeApi = getRouteApi('/room/$roomId/chat') -function EditPost() { +function ChatRoom() { const { roomId } = routeApi.useParams() const sendMessageMutation = useCoolMutation({ diff --git a/docs/router/framework/react/guide/deferred-data-loading.md b/docs/router/framework/react/guide/deferred-data-loading.md index cfff886fb4..151be5537c 100644 --- a/docs/router/framework/react/guide/deferred-data-loading.md +++ b/docs/router/framework/react/guide/deferred-data-loading.md @@ -62,7 +62,7 @@ function PostIdComponent() { ``` > [!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 `useLoaderData()` hook. +> 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. The `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. @@ -77,7 +77,7 @@ If the promise is rejected, the `Await` component will throw the serialized erro ## Deferred Data Loading with External libraries -When your strategy for fetching information for the route relies on [External Data Loading](./external-data-loading.md) with an external library like [TanStack Query](https://tanstack.com/query), deferred data loading works a bit differently, as the library handles the data fetching and caching for you outside of TanStack Router. +When your strategy for fetching information for the route relies on [External Data Loading](../external-data-loading.md) with an external library like [TanStack Query](https://tanstack.com/query), deferred data loading works a bit differently, as the library handles the data fetching and caching for you outside of TanStack Router. So, instead of using `defer` and `Await`, you'll instead want to use the Route's `loader` to kick off the data fetching and then use the library's hooks to access the data in your components. @@ -139,7 +139,7 @@ Streamed promises follow the same lifecycle as the loader data they are associat **Streaming requires a server that supports it and for TanStack Router to be configured to use it properly.** -Please read the entire [Streaming SSR Guide](./ssr.md#streaming-ssr) for step by step instructions on how to set up your server for streaming. +Please read the entire [Streaming SSR Guide](../ssr.md#streaming-ssr) for step by step instructions on how to set up your server for streaming. ## SSR Streaming Lifecycle diff --git a/docs/router/framework/react/guide/external-data-loading.md b/docs/router/framework/react/guide/external-data-loading.md index e2d0e1eb53..4e573de640 100644 --- a/docs/router/framework/react/guide/external-data-loading.md +++ b/docs/router/framework/react/guide/external-data-loading.md @@ -4,7 +4,7 @@ title: External Data Loading --- > [!IMPORTANT] -> 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. +> 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. ## To **Store** or to **Coordinate**? diff --git a/docs/router/framework/react/guide/history-types.md b/docs/router/framework/react/guide/history-types.md index c65afccd27..5559788d80 100644 --- a/docs/router/framework/react/guide/history-types.md +++ b/docs/router/framework/react/guide/history-types.md @@ -52,4 +52,4 @@ const memoryHistory = createMemoryHistory({ const router = createRouter({ routeTree, history: memoryHistory }) ``` -Refer to the [SSR Guide](./ssr.md#server-history) for usage on the server for server-side rendering. +Refer to the [SSR Guide](../ssr.md#server-history) for usage on the server for server-side rendering. diff --git a/docs/router/framework/react/guide/navigation-blocking.md b/docs/router/framework/react/guide/navigation-blocking.md index 575c4d1442..f95b5f5f94 100644 --- a/docs/router/framework/react/guide/navigation-blocking.md +++ b/docs/router/framework/react/guide/navigation-blocking.md @@ -79,7 +79,7 @@ function MyComponent() { } ``` -You can find more information about the `useBlocker` hook in the [API reference](../api/router/useBlockerHook.md). +You can find more information about the `useBlocker` hook in the [API reference](../../api/router/useBlockerHook.md). ## Component-based blocking diff --git a/docs/router/framework/react/guide/not-found-errors.md b/docs/router/framework/react/guide/not-found-errors.md index 08dec747b0..acf45085b6 100644 --- a/docs/router/framework/react/guide/not-found-errors.md +++ b/docs/router/framework/react/guide/not-found-errors.md @@ -16,7 +16,7 @@ There are 2 uses for not-found errors in TanStack Router: - Attempting to access `/posts/1/edit` when the route tree only handles `/posts/$postId` - **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 - **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. - - Will be handled by the nearest parent route with a `notFoundComponent` or the root route + - Will be handled by the nearest parent route with a `notFoundComponent` (when `notFound` is called within `loader`) or the root route. - Examples: - Attempting to access `/posts/1` when the post with ID 1 does not exist - Attempting to access `/docs/path/to/document` when the document does not exist @@ -256,7 +256,7 @@ export const Route = createFileRoute('/posts/$postId')({ ## Usage With SSR -See [SSR guide](./ssr.md) for more information. +See [SSR guide](../ssr.md) for more information. ## Migrating from `NotFoundRoute` diff --git a/docs/router/framework/react/guide/path-params.md b/docs/router/framework/react/guide/path-params.md index ba2d77df03..1b1399af65 100644 --- a/docs/router/framework/react/guide/path-params.md +++ b/docs/router/framework/react/guide/path-params.md @@ -68,7 +68,7 @@ function PostComponent() { } ``` -> 🧠 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. +> 🧠 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. ## Path Params outside of Routes @@ -113,7 +113,7 @@ Notice that the function style is useful when you need to persist params that ar ## Allowed Characters -By 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) +By 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) Example usage: diff --git a/docs/router/framework/react/guide/preloading.md b/docs/router/framework/react/guide/preloading.md index d72ddc5e59..5695245c75 100644 --- a/docs/router/framework/react/guide/preloading.md +++ b/docs/router/framework/react/guide/preloading.md @@ -21,7 +21,7 @@ Preloading in TanStack Router is a way to load a route before the user actually Preloaded route matches are temporarily cached in memory with a few important caveats: - **Unused preloaded data is removed after 30 seconds by default.** This can be configured by setting the `defaultPreloadMaxAge` option on your router. -- **Obviously, when a a route is loaded, its preloaded version is promoted to the router's normal pending matches state.** +- **Obviously, when a route is loaded, its preloaded version is promoted to the router's normal pending matches state.** If you need more control over preloading, caching and/or garbage collection of preloaded data, you should use an external caching library like [TanStack Query](https://tanstack.com/query). diff --git a/docs/router/framework/react/guide/route-masking.md b/docs/router/framework/react/guide/route-masking.md index b31647db81..d46dbc7f33 100644 --- a/docs/router/framework/react/guide/route-masking.md +++ b/docs/router/framework/react/guide/route-masking.md @@ -9,7 +9,7 @@ Route masking is a way to mask the actual URL of a route that gets persisted to - Navigating to a route with the search param `?showLogin=true`, but masking the URL to _not_ contain the search param - Navigating to a route with the search param `?modal=settings`, but masking the URL as `/settings' -Each of these scenarios can be achieved with route masking and even extended to support more advanced patterns like [parallel routes](./parallel-routes.md). +Each of these scenarios can be achieved with route masking and even extended to support more advanced patterns like [parallel routes](../parallel-routes.md). ## How does route masking work? diff --git a/docs/router/framework/react/guide/scroll-restoration.md b/docs/router/framework/react/guide/scroll-restoration.md index 77634d03e9..ffc1387e10 100644 --- a/docs/router/framework/react/guide/scroll-restoration.md +++ b/docs/router/framework/react/guide/scroll-restoration.md @@ -17,6 +17,19 @@ const router = createRouter({ }) ``` +For complex selectors that cannot be simply resolved using `document.querySelector(selector)`, you can pass functions that return HTML elements to `routerOptions.scrollToTopSelectors`: + +```tsx +const selector = () => + document + .querySelector('#shadowRootParent') + ?.shadowRoot?.querySelector('#main-scrollable-area') + +const router = createRouter({ + scrollToTopSelectors: [selector], +}) +``` + These selectors are handled **in addition to `window`** which cannot be disabled currently. ## Scroll Restoration @@ -177,12 +190,12 @@ function Component() { ## Scroll Behavior -To control the scroll behavior when navigating between pages, you can use the `scrollBehavior` option. This allows you to make the transition between pages instant instead of a smooth scroll. The global configuration of scroll restoration behavior has the same options as those supported by the browser, which are `smooth`, `instant`, and `auto` (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#behavior) for more information). +To control the scroll behavior when navigating between pages, you can use the `scrollRestorationBehavior` option. This allows you to make the transition between pages instant instead of a smooth scroll. The global configuration of scroll restoration behavior has the same options as those supported by the browser, which are `smooth`, `instant`, and `auto` (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#behavior) for more information). ```tsx import { createRouter } from '@tanstack/react-router' const router = createRouter({ - scrollBehavior: 'instant', + scrollRestorationBehavior: 'instant', }) ``` diff --git a/docs/router/framework/react/guide/search-params.md b/docs/router/framework/react/guide/search-params.md index 786ea2cd67..dec1850671 100644 --- a/docs/router/framework/react/guide/search-params.md +++ b/docs/router/framework/react/guide/search-params.md @@ -363,7 +363,7 @@ Once your search params have been validated and typed, you're finally ready to s ### Using Search Params in Loaders -Please 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. +Please 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. ### Search Params are inherited from Parent Routes @@ -415,7 +415,7 @@ const ProductList = () => { ``` > [!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 `useSearch()` hook. +> 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. ### Search Params outside of Route Components @@ -587,15 +587,15 @@ export const Route = createRootRoute({ validateSearch: zodValidator(searchSchema), search: { middlewares: [ - ({search, next}) => { + ({ search, next }) => { const result = next(search) return { - rootValue: search.rootValue - ...result + rootValue: search.rootValue, + ...result, } - } - ] - } + }, + ], + }, }) ``` diff --git a/docs/router/framework/react/guide/ssr.md b/docs/router/framework/react/guide/ssr.md index 9197f03a07..d4f4f6c5da 100644 --- a/docs/router/framework/react/guide/ssr.md +++ b/docs/router/framework/react/guide/ssr.md @@ -112,7 +112,7 @@ Resolved loader data fetched by routes is automatically dehydrated and rehydrate ⚠️ 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. -For more information on how to utilize data loading and data streaming, see the [Data Loading](./data-loading.md) and [Data Streaming](../data-streaming) guides. +For more information on how to utilize data loading, see the [Data Loading](../data-loading.md) guide. ### Rendering the Application on the Server @@ -220,7 +220,7 @@ If you are using more complex data types like `Map`, `Set`, `BigInt`, etc, you m The Data Serialization API allows the usage of a custom serializer that can allow us to transparently use these data types when communicating across the network. - + ```tsx import { SuperJSON } from 'superjson' diff --git a/docs/router/framework/react/guide/tanstack-start.md b/docs/router/framework/react/guide/tanstack-start.md index 9704d721c4..0dfd75acc7 100644 --- a/docs/router/framework/react/guide/tanstack-start.md +++ b/docs/router/framework/react/guide/tanstack-start.md @@ -124,7 +124,7 @@ Once configuration is done, we'll have a file tree that looks like the following ## The Router Configuration This is the file that will dictate the behavior of TanStack Router used within Start. Here, you can configure everything -from the default [preloading functionality](./preloading.md) to [caching staleness](./data-loading.md). +from the default [preloading functionality](../preloading.md) to [caching staleness](../data-loading.md). ```tsx // app/router.tsx diff --git a/docs/router/framework/react/guide/type-safety.md b/docs/router/framework/react/guide/type-safety.md index f6aee18a5f..f194d8779a 100644 --- a/docs/router/framework/react/guide/type-safety.md +++ b/docs/router/framework/react/guide/type-safety.md @@ -65,7 +65,7 @@ function PostsComponent() { Every 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. -> 🧠 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. +> 🧠 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. ### What if I don't know the route? What if it's a shared component? diff --git a/docs/router/framework/react/installation.md b/docs/router/framework/react/installation.md index 2ece19f70e..909b5aa0ad 100644 --- a/docs/router/framework/react/installation.md +++ b/docs/router/framework/react/installation.md @@ -33,3 +33,19 @@ TypeScript is _optional_, but **HIGHLY** recommended! If you are using it, pleas > [!IMPORTANT] > We aim to support the last five minor versions of TypeScript. If you are using an older version, you may run into issues. Please upgrade to the latest version of TypeScript to ensure compatibility. We may drop support for older versions of TypeScript, outside of the range mentioned above, without warning in a minor or patch release. + +### Vibe Coding Support + +All of our documentation for TanStack React Router is integrated into the NPM module and can be easily installed as Vibe coding rules. You can integrate Vibe coding rules into the editor of your choice using [vibe-rules](https://www.npmjs.com/package/vibe-rules). + +```bash +pnpm add -g vibe-rules +``` + +Then run `vibe-rules` with the editor of your choice. Here is an example for Cursor: + +```bash +vibe-rules install cursor +``` + +But you can also use `windsurf`, `claude-code`, etc. Check the [vibe-rules](https://www.npmjs.com/package/vibe-rules) documentation for more information. diff --git a/docs/router/framework/react/migrate-from-react-location.md b/docs/router/framework/react/migrate-from-react-location.md index 100d614cae..25dc9226d3 100644 --- a/docs/router/framework/react/migrate-from-react-location.md +++ b/docs/router/framework/react/migrate-from-react-location.md @@ -2,23 +2,23 @@ title: Migration from React Location --- -Before you begin your journey in migrating from React Location, it's important that you have a good understanding of the [Routing Concepts](./routing/routing-concepts.md) and [Design Decisions](./decisions-on-dx.md) used by TanStack Router. +Before you begin your journey in migrating from React Location, it's important that you have a good understanding of the [Routing Concepts](../routing/routing-concepts.md) and [Design Decisions](../decisions-on-dx.md) used by TanStack Router. ## Differences between React Location and TanStack Router React Location and TanStack Router share much of same design decisions concepts, but there are some key differences that you should be aware of. - React Location uses _generics_ to infer types for routes, while TanStack Router uses _module declaration merging_ to infer types. -- Route configuration in React Location is done using a single array of route definitions, while in TanStack Router, route configuration is done using a tree of route definitions starting with the [root route](./routing/routing-concepts.md#the-root-route). -- [File-based routing](./routing/file-based-routing.md) is the recommended way to define routes in TanStack Router, while React Location only allows you to define routes in a single file using a code-based approach. - - TanStack Router does support a [code-based approach](./routing/code-based-routing.md) to defining routes, but it is not recommended for most use cases. You can read more about why, over here: [why is file-based routing the preferred way to define routes?](./decisions-on-dx.md#3-why-is-file-based-routing-the-preferred-way-to-define-routes) +- Route configuration in React Location is done using a single array of route definitions, while in TanStack Router, route configuration is done using a tree of route definitions starting with the [root route](../routing/routing-concepts.md#the-root-route). +- [File-based routing](../routing/file-based-routing.md) is the recommended way to define routes in TanStack Router, while React Location only allows you to define routes in a single file using a code-based approach. + - TanStack Router does support a [code-based approach](../routing/code-based-routing.md) to defining routes, but it is not recommended for most use cases. You can read more about why, over here: [why is file-based routing the preferred way to define routes?](../decisions-on-dx.md#3-why-is-file-based-routing-the-preferred-way-to-define-routes) ## Migration guide In this guide we'll go over the process of migrating the [React Location Basic example](https://github.com/TanStack/router/tree/react-location/examples/basic) over to TanStack Router using file-based routing, with the end goal of having the same functionality as the original example (styling and other non-routing related code will be omitted). > [!TIP] -> To use a code-based approach for defining your routes, you can read the [code-based Routing](./routing/code-based-routing.md) guide. +> To use a code-based approach for defining your routes, you can read the [code-based Routing](../routing/code-based-routing.md) guide. ### Step 1: Swap over to TanStack Router's dependencies @@ -57,7 +57,7 @@ export default defineConfig({ }) ``` -However, if your application does not use Vite, you use one of our other [supported bundlers](./routing/file-based-routing.md#getting-started-with-file-based-routing), or you can use the `@tanstack/router-cli` package to watch for changes in your routes files and automatically update the routes configuration. +However, if your application does not use Vite, you use one of our other [supported bundlers](../routing/file-based-routing.md#getting-started-with-file-based-routing), or you can use the `@tanstack/router-cli` package to watch for changes in your routes files and automatically update the routes configuration. ### Step 3: Add the file-based configuration file to your project @@ -70,7 +70,7 @@ Create a `tsr.config.json` file in the root of your project with the following c } ``` -You can find the full list of options for the `tsr.config.json` file [here](./routing/file-based-routing.md#options). +You can find the full list of options for the `tsr.config.json` file [here](../routing/file-based-routing.md#options). ### Step 4: Create the routes directory @@ -249,19 +249,19 @@ You should now have successfully migrated your application from React Location t React Location also has a few more features that you might be using in your application. Here are some guides to help you migrate those features: -- [Search params](./guide/search-params.md) -- [Data loading](./guide/data-loading.md) -- [History types](./guide/history-types.md) -- [Wildcard / Splat / Catch-all routes](./routing/routing-concepts.md#splat--catch-all-routes) -- [Authenticated routes](./guide/authenticated-routes.md) +- [Search params](../guide/search-params.md) +- [Data loading](../guide/data-loading.md) +- [History types](../guide/history-types.md) +- [Wildcard / Splat / Catch-all routes](../routing/routing-concepts.md#splat--catch-all-routes) +- [Authenticated routes](../guide/authenticated-routes.md) TanStack Router also has a few more features that you might want to explore: -- [Router Context](./guide/router-context.md) -- [Preloading](./guide/preloading.md) -- [Pathless Layout Routes](./routing/routing-concepts.md#pathless-layout-routes) -- [Route masking](./guide/route-masking.md) -- [SSR](./guide/ssr.md) +- [Router Context](../guide/router-context.md) +- [Preloading](../guide/preloading.md) +- [Pathless Layout Routes](../routing/routing-concepts.md#pathless-layout-routes) +- [Route masking](../guide/route-masking.md) +- [SSR](../guide/ssr.md) - ... and more! If you are facing any issues or have any questions, feel free to ask for help in the TanStack Discord. diff --git a/docs/router/framework/react/overview.md b/docs/router/framework/react/overview.md index be83513a79..c2e171feda 100644 --- a/docs/router/framework/react/overview.md +++ b/docs/router/framework/react/overview.md @@ -44,7 +44,7 @@ It's probably no surprise at this point that picking a router is so important th TanStack 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](https://tanstack.com/start), a full-stack framework that is built on top of TanStack Router and tools like Nitro, and Vite. -For 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). +For 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). ## Why TanStack Router? diff --git a/docs/router/framework/react/quick-start.md b/docs/router/framework/react/quick-start.md index b9264f8742..461d5e6834 100644 --- a/docs/router/framework/react/quick-start.md +++ b/docs/router/framework/react/quick-start.md @@ -58,7 +58,7 @@ export default defineConfig({ ``` > [!TIP] -> If you are not using Vite, or any of the supported bundlers, you can check out the [TanStack Router CLI](./routing/installation-with-router-cli.md) guide for more info. +> If you are not using Vite, or any of the supported bundlers, you can check out the [TanStack Router CLI](../routing/installation-with-router-cli.md) guide for more info. Create the following files: diff --git a/docs/router/framework/react/routing/code-based-routing.md b/docs/router/framework/react/routing/code-based-routing.md index 3adbf14ca2..c4562541fd 100644 --- a/docs/router/framework/react/routing/code-based-routing.md +++ b/docs/router/framework/react/routing/code-based-routing.md @@ -3,18 +3,18 @@ title: Code-Based Routing --- > [!TIP] -> Code-based routing is not recommended for most applications. It is recommended to use [File-Based Routing](./file-based-routing.md) instead. +> Code-based routing is not recommended for most applications. It is recommended to use [File-Based Routing](../file-based-routing.md) instead. ## ⚠️ Before You Start -- If you're using [File-Based Routing](./file-based-routing.md), **skip this guide**. -- 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. +- If you're using [File-Based Routing](../file-based-routing.md), **skip this guide**. +- 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. ## Route Trees Code-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. -Let's consider the same route tree from the [Route Trees & Nesting](./route-trees.md#route-trees) guide, and convert it to code-based routing: +Let's consider the same route tree from the [Route Trees & Nesting](../route-trees.md#route-trees) guide, and convert it to code-based routing: Here is the file-based version: @@ -179,7 +179,7 @@ But before you can go ahead and build the route tree, you need to understand how Believe 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. -We're going to assume you've read the [Routing Concepts](./routing-concepts.md) guide and are familiar with each of these main concepts: +We're going to assume you've read the [Routing Concepts](../routing-concepts.md) guide and are familiar with each of these main concepts: - The Root Route - Basic Routes @@ -214,7 +214,7 @@ export interface MyRouterContext { const rootRoute = createRootRouteWithContext() ``` -To learn more about Context in TanStack Router, see the [Router Context](../guide/router-context.md) guide. +To learn more about Context in TanStack Router, see the [Router Context](../../guide/router-context.md) guide. ## Basic Routes @@ -269,7 +269,7 @@ function PostComponent() { ``` > [!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 `postIdRoute` configuration to get access to the typed `useParams()` hook. +> 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. ## Splat / Catch-All Routes diff --git a/docs/router/framework/react/routing/file-based-routing.md b/docs/router/framework/react/routing/file-based-routing.md index 03e9942d7e..99d22d1cfb 100644 --- a/docs/router/framework/react/routing/file-based-routing.md +++ b/docs/router/framework/react/routing/file-based-routing.md @@ -2,7 +2,7 @@ title: File-Based Routing --- -Most 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. +Most 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. ## What is File-Based Routing? @@ -47,6 +47,9 @@ See the example below: | ┄ ʦ `route-b.tsx` | `/route-b` | `` | | 📂 `files` | | | | ┄ ʦ `$.tsx` | `/files/$` | `` | +| 📂 `account` | | | +| ┄ ʦ `route.tsx` | `/account` | `` | +| ┄ ʦ `overview.tsx` | `/account/overview` | `` | ## Flat Routes @@ -72,6 +75,8 @@ See the example below: | ʦ `_pathlessLayout.route-a.tsx` | `/route-a` | `` | | ʦ `_pathlessLayout.route-b.tsx` | `/route-b` | `` | | ʦ `files.$.tsx` | `/files/$` | `` | +| ʦ `account.tsx` | `/account` | `` | +| ʦ `account.overview.tsx` | `/account/overview` | `` | ## Mixed Flat and Directory Routes @@ -92,11 +97,13 @@ See the example below: | ʦ `settings.tsx` | `/settings` | `` | | ʦ `settings.profile.tsx` | `/settings/profile` | `` | | ʦ `settings.notifications.tsx` | `/settings/notifications` | `` | +| ʦ `account.tsx` | `/account` | `` | +| ʦ `account.overview.tsx` | `/account/overview` | `` | Both flat and directory routes can be mixed together to create a route tree that uses the best of both worlds where it makes sense. > [!TIP] -> 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. +> 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. ## Getting started with File-Based Routing @@ -106,13 +113,13 @@ To enable file-based routing, you'll need to be using React with a supported bun [//]: # 'SupportedBundlersList' -- [Installation with Vite](./installation-with-vite.md) -- [Installation with Rspack/Rsbuild](./installation-with-rspack.md) -- [Installation with Webpack](./installation-with-webpack.md) -- [Installation with Esbuild](./installation-with-esbuild.md) +- [Installation with Vite](../installation-with-vite.md) +- [Installation with Rspack/Rsbuild](../installation-with-rspack.md) +- [Installation with Webpack](../installation-with-webpack.md) +- [Installation with Esbuild](../installation-with-esbuild.md) [//]: # 'SupportedBundlersList' When 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. -If your bundler is not yet supported, you can reach out to us on Discord or GitHub to let us know. Till then, fear not! You can still use the [`@tanstack/router-cli`](./installation-with-router-cli.md) package to generate your route tree file. +If your bundler is not yet supported, you can reach out to us on Discord or GitHub to let us know. Till then, fear not! You can still use the [`@tanstack/router-cli`](../installation-with-router-cli.md) package to generate your route tree file. diff --git a/docs/router/framework/react/routing/file-naming-conventions.md b/docs/router/framework/react/routing/file-naming-conventions.md index 300895addc..859b05019c 100644 --- a/docs/router/framework/react/routing/file-naming-conventions.md +++ b/docs/router/framework/react/routing/file-naming-conventions.md @@ -2,21 +2,21 @@ title: File Naming Conventions --- -File-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. - -| Feature | Description | -| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **`__root.tsx`** | The root route file must be named `__root.tsx` and must be placed in the root of the configured `routesDirectory`. | -| **`.` Separator** | Routes can use the `.` character to denote a nested route. For example, `blog.post` will be generated as a child of `blog`. | -| **`$` Token** | Route segments with the `$` token are parameterized and will extract the value from the URL pathname as a route `param`. | -| **`_` 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. | -| **`_` Suffix** | Route segments with the `_` suffix exclude the route from being nested under any parent routes. | -| **`-` 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. | -| **`(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. | -| **`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, see [options](../../../api/file-based-routing.md#indextoken). | -| **`.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, see [options](../../../api/file-based-routing.md#routetoken). | - -> **💡 Remember:** The file-naming conventions for your project could be affected by what [options](../../../api/file-based-routing.md) are configured. +File-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. + +| Feature | Description | +| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`__root.tsx`** | The root route file must be named `__root.tsx` and must be placed in the root of the configured `routesDirectory`. | +| **`.` Separator** | Routes can use the `.` character to denote a nested route. For example, `blog.post` will be generated as a child of `blog`. | +| **`$` Token** | Route segments with the `$` token are parameterized and will extract the value from the URL pathname as a route `param`. | +| **`_` 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. | +| **`_` Suffix** | Route segments with the `_` suffix exclude the route from being nested under any parent routes. | +| **`-` 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. | +| **`(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. | +| **`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, see [options](../../../../api/file-based-routing.md#indextoken). | +| **`.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, see [options](../../../../api/file-based-routing.md#routetoken). | + +> **💡 Remember:** The file-naming conventions for your project could be affected by what [options](../../../../api/file-based-routing.md) are configured. ## Dynamic Path Params @@ -27,7 +27,7 @@ Dynamic path params can be used in both flat and directory routes to create rout | ... | ... | ... | | ʦ `posts.$postId.tsx` | `/posts/$postId` | `` | -We'll learn more about dynamic path params in the [Path Params](../guide/path-params.md) guide. +We'll learn more about dynamic path params in the [Path Params](../../guide/path-params.md) guide. ## Pathless Routes @@ -39,4 +39,4 @@ Pathless routes wrap child routes with either logic or a component without requi | ʦ `_app.a.tsx` | /a | `` | | ʦ `_app.b.tsx` | /b | `` | -To learn more about pathless routes, see the [Routing Concepts - Pathless Routes](./routing-concepts.md#pathless-layout-routes) guide. +To learn more about pathless routes, see the [Routing Concepts - Pathless Routes](../routing-concepts.md#pathless-layout-routes) guide. diff --git a/docs/router/framework/react/routing/installation-with-esbuild.md b/docs/router/framework/react/routing/installation-with-esbuild.md index 262213f386..d7facef96f 100644 --- a/docs/router/framework/react/routing/installation-with-esbuild.md +++ b/docs/router/framework/react/routing/installation-with-esbuild.md @@ -76,4 +76,4 @@ When using the TanStack Router Plugin with Esbuild for File-based routing, it co If 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 `TanStackRouterEsbuild` function. -You can find all the available configuration options in the [File-based Routing API Reference](../../../api/file-based-routing.md). +You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md). diff --git a/docs/router/framework/react/routing/installation-with-router-cli.md b/docs/router/framework/react/routing/installation-with-router-cli.md index c67d7c1f9c..53b94fcd6f 100644 --- a/docs/router/framework/react/routing/installation-with-router-cli.md +++ b/docs/router/framework/react/routing/installation-with-router-cli.md @@ -100,4 +100,4 @@ If these defaults work for your project, you don't need to configure anything at [//]: # 'TargetConfiguration' [//]: # 'TargetConfiguration' -You can find all the available configuration options in the [File-based Routing API Reference](../../../api/file-based-routing.md). +You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md). diff --git a/docs/router/framework/react/routing/installation-with-rspack.md b/docs/router/framework/react/routing/installation-with-rspack.md index b118773f86..d8d6b96af3 100644 --- a/docs/router/framework/react/routing/installation-with-rspack.md +++ b/docs/router/framework/react/routing/installation-with-rspack.md @@ -82,4 +82,4 @@ When using the TanStack Router Plugin with Rspack (or Rsbuild) for File-based ro If 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 `TanStackRouterVite` function. -You can find all the available configuration options in the [File-based Routing API Reference](../../../api/file-based-routing.md). +You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md). diff --git a/docs/router/framework/react/routing/installation-with-vite.md b/docs/router/framework/react/routing/installation-with-vite.md index 00dc2e37d8..f23f019768 100644 --- a/docs/router/framework/react/routing/installation-with-vite.md +++ b/docs/router/framework/react/routing/installation-with-vite.md @@ -84,4 +84,4 @@ When using the TanStack Router Plugin with Vite for File-based routing, it comes If 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 `TanStackRouterVite` function. -You can find all the available configuration options in the [File-based Routing API Reference](../../../api/file-based-routing.md). +You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md). diff --git a/docs/router/framework/react/routing/installation-with-webpack.md b/docs/router/framework/react/routing/installation-with-webpack.md index f98a9a1cd2..35514757ac 100644 --- a/docs/router/framework/react/routing/installation-with-webpack.md +++ b/docs/router/framework/react/routing/installation-with-webpack.md @@ -75,4 +75,4 @@ When using the TanStack Router Plugin with Webpack for File-based routing, it co If 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 `TanStackRouterWebpack` function. -You can find all the available configuration options in the [File-based Routing API Reference](../../../api/file-based-routing.md). +You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md). diff --git a/docs/router/framework/react/routing/route-trees.md b/docs/router/framework/react/routing/route-trees.md index c2d9729666..6065034b73 100644 --- a/docs/router/framework/react/routing/route-trees.md +++ b/docs/router/framework/react/routing/route-trees.md @@ -6,8 +6,8 @@ TanStack Router uses a nested route tree to match up the URL with the correct co To build a route tree, TanStack Router supports: -- [File-Based Routing](./file-based-routing.md) -- [Code-Based Routing](./code-based-routing.md) +- [File-Based Routing](../file-based-routing.md) +- [Code-Based Routing](../code-based-routing.md) Both 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. @@ -58,10 +58,10 @@ The above is a valid route tree configuration that can be used with TanStack Rou Route trees can be configured using a few different ways: -- [Flat Routes](./file-based-routing.md#flat-routes) -- [Directories](./file-based-routing.md#directory-routes) -- [Mixed Flat Routes and Directories](./file-based-routing.md#mixed-flat-and-directory-routes) -- [Virtual File Routes](./virtual-file-routes.md) -- [Code-Based Routes](./code-based-routing.md) +- [Flat Routes](../file-based-routing.md#flat-routes) +- [Directories](../file-based-routing.md#directory-routes) +- [Mixed Flat Routes and Directories](../file-based-routing.md#mixed-flat-and-directory-routes) +- [Virtual File Routes](../virtual-file-routes.md) +- [Code-Based Routes](../code-based-routing.md) Please 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. diff --git a/docs/router/framework/react/routing/routing-concepts.md b/docs/router/framework/react/routing/routing-concepts.md index 38d9ae5cd9..fcc4a6c7ef 100644 --- a/docs/router/framework/react/routing/routing-concepts.md +++ b/docs/router/framework/react/routing/routing-concepts.md @@ -59,7 +59,7 @@ export interface MyRouterContext { export const Route = createRootRouteWithContext() ``` -To learn more about Context in TanStack Router, see the [Router Context](../guide/router-context.md) guide. +To learn more about Context in TanStack Router, see the [Router Context](../../guide/router-context.md) guide. ## Basic Routes diff --git a/docs/router/framework/solid/quick-start.md b/docs/router/framework/solid/quick-start.md index a831b6de1d..de012cae2c 100644 --- a/docs/router/framework/solid/quick-start.md +++ b/docs/router/framework/solid/quick-start.md @@ -57,7 +57,7 @@ export default defineConfig({ ``` > [!TIP] -> If you are not using Vite, or any of the supported bundlers, you can check out the [TanStack Router CLI](./routing/installation-with-router-cli.md) guide for more info. +> If you are not using Vite, or any of the supported bundlers, you can check out the [TanStack Router CLI](../routing/installation-with-router-cli.md) guide for more info. Create the following files: diff --git a/docs/start/config.json b/docs/start/config.json index 76cae14ab8..f7e31b3557 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -76,6 +76,10 @@ { "label": "Path Aliases", "to": "framework/react/path-aliases" + }, + { + "label": "Migrate from Next.js", + "to": "framework/react/migrate-from-next-js" } ] }, diff --git a/docs/start/framework/react/authentication.md b/docs/start/framework/react/authentication.md index fe90ebbcff..6b78767a62 100644 --- a/docs/start/framework/react/authentication.md +++ b/docs/start/framework/react/authentication.md @@ -17,9 +17,9 @@ That said, authentication is not something to be taken lightly. After much vetti - - - Clerk logo + + + Clerk logo @@ -27,8 +27,8 @@ Clerk is a modern authentication platform that provides a full suite of authenti - To learn more about Clerk, visit the [Clerk website](https://go.clerk.com/wOwHtuJ) - To sign up, visit the [Clerk dashboard](https://go.clerk.com/PrSDXti) -- To get started with Clerk, check out our [official Start + Clerk examples!](../examples/start-clerk-basic/) +- To get started with Clerk, check out our [official Start + Clerk examples!](https://github.com/TanStack/router/tree/main/examples/react/start-clerk-basic/) ## Documentation & APIs -Documentation for implementing your own authentication logic with TanStack Start is coming soon! In the meantime, you can check out any of the `-auth` prefixed [examples](../examples) for a starting point. +Documentation for implementing your own authentication logic with TanStack Start is coming soon! In the meantime, you can check out any of the `-auth` prefixed [examples](https://github.com/TanStack/router/tree/main/examples/react) for a starting point. diff --git a/docs/start/framework/react/build-from-scratch.md b/docs/start/framework/react/build-from-scratch.md index 055526d7f8..a0f09832d9 100644 --- a/docs/start/framework/react/build-from-scratch.md +++ b/docs/start/framework/react/build-from-scratch.md @@ -315,4 +315,4 @@ That's it! 🤯 You've now set up a TanStack Start project and written your firs You can now run `npm run dev` to start your server and navigate to `http://localhost:3000` to see your route in action. -You want to deploy your application? Check out the [hosting guide](./hosting.md). +You want to deploy your application? Check out the [hosting guide](../hosting.md). diff --git a/docs/start/framework/react/databases.md b/docs/start/framework/react/databases.md index 6f6aba34bd..39d4cd09dc 100644 --- a/docs/start/framework/react/databases.md +++ b/docs/start/framework/react/databases.md @@ -41,9 +41,9 @@ While TanStack Start is designed to work with any database provider, we highly r - - - Neon logo + + + Neon logo @@ -57,17 +57,18 @@ Key features that make Neon stand out: - Point-in-time restore - Web-based SQL editor - Bottomless storage - +
+
- To learn more about Neon, visit the [Neon website](https://neon.tech?utm_source=tanstack) -- To sign up, visit the [Neon dashboard](https://console.neon.tech/sign_up?utm_source=tanstack) +- To sign up, visit the [Neon dashboard](https://console.neon.tech/signup?utm_source=tanstack) ## What is Convex? - - - Convex logo + + + Convex logo diff --git a/docs/start/framework/react/hosting.md b/docs/start/framework/react/hosting.md index 5241494e80..d48eb493b0 100644 --- a/docs/start/framework/react/hosting.md +++ b/docs/start/framework/react/hosting.md @@ -15,9 +15,9 @@ However, since hosting is one of the most crucial aspects of your application's - - - Netlify logo + + + Netlify logo diff --git a/docs/start/framework/react/migrate-from-next-js.md b/docs/start/framework/react/migrate-from-next-js.md new file mode 100644 index 0000000000..acf0d4a06d --- /dev/null +++ b/docs/start/framework/react/migrate-from-next-js.md @@ -0,0 +1,408 @@ +--- +id: migrate-from-next-js +title: Migrate from Next.js +--- + +> [!IMPORTANT] +> This guide is based on the upcoming work in the `alpha` branch of **TanStack Start**. We are actively working on exciting new features, and this guide will be updated soon. + +This guide provides a step-by-step process to migrate a project from the Next.js App Router to **TanStack Start**. We respect the powerful features of Next.js and aim to make this transition as smooth as possible. + +## Step-by-Step (Basics) + +This step-by-step guide provides an overview of how to migrate your Next.js App Router project to TanStack Start using a starter template. The goal is to help you understand the basic steps involved in the migration process so you can adapt them to your specific project needs. + +### Prerequisites + +Before we begin, this guide assumes your project structure looks like this: + +```txt +. +├── next.config.ts +├── package.json +├── postcss.config.mjs +├── public +│ ├── file.svg +│ ├── globe.svg +│ ├── next.svg +│ ├── vercel.svg +│ └── window.svg +├── README.md +├── src +│ └── app +│ ├── favicon.ico +│ ├── globals.css +│ ├── layout.tsx +│ └── page.tsx +└── tsconfig.json +``` + +Alternatively, you can follow along by cloning the following [starter template](https://github.com/nrjdalal/awesome-templates/tree/main/next.js-apps/next.js-start): + +```sh +npx gitpick nrjdalal/awesome-templates/tree/main/next.js-apps/next.js-start next.js-start-er +``` + +This structure or starter is a basic Next.js application using the App Router, which we will migrate to TanStack Start. + +### 1. Remove Next.js + +First, uninstall Next.js and remove related configuration files: + +```sh +npm uninstall @tailwindcss/postcss next +rm postcss.config.* next.config.* +``` + +### 2. Install Required Dependencies + +TanStack Start leverages [Vite](https://vite.dev) and TanStack Router: + +> [!NOTE] +> We're using the `alpha` version of TanStack Start and TanStack Router. This will change once they are merged into main. + +```sh +npm i @tanstack/react-router@alpha @tanstack/react-start@alpha vite +``` + +For Tailwind CSS and resolving imports using path aliases: + +```sh +npm i -D @tailwindcss/vite tailwindcss vite-tsconfig-paths +``` + +### 3. Update Project Configuration + +Now that you've installed the necessary dependencies, update your project configuration files to work with TanStack Start. + +- `package.json` + +```json +{ + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "start": "node .output/server/index.mjs" + } +} +``` + +- `vite.config.ts` + +```ts +// vite.config.ts +import tailwindcss from '@tailwindcss/vite' +import { tanstackStart } from '@tanstack/react-start/plugin/vite' +import { defineConfig } from 'vite' +import tsconfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + server: { + port: 3000, + }, + plugins: [ + tailwindcss(), + // Enables Vite to resolve imports using path aliases. + tsconfigPaths(), + tanstackStart({ + tsr: { + // Specifies the directory TanStack Router uses for your routes. + routesDirectory: 'src/app', // Defaults to "src/routes" + }, + }), + ], +}) +``` + +By default, `routesDirectory` is set to `src/routes`. To maintain consistency with Next.js App Router conventions, you can set it to `src/app` instead. + +### 4. Adapt the Root Layout + +> TanStack Start uses a routing approach similar to Remix, with some changes to support nested structures and special features using tokens. Learn more about it at [Routing Concepts](/router/latest/docs/framework/react/routing/routing-concepts) guide. + +Instead of `layout.tsx`, create a file named `__root.tsx` in the `src/app` directory. This file will serve as the root layout for your application. + +- `src/app/layout.tsx` to `src/app/__root.tsx` + +```tsx +- import type { Metadata } from "next" // [!code --] +import { + Outlet, + createRootRoute, + HeadContent, + Scripts, +} from "@tanstack/react-router" +import "./globals.css" + +- export const metadata: Metadata = { // [!code --] +- title: "Create Next App", // [!code --] +- description: "Generated by create next app", // [!code --] +- } // [!code --] +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { charSet: "utf-8" }, + { + name: "viewport", + content: "width=device-width, initial-scale=1", + }, + { title: "TanStack Start Starter" } + ], + }), + component: RootLayout, +}) + +- export default function RootLayout({ // [!code --] +- children, // [!code --] +- }: Readonly<{ // [!code --] +- children: React.ReactNode // [!code --] +- }>) { // [!code --] +- return ( // [!code --] +- // [!code --] +- {children} // [!code --] +- // [!code --] +- ) // [!code --] +- } // [!code --] +function RootLayout() { + return ( + + + + + + + + + + ) +} +``` + +### 5. Adapt the Home Page + +Instead of `page.tsx`, create an `index.tsx` file for the `/` route. + +- `src/app/page.tsx` to `src/app/index.tsx` + +```tsx +- export default function Home() { // [!code --] ++ export const Route = createFileRoute({ // [!code ++] ++ component: Home, // [!code ++] ++ }) // [!code ++] + ++ function Home() { // [!code ++] + return ( +
+ TanStack Logo +

+ Next.js TanStack Start +

+ + Docs + +
+ ) +} +``` + +### 6. Are we migrated yet? + +Before you can run the development server, you need to create a router file that will define the behavior of TanStack Router within TanStack Start. + +- `src/router.tsx` + +```tsx +import { createRouter as createTanStackRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' + +export function createRouter() { + const router = createTanStackRouter({ + routeTree, + scrollRestoration: true, + }) + + return router +} + +declare module '@tanstack/react-router' { + interface Register { + router: ReturnType + } +} +``` + +> 🧠 Here you can configure everything from the default [preloading functionality](/router/latest/docs/framework/react/guide/preloading) to [caching staleness](/router/latest/docs/framework/react/guide/data-loading). + +Don't worry if you see some TypeScript errors at this point; the next step will resolve them. + +### 7. Verify the Migration + +Run the development server: + +```sh +npm run dev +``` + +Then, visit `http://localhost:3000`. You should see the TanStack Start welcome page with its logo and a documentation link. + +> If you encounter issues, review the steps above and ensure that file names and paths match exactly. For a reference implementation, see the [post-migration repository](https://github.com/nrjdalal/next-to-start). + +## Next Steps (Advanced) + +Now that you have migrated the basic structure of your Next.js application to TanStack Start, you can explore more advanced features and concepts. + +### Routing Concepts + +| Route Example | Next.js | TanStack Start | +| ------------------------------ | ---------------------------------- | ------------------------- | +| Root Layout | `src/app/layout.tsx` | `src/app/__root.tsx` | +| `/` (Home Page) | `src/app/page.tsx` | `src/app/index.tsx` | +| `/posts` (Static Route) | `src/app/posts/page.tsx` | `src/app/posts.tsx` | +| `/posts/[slug]` (Dynamic) | `src/app/posts/[slug]/page.tsx` | `src/app/posts/$slug.tsx` | +| `/posts/[...slug]` (Catch-All) | `src/app/posts/[...slug]/page.tsx` | `src/app/posts/$.tsx` | +| `/api/endpoint` (API Route) | `src/app/api/endpoint/route.ts` | `src/app/api/endpoint.ts` | + +Learn more about the [Routing Concepts](/router/latest/docs/framework/react/routing/routing-concepts). + +### Dynamic and Catch-All Routes + +Retrieving dynamic route parameters in TanStack Start is straightforward. + +```tsx +- export default async function Page({ // [!code --] +- params, // [!code --] +- }: { // [!code --] +- params: Promise<{ slug: string }> // [!code --] +- }) { // [!code --] ++ export const Route = createFileRoute({ // [!code ++] ++ component: Page, // [!code ++] ++ }) // [!code ++] + ++ function Page() { // [!code ++] +- const { slug } = await params // [!code --] ++ const { slug } = Route.useParams() // [!code ++] + return
My Post: {slug}
+} +``` + +> Note: If you've made a catch-all route (like `src/app/posts/$.tsx`), you can access the parameters via `const { _splat } = Route.useParams()`. + +Similarly, you can access `searchParams` using `const { page, filter, sort } = Route.useSearch()`. + +Learn more about the [Dynamic and Catch-All Routes](/router/latest/docs/framework/react/routing/routing-concepts#dynamic-route-segments). + +### Links + +```tsx +- import Link from "next/link" // [!code --] ++ import { Link } from "@tanstack/react-router" // [!code ++] + +function Component() { +- return Dashboard // [!code --] ++ return Dashboard // [!code ++] +} +``` + +Learn more about the [Links](../learn-the-basics.md#navigation). + +### Server ~Actions~ Functions + +```tsx +- 'use server' // [!code --] ++ import { createServerFn } from "@tanstack/react-start" // [!code ++] + +- export const create = async () => { // [!code --] ++ export const create = createServerFn().handler(async () => { // [!code ++] + return true +- } // [!code --] ++ }) // [!code ++] +``` + +Learn more about the [Server Functions](./server-functions.md). + +### Server Routes ~Handlers~ + +```ts +- export async function GET() { // [!code --] ++ export const ServerRoute = createServerFileRoute().methods({ // [!code ++] ++ GET: async () => { // [!code ++] + return Response.json("Hello, World!") + } ++ }) // [!code ++] +``` + +Learn more about the [Server Routes](./server-routes.md). + +### Fonts + +```tsx +- import { Inter } from "next/font/google" // [!code --] + +- const inter = Inter({ // [!code --] +- subsets: ["latin"], // [!code --] +- display: "swap", // [!code --] +- }) // [!code --] + +- export default function Page() { // [!code --] +- return

Font Sans

// [!code --] +- } // [!code --] +``` + +Instead of `next/font`, use Tailwind CSS’s CSS-first approach. Install fonts (for example, from [Fontsource](https://github.com/fontsource/fontsource)): + +```sh +npm i -D @fontsource-variable/dm-sans @fontsource-variable/jetbrains-mono +``` + +Add the following to `src/app/globals.css`: + +```css +@import 'tailwindcss'; + +@import '@fontsource-variable/dm-sans'; /* [!code ++] */ +@import '@fontsource-variable/jetbrains-mono'; /* [!code ++] */ + +@theme inline { + --font-sans: 'DM Sans Variable', sans-serif; /* [!code ++] */ + --font-mono: 'JetBrains Mono Variable', monospace; /* [!code ++] */ + /* ... */ +} + +/* ... */ +``` + +### Fetching Data + +```tsx +- export default async function Page() { // [!code --] ++ export const Route = createFileRoute({ // [!code ++] ++ component: Page, // [!code ++] ++ loader: async () => { // [!code ++] ++ const res = await fetch('https://api.vercel.app/blog') // [!code ++] ++ return res.json() // [!code ++] ++ }, // [!code ++] ++ }) // [!code ++] + ++ function Page() { // [!code ++] +- const data = await fetch('https://api.vercel.app/blog') // [!code --] +- const posts = await data.json() // [!code --] ++ const posts = Route.useLoaderData() // [!code ++] + + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` diff --git a/docs/start/framework/react/observability.md b/docs/start/framework/react/observability.md index fb483ba814..78e6a2dff3 100644 --- a/docs/start/framework/react/observability.md +++ b/docs/start/framework/react/observability.md @@ -15,9 +15,9 @@ However, for the best observability experience, we highly recommend using [Sentr - - - Convex logo + + + Convex logo diff --git a/docs/start/framework/react/overview.md b/docs/start/framework/react/overview.md index ca4bbac835..4316c864e5 100644 --- a/docs/start/framework/react/overview.md +++ b/docs/start/framework/react/overview.md @@ -71,45 +71,45 @@ TanStack works closely with our partners to provide the best possible developer - **Clerk** - - - Clerk logo + + + Clerk logo The best possible authentication experience for modern web applications, including TanStack Start applications. Clerk provides TanStack Start users with first-class integrations and solutions to auth and collaborates closely with the TanStack team to ensure that TanStack Start provides APIs that are up to date with the latest in auth best practices. - **Netlify** - - - Netlify logo + + + Netlify logo The leading hosting platform for web applications that provides a fast, secure, and reliable environment for deploying your web applications. We work closely with Netlify to ensure that TanStack Start applications not only deploy seamlessly to their platform, but also implement best practices for performance, security, and reliability regardless of where you end up deploying. - **Neon** - - - Neon logo + + + Neon logo A serverless, autoscaling Postgres solution purpose-built for modern full-stack apps. Neon offers rich integration opportunities with TanStack Start, including server functions and database-backed routing. Together, we’re simplifying the database experience for developers using TanStack. - **Convex** - - - Convex logo + + + Convex logo A serverless database platform that integrates seamlessly with TanStack Start. Convex is designed to simplify the process of managing your application's data and provides a real-time, scalable, and transactional data backend that works well with TanStack Start applications. Convex also collaborates closely with the TanStack team to ensure that TanStack Start provides APIs that are up to date with the latest in database best practices. - **Sentry** - - - Sentry logo + + + Sentry logo A powerful, full-featured observability platform that integrates seamlessly with TanStack Start. Sentry helps developers monitor and fix crashes in real-time and provides insights into your application's performance and error tracking. Sentry collaborates closely with the TanStack team to ensure that TanStack Start provides APIs that are up to date with the latest in observability best practices. diff --git a/docs/start/framework/react/quick-start.md b/docs/start/framework/react/quick-start.md index dab0590691..75913e24e2 100644 --- a/docs/start/framework/react/quick-start.md +++ b/docs/start/framework/react/quick-start.md @@ -5,7 +5,7 @@ title: Quick Start ## Impatient? -If you're impatient, you can clone and run the [Basic](../examples/start-basic) example right away with the following commands: +If you're impatient, you can clone and run the [Basic](https://github.com/TanStack/router/tree/main/examples/react/start-basic) example right away with the following commands: ```bash npx gitpick TanStack/router/tree/main/examples/react/start-basic start-basic @@ -22,16 +22,16 @@ Once you've cloned the example you want, head back to the [Learn the Basics](../ TanStack Start has load of examples to get you started. Pick one of the examples below to get started! -- [Basic](../examples/start-basic) (start-basic) -- [Basic + Auth](../examples/start-basic-auth) (start-basic-auth) -- [Counter](../examples/start-counter) (start-counter) -- [Basic + React Query](../examples/start-basic-react-query) (start-basic-react-query) -- [Clerk Auth](../examples/start-clerk-basic) (start-clerk-basic) -- [Convex + Trellaux](../examples/start-convex-trellaux) (start-convex-trellaux) -- [Supabase](../examples/start-supabase-basic) (start-supabase-basic) -- [Trellaux](../examples/start-trellaux) (start-trellaux) -- [WorkOS](../examples/start-workos) (start-workos) -- [Material UI](../examples/start-material-ui) (start-material-ui) +- [Basic](https://github.com/TanStack/router/tree/main/examples/react/start-basic) (start-basic) +- [Basic + Auth](https://github.com/TanStack/router/tree/main/examples/react/start-basic-auth) (start-basic-auth) +- [Counter](https://github.com/TanStack/router/tree/main/examples/react/start-counter) (start-counter) +- [Basic + React Query](https://github.com/TanStack/router/tree/main/examples/react/start-basic-react-query) (start-basic-react-query) +- [Clerk Auth](https://github.com/TanStack/router/tree/main/examples/react/start-clerk-basic) (start-clerk-basic) +- [Convex + Trellaux](https://github.com/TanStack/router/tree/main/examples/react/start-convex-trellaux) (start-convex-trellaux) +- [Supabase](https://github.com/TanStack/router/tree/main/examples/react/start-supabase-basic) (start-supabase-basic) +- [Trellaux](https://github.com/TanStack/router/tree/main/examples/react/start-trellaux) (start-trellaux) +- [WorkOS](https://github.com/TanStack/router/tree/main/examples/react/start-workos) (start-workos) +- [Material UI](https://github.com/TanStack/router/tree/main/examples/react/start-material-ui) (start-material-ui) ### Stackblitz @@ -58,15 +58,15 @@ Once you've clone or deployed an example, head back to the [Learn the Basics](.. While not Start-specific examples, these may help you understand more about how TanStack Router works: -- [Quickstart (file-based)](../examples/quickstart-file-based) -- [Basic (file-based)](../examples/basic-file-based) -- [Kitchen Sink (file-based)](../examples/kitchen-sink-file-based) -- [Kitchen Sink + React Query (file-based)](../examples/kitchen-sink-react-query-file-based) -- [Location Masking](../examples/location-masking) -- [Authenticated Routes](../examples/authenticated-routes) -- [Scroll Restoration](../examples/scroll-restoration) -- [Deferred Data](../examples/deferred-data) -- [Navigation Blocking](../examples/navigation-blocking) -- [View Transitions](../examples/view-transitions) -- [With tRPC](../examples/with-trpc) -- [With tRPC + React Query](../examples/with-trpc-react-query) +- [Quickstart (file-based)](https://github.com/TanStack/router/tree/main/examples/react/quickstart-file-based) +- [Basic (file-based)](https://github.com/TanStack/router/tree/main/examples/react/basic-file-based) +- [Kitchen Sink (file-based)](https://github.com/TanStack/router/tree/main/examples/react/kitchen-sink-file-based) +- [Kitchen Sink + React Query (file-based)](https://github.com/TanStack/router/tree/main/examples/react/kitchen-sink-react-query-file-based) +- [Location Masking](https://github.com/TanStack/router/tree/main/examples/react/location-masking) +- [Authenticated Routes](https://github.com/TanStack/router/tree/main/examples/react/authenticated-routes) +- [Scroll Restoration](https://github.com/TanStack/router/tree/main/examples/react/scroll-restoration) +- [Deferred Data](https://github.com/TanStack/router/tree/main/examples/react/deferred-data) +- [Navigation Blocking](https://github.com/TanStack/router/tree/main/examples/react/navigation-blocking) +- [View Transitions](https://github.com/TanStack/router/tree/main/examples/react/view-transitions) +- [With tRPC](https://github.com/TanStack/router/tree/main/examples/react/with-trpc) +- [With tRPC + React Query](https://github.com/TanStack/router/tree/main/examples/react/with-trpc-react-query) diff --git a/docs/start/framework/react/server-functions.md b/docs/start/framework/react/server-functions.md index 61c97a5080..9204938d8a 100644 --- a/docs/start/framework/react/server-functions.md +++ b/docs/start/framework/react/server-functions.md @@ -7,7 +7,7 @@ title: Server Functions Server functions allow you to specify logic that can be invoked almost anywhere (even the client), but run **only** on the server. In fact, they are not so different from an API Route, but with a few key differences: -- They do not have stable public URL (but you'll be able to do this very soon!) +- They do not have stable public URL - They can be called from anywhere in your application, including loaders, hooks, components, etc., but cannot be called from API Routes. However, they are similar to regular API Routes in that: @@ -32,7 +32,7 @@ Server functions can be defined anywhere in your application, but must be define ## Server Function Middleware -Server functions can use middleware to share logic, context, common operations, prerequisites, and much more. To learn more about server function middleware, be sure to read about them in the [Middleware guide](./middleware.md). +Server functions can use middleware to share logic, context, common operations, prerequisites, and much more. To learn more about server function middleware, be sure to read about them in the [Middleware guide](../middleware.md). ## Defining Server Functions diff --git a/docs/start/framework/solid/authentication.md b/docs/start/framework/solid/authentication.md index 24a74f7316..eee03da9da 100644 --- a/docs/start/framework/solid/authentication.md +++ b/docs/start/framework/solid/authentication.md @@ -17,9 +17,9 @@ That said, authentication is not something to be taken lightly. After much vetti - - - Convex logo + + + Convex logo diff --git a/docs/start/framework/solid/build-from-scratch.md b/docs/start/framework/solid/build-from-scratch.md index c5fcdb3563..7d0e7c6515 100644 --- a/docs/start/framework/solid/build-from-scratch.md +++ b/docs/start/framework/solid/build-from-scratch.md @@ -297,4 +297,4 @@ That's it! 🤯 You've now set up a TanStack Start project and written your firs You can now run `npm run dev` to start your server and navigate to `http://localhost:3000` to see your route in action. -You want to deploy your application? Check out the [hosting guide](./hosting.md). +You want to deploy your application? Check out the [hosting guide](../hosting.md). diff --git a/docs/start/framework/solid/hosting.md b/docs/start/framework/solid/hosting.md index 0acb872b8b..0a1586223e 100644 --- a/docs/start/framework/solid/hosting.md +++ b/docs/start/framework/solid/hosting.md @@ -15,9 +15,9 @@ However, since hosting is one of the most crucial aspects of your application's - - - Netlify logo + + + Netlify logo diff --git a/docs/start/framework/solid/quick-start.md b/docs/start/framework/solid/quick-start.md index 8d296d362c..4006381156 100644 --- a/docs/start/framework/solid/quick-start.md +++ b/docs/start/framework/solid/quick-start.md @@ -5,7 +5,7 @@ title: Quick Start ## Impatient? -If you're impatient, you can clone and run the [Basic](../examples/start-basic) example right away with the following commands: +If you're impatient, you can clone and run the [Basic](https://github.com/TanStack/router/tree/main/examples/solid/start-basic) example right away with the following commands: ```bash npx degit https://github.com/tanstack/router/examples/solid/start-basic start-basic @@ -22,8 +22,8 @@ Once you've cloned the example you want, head back to the [Learn the Basics](../ TanStack Start has load of examples to get you started. Pick one of the examples below to get started! -- [Basic](../examples/start-basic) (start-basic) -- [Bare](../examples/start-bare) (start-bare) +- [Basic](https://github.com/TanStack/router/tree/main/examples/solid/start-basic) (start-basic) +- [Bare](https://github.com/TanStack/router/tree/main/examples/solid/start-bare) (start-bare) ### Stackblitz diff --git a/e2e/react-start/basic/src/routeTree.gen.ts b/e2e/react-start/basic/src/routeTree.gen.ts index 1335898845..e313ca5580 100644 --- a/e2e/react-start/basic/src/routeTree.gen.ts +++ b/e2e/react-start/basic/src/routeTree.gen.ts @@ -30,6 +30,8 @@ import { Route as RedirectTargetImport } from './routes/redirect/$target' import { Route as PostsPostIdImport } from './routes/posts.$postId' import { Route as NotFoundViaLoaderImport } from './routes/not-found/via-loader' import { Route as NotFoundViaBeforeLoadImport } from './routes/not-found/via-beforeLoad' +import { Route as ErrorHandlingViaLoaderImport } from './routes/error-handling/via-loader' +import { Route as ErrorHandlingViaBeforeLoadImport } from './routes/error-handling/via-beforeLoad' import { Route as LayoutLayout2Import } from './routes/_layout/_layout-2' import { Route as RedirectTargetIndexImport } from './routes/redirect/$target/index' import { Route as RedirectTargetViaLoaderImport } from './routes/redirect/$target/via-loader' @@ -157,6 +159,20 @@ const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadImport.update({ getParentRoute: () => NotFoundRouteRoute, } as any) +const ErrorHandlingViaLoaderRoute = ErrorHandlingViaLoaderImport.update({ + id: '/error-handling/via-loader', + path: '/error-handling/via-loader', + getParentRoute: () => rootRoute, +} as any) + +const ErrorHandlingViaBeforeLoadRoute = ErrorHandlingViaBeforeLoadImport.update( + { + id: '/error-handling/via-beforeLoad', + path: '/error-handling/via-beforeLoad', + getParentRoute: () => rootRoute, + } as any, +) + const LayoutLayout2Route = LayoutLayout2Import.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, @@ -308,6 +324,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutLayout2Import parentRoute: typeof LayoutImport } + '/error-handling/via-beforeLoad': { + id: '/error-handling/via-beforeLoad' + path: '/error-handling/via-beforeLoad' + fullPath: '/error-handling/via-beforeLoad' + preLoaderRoute: typeof ErrorHandlingViaBeforeLoadImport + parentRoute: typeof rootRoute + } + '/error-handling/via-loader': { + id: '/error-handling/via-loader' + path: '/error-handling/via-loader' + fullPath: '/error-handling/via-loader' + preLoaderRoute: typeof ErrorHandlingViaLoaderImport + parentRoute: typeof rootRoute + } '/not-found/via-beforeLoad': { id: '/not-found/via-beforeLoad' path: '/via-beforeLoad' @@ -548,6 +578,8 @@ export interface FileRoutesByFullPath { '/search-params': typeof SearchParamsRoute '/stream': typeof StreamRoute '/users': typeof UsersRouteWithChildren + '/error-handling/via-beforeLoad': typeof ErrorHandlingViaBeforeLoadRoute + '/error-handling/via-loader': typeof ErrorHandlingViaLoaderRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute '/posts/$postId': typeof PostsPostIdRoute @@ -577,6 +609,8 @@ export interface FileRoutesByTo { '/scripts': typeof ScriptsRoute '/search-params': typeof SearchParamsRoute '/stream': typeof StreamRoute + '/error-handling/via-beforeLoad': typeof ErrorHandlingViaBeforeLoadRoute + '/error-handling/via-loader': typeof ErrorHandlingViaLoaderRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute '/posts/$postId': typeof PostsPostIdRoute @@ -610,6 +644,8 @@ export interface FileRoutesById { '/stream': typeof StreamRoute '/users': typeof UsersRouteWithChildren '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren + '/error-handling/via-beforeLoad': typeof ErrorHandlingViaBeforeLoadRoute + '/error-handling/via-loader': typeof ErrorHandlingViaLoaderRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute '/posts/$postId': typeof PostsPostIdRoute @@ -644,6 +680,8 @@ export interface FileRouteTypes { | '/search-params' | '/stream' | '/users' + | '/error-handling/via-beforeLoad' + | '/error-handling/via-loader' | '/not-found/via-beforeLoad' | '/not-found/via-loader' | '/posts/$postId' @@ -672,6 +710,8 @@ export interface FileRouteTypes { | '/scripts' | '/search-params' | '/stream' + | '/error-handling/via-beforeLoad' + | '/error-handling/via-loader' | '/not-found/via-beforeLoad' | '/not-found/via-loader' | '/posts/$postId' @@ -703,6 +743,8 @@ export interface FileRouteTypes { | '/stream' | '/users' | '/_layout/_layout-2' + | '/error-handling/via-beforeLoad' + | '/error-handling/via-loader' | '/not-found/via-beforeLoad' | '/not-found/via-loader' | '/posts/$postId' @@ -736,6 +778,8 @@ export interface RootRouteChildren { SearchParamsRoute: typeof SearchParamsRoute StreamRoute: typeof StreamRoute UsersRoute: typeof UsersRouteWithChildren + ErrorHandlingViaBeforeLoadRoute: typeof ErrorHandlingViaBeforeLoadRoute + ErrorHandlingViaLoaderRoute: typeof ErrorHandlingViaLoaderRoute RedirectTargetRoute: typeof RedirectTargetRouteWithChildren RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute @@ -752,6 +796,8 @@ const rootRouteChildren: RootRouteChildren = { SearchParamsRoute: SearchParamsRoute, StreamRoute: StreamRoute, UsersRoute: UsersRouteWithChildren, + ErrorHandlingViaBeforeLoadRoute: ErrorHandlingViaBeforeLoadRoute, + ErrorHandlingViaLoaderRoute: ErrorHandlingViaLoaderRoute, RedirectTargetRoute: RedirectTargetRouteWithChildren, RedirectIndexRoute: RedirectIndexRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, @@ -777,6 +823,8 @@ export const routeTree = rootRoute "/search-params", "/stream", "/users", + "/error-handling/via-beforeLoad", + "/error-handling/via-loader", "/redirect/$target", "/redirect/", "/posts_/$postId/deep" @@ -836,6 +884,12 @@ export const routeTree = rootRoute "/_layout/_layout-2/layout-b" ] }, + "/error-handling/via-beforeLoad": { + "filePath": "error-handling/via-beforeLoad.tsx" + }, + "/error-handling/via-loader": { + "filePath": "error-handling/via-loader.tsx" + }, "/not-found/via-beforeLoad": { "filePath": "not-found/via-beforeLoad.tsx", "parent": "/not-found" diff --git a/e2e/react-start/basic/src/routes/__root.tsx b/e2e/react-start/basic/src/routes/__root.tsx index 1b64b1c468..17a88f34d3 100644 --- a/e2e/react-start/basic/src/routes/__root.tsx +++ b/e2e/react-start/basic/src/routes/__root.tsx @@ -87,7 +87,7 @@ function RootDocument({ children }: { children: React.ReactNode }) { -
+
This Route Does Not Exist + + Error Handling (beforeLoad) + + + Error Handling (loader) +

{children} diff --git a/e2e/react-start/basic/src/routes/error-handling/via-beforeLoad.tsx b/e2e/react-start/basic/src/routes/error-handling/via-beforeLoad.tsx new file mode 100644 index 0000000000..3c60c36951 --- /dev/null +++ b/e2e/react-start/basic/src/routes/error-handling/via-beforeLoad.tsx @@ -0,0 +1,23 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/error-handling/via-beforeLoad')({ + component: RouteComponent, + beforeLoad: async () => { + throw new Error('before load error') + }, + errorComponent: () => { + return ( +
+ error component +
+ ) + }, +}) + +function RouteComponent() { + return ( +
+ route component +
+ ) +} diff --git a/e2e/react-start/basic/src/routes/error-handling/via-loader.tsx b/e2e/react-start/basic/src/routes/error-handling/via-loader.tsx new file mode 100644 index 0000000000..5f46ba2101 --- /dev/null +++ b/e2e/react-start/basic/src/routes/error-handling/via-loader.tsx @@ -0,0 +1,23 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/error-handling/via-loader')({ + loader: async () => { + throw new Error('Loader error in /error-handling/loader-error-handling') + }, + component: RouteComponent, + errorComponent: () => { + return ( +
+ error component +
+ ) + }, +}) + +function RouteComponent() { + return ( +
+ route component +
+ ) +} diff --git a/e2e/react-start/basic/tests/error-handling.spec.ts b/e2e/react-start/basic/tests/error-handling.spec.ts new file mode 100644 index 0000000000..580d70d139 --- /dev/null +++ b/e2e/react-start/basic/tests/error-handling.spec.ts @@ -0,0 +1,52 @@ +import { expect, test } from '@playwright/test' +import type { Page } from '@playwright/test' + +async function expectBeforeLoadHandledByErrorComponent(page: Page) { + await page.waitForLoadState('networkidle') + await expect( + page.getByTestId('error-handling-before-load-error-component'), + ).toBeVisible() + await expect( + page.getByTestId('error-handling-before-load-route-component'), + ).not.toBeVisible() +} + +test('beforeLoad error should be handled by errorComponent on client navigation', async ({ + page, +}) => { + await page.goto('/') + await page.click('a[href="/error-handling/via-beforeLoad"]') + await expectBeforeLoadHandledByErrorComponent(page) +}) + +test('beforeLoad error should be handled by errorComponent on initial request', async ({ + page, +}) => { + await page.goto('/error-handling/via-beforeLoad') + await expectBeforeLoadHandledByErrorComponent(page) +}) + +async function expectLoaderErrorHandledByErrorComponent(page: Page) { + await page.waitForLoadState('networkidle') + await expect( + page.getByTestId('error-handling-loader-error-component'), + ).toBeVisible() + await expect( + page.getByTestId('error-handling-loader-route-component'), + ).not.toBeVisible() +} + +test('loader error should be handled by errorComponent on client navigation', async ({ + page, +}) => { + await page.goto('/') + await page.click('a[href="/error-handling/via-loader"]') + await expectLoaderErrorHandledByErrorComponent(page) +}) + +test('loader error should be handled by errorComponent on initial request', async ({ + page, +}) => { + await page.goto('/error-handling/via-loader') + await expectLoaderErrorHandledByErrorComponent(page) +}) diff --git a/examples/react/authenticated-routes-firebase/package.json b/examples/react/authenticated-routes-firebase/package.json index ac20a7fa3c..0d1e392234 100644 --- a/examples/react/authenticated-routes-firebase/package.json +++ b/examples/react/authenticated-routes-firebase/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "autoprefixer": "^10.4.20", "firebase": "^11.4.0", "postcss": "^8.5.1", diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json index 5bedf2de76..5d2f0b46cf 100644 --- a/examples/react/authenticated-routes/package.json +++ b/examples/react/authenticated-routes/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json index 4ac49178e6..d8aa50df77 100644 --- a/examples/react/basic-default-search-params/package.json +++ b/examples/react/basic-default-search-params/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-devtools-panel/package.json b/examples/react/basic-devtools-panel/package.json index 6925019d18..a8094147ef 100644 --- a/examples/react/basic-devtools-panel/package.json +++ b/examples/react/basic-devtools-panel/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "@tanstack/react-query-devtools": "^5.67.2", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json index 53e01ca07c..ba21856b27 100644 --- a/examples/react/basic-file-based/package.json +++ b/examples/react/basic-file-based/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-non-nested-devtools/package.json b/examples/react/basic-non-nested-devtools/package.json index 2b45e427a8..a52535c634 100644 --- a/examples/react/basic-non-nested-devtools/package.json +++ b/examples/react/basic-non-nested-devtools/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json index 0376b204be..24c517a6ce 100644 --- a/examples/react/basic-react-query-file-based/package.json +++ b/examples/react/basic-react-query-file-based/package.json @@ -11,9 +11,9 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query/package.json b/examples/react/basic-react-query/package.json index 8cdbbd949c..358505769a 100644 --- a/examples/react/basic-react-query/package.json +++ b/examples/react/basic-react-query/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json index 74062d8926..b18ac41829 100644 --- a/examples/react/basic-ssr-file-based/package.json +++ b/examples/react/basic-ssr-file-based/package.json @@ -11,10 +11,10 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "get-port": "^7.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json index a29711ecc0..cde25b939e 100644 --- a/examples/react/basic-ssr-streaming-file-based/package.json +++ b/examples/react/basic-ssr-streaming-file-based/package.json @@ -11,10 +11,10 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "get-port": "^7.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json index ff649aeac9..2f5d324344 100644 --- a/examples/react/basic-virtual-file-based/package.json +++ b/examples/react/basic-virtual-file-based/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "@tanstack/virtual-file-routes": "^1.115.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json index b8b045f74c..67bbb2d32c 100644 --- a/examples/react/basic-virtual-inside-file-based/package.json +++ b/examples/react/basic-virtual-inside-file-based/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "@tanstack/virtual-file-routes": "^1.115.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json index 2f2c75d4b0..795f71811f 100644 --- a/examples/react/basic/package.json +++ b/examples/react/basic/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/deferred-data/package.json b/examples/react/deferred-data/package.json index 07d7ca2b8c..d269b3cec3 100644 --- a/examples/react/deferred-data/package.json +++ b/examples/react/deferred-data/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json index 6fbb0f6ae3..a4cde92147 100644 --- a/examples/react/kitchen-sink-file-based/package.json +++ b/examples/react/kitchen-sink-file-based/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json index 01c44d2c42..6ba8d9fc9b 100644 --- a/examples/react/kitchen-sink-react-query-file-based/package.json +++ b/examples/react/kitchen-sink-react-query-file-based/package.json @@ -11,9 +11,9 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query/package.json b/examples/react/kitchen-sink-react-query/package.json index 24629dc4d7..55339a6ed3 100644 --- a/examples/react/kitchen-sink-react-query/package.json +++ b/examples/react/kitchen-sink-react-query/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink/package.json b/examples/react/kitchen-sink/package.json index fcda245ccc..981b98a3b3 100644 --- a/examples/react/kitchen-sink/package.json +++ b/examples/react/kitchen-sink/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json index b028ce0f14..af80ae8c88 100644 --- a/examples/react/large-file-based/package.json +++ b/examples/react/large-file-based/package.json @@ -12,9 +12,9 @@ }, "dependencies": { "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/location-masking/package.json b/examples/react/location-masking/package.json index 76974a7fd6..525409af6c 100644 --- a/examples/react/location-masking/package.json +++ b/examples/react/location-masking/package.json @@ -11,8 +11,8 @@ "dependencies": { "@radix-ui/react-dialog": "^1.1.6", "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/navigation-blocking/package.json b/examples/react/navigation-blocking/package.json index a02bbd11c7..95ccd28f78 100644 --- a/examples/react/navigation-blocking/package.json +++ b/examples/react/navigation-blocking/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-esbuild-file-based/package.json b/examples/react/quickstart-esbuild-file-based/package.json index a70374a87d..8485c64e08 100644 --- a/examples/react/quickstart-esbuild-file-based/package.json +++ b/examples/react/quickstart-esbuild-file-based/package.json @@ -9,9 +9,9 @@ "start": "dev" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json index f5ee2a99ee..5b28e0cccd 100644 --- a/examples/react/quickstart-file-based/package.json +++ b/examples/react/quickstart-file-based/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index 950bfd7b4f..a32b20c5e7 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -8,8 +8,8 @@ "preview": "rsbuild preview" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "postcss": "^8.5.1", @@ -19,7 +19,7 @@ "devDependencies": { "@rsbuild/core": "1.2.4", "@rsbuild/plugin-react": "1.1.0", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "typescript": "^5.6.2" diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json index 6dec44454f..480dbcc0a1 100644 --- a/examples/react/quickstart-webpack-file-based/package.json +++ b/examples/react/quickstart-webpack-file-based/package.json @@ -7,14 +7,14 @@ "build": "webpack build && tsc --noEmit" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@swc/core": "^1.10.15", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "html-webpack-plugin": "^5.6.3", diff --git a/examples/react/quickstart/package.json b/examples/react/quickstart/package.json index 68ee7d2a5d..4c5493056f 100644 --- a/examples/react/quickstart/package.json +++ b/examples/react/quickstart/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "postcss": "^8.5.1", diff --git a/examples/react/router-monorepo-react-query/package.json b/examples/react/router-monorepo-react-query/package.json index d63d9c1272..9d31da4c45 100644 --- a/examples/react/router-monorepo-react-query/package.json +++ b/examples/react/router-monorepo-react-query/package.json @@ -12,9 +12,9 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-react-query/packages/app/package.json b/examples/react/router-monorepo-react-query/packages/app/package.json index f108f10e39..4741472b55 100644 --- a/examples/react/router-monorepo-react-query/packages/app/package.json +++ b/examples/react/router-monorepo-react-query/packages/app/package.json @@ -20,7 +20,7 @@ "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^4.3.4", "typescript": "^5.7.2", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router-devtools": "^1.120.15", "postcss": "^8.5.1", "autoprefixer": "^10.4.20", "tailwindcss": "^3.4.17", diff --git a/examples/react/router-monorepo-react-query/packages/router/package.json b/examples/react/router-monorepo-react-query/packages/router/package.json index 87e0b53e46..73cc5a2c84 100644 --- a/examples/react/router-monorepo-react-query/packages/router/package.json +++ b/examples/react/router-monorepo-react-query/packages/router/package.json @@ -10,8 +10,8 @@ "dependencies": { "@tanstack/history": "^1.115.0", "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "@router-mono-react-query/post-query": "workspace:*", "redaxios": "^0.5.1", "zod": "^3.24.2", diff --git a/examples/react/router-monorepo-simple-lazy/package.json b/examples/react/router-monorepo-simple-lazy/package.json index 8805fcadee..371c604584 100644 --- a/examples/react/router-monorepo-simple-lazy/package.json +++ b/examples/react/router-monorepo-simple-lazy/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple-lazy/packages/app/package.json b/examples/react/router-monorepo-simple-lazy/packages/app/package.json index 6e8e6a773b..3185f9013a 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/app/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/app/package.json @@ -19,7 +19,7 @@ "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^4.3.4", "typescript": "^5.7.2", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router-devtools": "^1.120.15", "postcss": "^8.5.1", "autoprefixer": "^10.4.20", "tailwindcss": "^3.4.17", diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/package.json b/examples/react/router-monorepo-simple-lazy/packages/router/package.json index 4ab5a93131..a0e2eaebcc 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.115.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "redaxios": "^0.5.1", "zod": "^3.24.2", "react": "^19.0.0", diff --git a/examples/react/router-monorepo-simple/package.json b/examples/react/router-monorepo-simple/package.json index 35e73aefb1..1219700f5d 100644 --- a/examples/react/router-monorepo-simple/package.json +++ b/examples/react/router-monorepo-simple/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple/packages/app/package.json b/examples/react/router-monorepo-simple/packages/app/package.json index 3b1f2e4cb1..7c0e9b3131 100644 --- a/examples/react/router-monorepo-simple/packages/app/package.json +++ b/examples/react/router-monorepo-simple/packages/app/package.json @@ -19,7 +19,7 @@ "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^4.3.4", "typescript": "^5.7.2", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router-devtools": "^1.120.15", "vite": "^6.1.0", "postcss": "^8.5.1", "autoprefixer": "^10.4.20", diff --git a/examples/react/router-monorepo-simple/packages/router/package.json b/examples/react/router-monorepo-simple/packages/router/package.json index 8f110580a1..bb144d2c64 100644 --- a/examples/react/router-monorepo-simple/packages/router/package.json +++ b/examples/react/router-monorepo-simple/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.115.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "redaxios": "^0.5.1", "zod": "^3.24.2", "react": "^19.0.0", diff --git a/examples/react/scroll-restoration/package.json b/examples/react/scroll-restoration/package.json index 8e07cf2589..1a48183b0f 100644 --- a/examples/react/scroll-restoration/package.json +++ b/examples/react/scroll-restoration/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", + "@tanstack/react-router": "^1.120.15", "@tanstack/react-virtual": "^3.13.0", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "postcss": "^8.5.1", diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json index af25aedde5..0ad9631bf0 100644 --- a/examples/react/search-validator-adapters/package.json +++ b/examples/react/search-validator-adapters/package.json @@ -10,13 +10,13 @@ "test:unit": "vitest" }, "dependencies": { - "@tanstack/arktype-adapter": "^1.120.3", + "@tanstack/arktype-adapter": "^1.120.15", "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", - "@tanstack/valibot-adapter": "^1.120.3", - "@tanstack/zod-adapter": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", + "@tanstack/valibot-adapter": "^1.120.15", + "@tanstack/zod-adapter": "^1.120.15", "arktype": "^2.1.7", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index c87ecebfde..4f7d13238f 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -9,9 +9,9 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "vinxi": "0.5.3", diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 554777cc4e..9f66d3cca6 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@prisma/client": "5.22.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "prisma": "^5.22.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 9c27d1a42d..478ddb2ca3 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -11,10 +11,10 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-with-query": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-with-query": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-rsc/package.json b/examples/react/start-basic-rsc/package.json index e526759a5b..44e8814d03 100644 --- a/examples/react/start-basic-rsc/package.json +++ b/examples/react/start-basic-rsc/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@babel/plugin-syntax-typescript": "^7.25.9", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index 5481062895..4b765bd887 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -9,9 +9,9 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index b7475e12c3..4da18d8358 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -9,9 +9,9 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^2.6.0", diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 81c2bb8456..85a1657f07 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@clerk/tanstack-react-start": "0.12.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index ab54a780df..1425abe030 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -14,10 +14,10 @@ "@convex-dev/react-query": "0.0.0-alpha.8", "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-with-query": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-with-query": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index 5597354b06..da7c9b84fd 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -9,9 +9,9 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "vinxi": "0.5.3" diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index a9c4ced79d..8cc9fe2408 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -12,9 +12,9 @@ }, "dependencies": { "@tanstack/react-query": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index e327c367b7..0df775b69c 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -14,9 +14,9 @@ "@emotion/styled": "11.14.0", "@fontsource-variable/roboto": "5.2.5", "@mui/material": "6.4.7", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-start": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-start": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "vinxi": "0.5.3", diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 45d347e083..8ad4c032c6 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -15,9 +15,9 @@ "dependencies": { "@supabase/ssr": "^0.5.2", "@supabase/supabase-js": "^2.48.1", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "vinxi": "0.5.3" diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 8e30f6062d..f9de19d4db 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -11,10 +11,10 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-with-query": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-with-query": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index 9b4f6be98b..d67e8026ef 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -14,9 +14,9 @@ "license": "ISC", "dependencies": { "@radix-ui/themes": "^3.2.1", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "@workos-inc/node": "^7.45.0", "iron-session": "^8.0.4", "jose": "^6.0.10", diff --git a/examples/react/view-transitions/package.json b/examples/react/view-transitions/package.json index 3b99a514bc..a12ffe5967 100644 --- a/examples/react/view-transitions/package.json +++ b/examples/react/view-transitions/package.json @@ -9,9 +9,9 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/with-framer-motion/package.json b/examples/react/with-framer-motion/package.json index 3bdb7d217c..166598dbf2 100644 --- a/examples/react/with-framer-motion/package.json +++ b/examples/react/with-framer-motion/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", "framer-motion": "^11.18.2", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json index bc9b26032a..5c72a40d4d 100644 --- a/examples/react/with-trpc-react-query/package.json +++ b/examples/react/with-trpc-react-query/package.json @@ -10,10 +10,10 @@ "dependencies": { "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.66.0", - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "@trpc/client": "11.0.0-rc.772", "@trpc/server": "11.0.0-rc.772", "@trpc/tanstack-react-query": "11.0.0-rc.772", diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json index 2414353240..6150e096c0 100644 --- a/examples/react/with-trpc/package.json +++ b/examples/react/with-trpc/package.json @@ -8,10 +8,10 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/react-router": "^1.120.3", - "@tanstack/react-router-devtools": "^1.120.3", - "@tanstack/router-plugin": "^1.120.3", - "@tanstack/react-start": "^1.120.3", + "@tanstack/react-router": "^1.120.15", + "@tanstack/react-router-devtools": "^1.120.15", + "@tanstack/router-plugin": "^1.120.15", + "@tanstack/react-start": "^1.120.15", "@trpc/client": "11.0.0-rc.772", "@trpc/server": "11.0.0-rc.772", "react": "^19.0.0", diff --git a/examples/solid/basic-devtools-panel/package.json b/examples/solid/basic-devtools-panel/package.json index ecc30cd748..b93c8bf545 100644 --- a/examples/solid/basic-devtools-panel/package.json +++ b/examples/solid/basic-devtools-panel/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "solid-js": "^1.9.5", "redaxios": "^0.5.1", "postcss": "^8.5.1", diff --git a/examples/solid/basic-file-based/package.json b/examples/solid/basic-file-based/package.json index 2c97989801..5d5f1e62ed 100644 --- a/examples/solid/basic-file-based/package.json +++ b/examples/solid/basic-file-based/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "autoprefixer": "^10.4.20", "postcss": "^8.5.1", "redaxios": "^0.5.1", @@ -19,7 +19,7 @@ "zod": "^3.24.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "typescript": "^5.7.2", "vite": "^6.1.0", "vite-plugin-solid": "^2.11.2" diff --git a/examples/solid/basic-non-nested-devtools/package.json b/examples/solid/basic-non-nested-devtools/package.json index 9f0ae1d786..a1ba1ce494 100644 --- a/examples/solid/basic-non-nested-devtools/package.json +++ b/examples/solid/basic-non-nested-devtools/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "redaxios": "^0.5.1", "postcss": "^8.5.1", "solid-js": "^1.9.5", diff --git a/examples/solid/basic-solid-query-file-based/package.json b/examples/solid/basic-solid-query-file-based/package.json index f074c64838..e5f6fee4f6 100644 --- a/examples/solid/basic-solid-query-file-based/package.json +++ b/examples/solid/basic-solid-query-file-based/package.json @@ -12,8 +12,8 @@ "dependencies": { "@tanstack/solid-query": "^5.71.9", "@tanstack/solid-query-devtools": "^5.71.9", - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "solid-js": "^1.9.5", "redaxios": "^0.5.1", "postcss": "^8.5.1", @@ -22,7 +22,7 @@ "zod": "^3.24.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "typescript": "^5.7.2", "vite": "^6.1.0", "vite-plugin-solid": "^2.11.2" diff --git a/examples/solid/basic-solid-query/package.json b/examples/solid/basic-solid-query/package.json index 72aa31853b..3d59a21210 100644 --- a/examples/solid/basic-solid-query/package.json +++ b/examples/solid/basic-solid-query/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tanstack/solid-query": "^5.71.9", "@tanstack/solid-query-devtools": "^5.71.9", - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "solid-js": "^1.9.5", "redaxios": "^0.5.1", "postcss": "^8.5.1", @@ -20,7 +20,7 @@ "tailwindcss": "^3.4.17" }, "devDependencies": { - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "typescript": "^5.7.2", "vite": "^6.1.0", "vite-plugin-solid": "^2.11.2" diff --git a/examples/solid/basic/package.json b/examples/solid/basic/package.json index c602e83393..4b82cffef4 100644 --- a/examples/solid/basic/package.json +++ b/examples/solid/basic/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "redaxios": "^0.5.1", "postcss": "^8.5.1", "solid-js": "^1.9.5", diff --git a/examples/solid/kitchen-sink-file-based/package.json b/examples/solid/kitchen-sink-file-based/package.json index 190dd6540c..4c6600317b 100644 --- a/examples/solid/kitchen-sink-file-based/package.json +++ b/examples/solid/kitchen-sink-file-based/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "immer": "^10.1.1", "solid-js": "^1.9.5", "redaxios": "^0.5.1", @@ -20,7 +20,7 @@ "zod": "^3.24.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "typescript": "^5.7.2", "vite": "^6.1.0", "vite-plugin-solid": "^2.11.2" diff --git a/examples/solid/quickstart-file-based/package.json b/examples/solid/quickstart-file-based/package.json index e22354139e..1f08585843 100644 --- a/examples/solid/quickstart-file-based/package.json +++ b/examples/solid/quickstart-file-based/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", "autoprefixer": "^10.4.20", "postcss": "^8.5.1", "redaxios": "^0.5.1", @@ -19,7 +19,7 @@ "zod": "^3.24.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.120.3", + "@tanstack/router-plugin": "^1.120.15", "typescript": "^5.7.2", "vite": "^6.1.0", "vite-plugin-solid": "^2.11.2" diff --git a/examples/solid/start-bare/package.json b/examples/solid/start-bare/package.json index 2a94ab0a76..ce201c9b9c 100644 --- a/examples/solid/start-bare/package.json +++ b/examples/solid/start-bare/package.json @@ -9,9 +9,9 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", - "@tanstack/solid-start": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", + "@tanstack/solid-start": "^1.120.15", "solid-js": "^1.9.5", "redaxios": "^0.5.1", "tailwind-merge": "^2.6.0", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 9503c2499a..c80dd21dd4 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -9,9 +9,9 @@ "start": "vinxi start" }, "dependencies": { - "@tanstack/solid-router": "^1.120.3", - "@tanstack/solid-router-devtools": "^1.120.3", - "@tanstack/solid-start": "^1.120.3", + "@tanstack/solid-router": "^1.120.15", + "@tanstack/solid-router-devtools": "^1.120.15", + "@tanstack/solid-start": "^1.120.15", "solid-js": "^1.9.5", "redaxios": "^0.5.1", "tailwind-merge": "^2.6.0", diff --git a/package.json b/package.json index 9ac5606a3a..42238c2ad3 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "gpt-generate": "node gpt/generate.js", "set-ts-version": "node scripts/set-ts-version.js", "labeler-generate": "node scripts/generateLabelerConfig.mjs", - "cleanup-empty-packages": "node scripts/cleanup-empty-packages.mjs" + "cleanup-empty-packages": "node scripts/cleanup-empty-packages.mjs", + "verify-links": "node scripts/verify-links.ts" }, "devDependencies": { "@arethetypeswrong/cli": "^0.17.3", @@ -47,7 +48,9 @@ "eslint": "^9.22.0", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-unused-imports": "^4.1.4", + "fast-glob": "^3.3.3", "jsdom": "^25.0.1", + "markdown-link-extractor": "^4.0.2", "nx": "20.8.1", "prettier": "^3.5.0", "publint": "^0.3.4", @@ -57,13 +60,13 @@ "rimraf": "^6.0.1", "tinyglobby": "^0.2.12", "typescript": "^5.8.2", - "vite": "6.1.4", - "vitest": "^3.0.6", "typescript53": "npm:typescript@5.3", "typescript54": "npm:typescript@5.4", "typescript55": "npm:typescript@5.5", "typescript56": "npm:typescript@5.6", - "typescript57": "npm:typescript@5.7" + "typescript57": "npm:typescript@5.7", + "vite": "6.1.4", + "vitest": "^3.0.6" }, "resolutions": { "use-sync-external-store": "1.2.2" diff --git a/packages/arktype-adapter/package.json b/packages/arktype-adapter/package.json index ebe26da7e3..a199a36a9d 100644 --- a/packages/arktype-adapter/package.json +++ b/packages/arktype-adapter/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/arktype-adapter", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-router-devtools/package.json b/packages/react-router-devtools/package.json index 940def08b5..5b0b59e923 100644 --- a/packages/react-router-devtools/package.json +++ b/packages/react-router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-router-devtools", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-router-with-query/package.json b/packages/react-router-with-query/package.json index fbfa01431f..1067626364 100644 --- a/packages/react-router-with-query/package.json +++ b/packages/react-router-with-query/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-router-with-query", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-router/package.json b/packages/react-router/package.json index caac296dc9..d5afb9f3c0 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-router", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", @@ -38,7 +38,9 @@ "test:perf": "vitest bench", "test:perf:dev": "pnpm run test:perf --watch --hideSkippedTests", "test:build": "publint --strict && attw --ignore-rules no-resolution --pack .", - "build": "vite build" + "build": "pnpm run build:lib && pnpm run build:llm", + "build:lib": "vite build", + "build:llm": "node ../../scripts/llms-generate.mjs react-router && tsc -p ./llms/tsconfig.json" }, "type": "module", "types": "dist/esm/index.d.ts", @@ -55,7 +57,13 @@ "default": "./dist/cjs/index.cjs" } }, - "./package.json": "./package.json" + "./package.json": "./package.json", + "./llms": { + "import": { + "types": "./dist/llms/index.d.ts", + "default": "./dist/llms/index.js" + } + } }, "sideEffects": false, "files": [ @@ -81,6 +89,7 @@ "combinate": "^1.1.11", "react": "^19.0.0", "react-dom": "^19.0.0", + "vibe-rules": "^0.2.55", "zod": "^3.24.2" }, "peerDependencies": { diff --git a/packages/react-router/src/ScrollRestoration.tsx b/packages/react-router/src/ScrollRestoration.tsx index 063d93914f..71d472723d 100644 --- a/packages/react-router/src/ScrollRestoration.tsx +++ b/packages/react-router/src/ScrollRestoration.tsx @@ -64,6 +64,6 @@ export function useElementScrollRestoration( } const restoreKey = getKey(router.latestLocation) - const byKey = scrollRestorationCache.state[restoreKey] + const byKey = scrollRestorationCache?.state[restoreKey] return byKey?.[elementSelector] } diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 3023a10288..301f381a86 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -364,8 +364,8 @@ type UseLinkReactProps = TComp extends keyof React.JSX.IntrinsicElements > & React.RefAttributes< TComp extends - | React.FC<{ ref: infer TRef }> - | React.Component<{ ref: infer TRef }> + | React.FC<{ ref: React.Ref }> + | React.Component<{ ref: React.Ref }> ? TRef : never > diff --git a/packages/react-router/src/route.tsx b/packages/react-router/src/route.tsx index 8ec238a5b3..d3730e819e 100644 --- a/packages/react-router/src/route.tsx +++ b/packages/react-router/src/route.tsx @@ -25,9 +25,11 @@ import type { ResolveFullPath, ResolveId, ResolveParams, + RootRoute as RootRouteCore, RootRouteId, RootRouteOptions, RouteConstraints, + Route as RouteCore, RouteIds, RouteMask, RouteOptions, @@ -148,43 +150,62 @@ export class RouteApi< } export class Route< - in out TParentRoute extends RouteConstraints['TParentRoute'] = AnyRoute, - in out TPath extends RouteConstraints['TPath'] = '/', - in out TFullPath extends RouteConstraints['TFullPath'] = ResolveFullPath< - TParentRoute, - TPath - >, - in out TCustomId extends RouteConstraints['TCustomId'] = string, - in out TId extends RouteConstraints['TId'] = ResolveId< + in out TParentRoute extends RouteConstraints['TParentRoute'] = AnyRoute, + in out TPath extends RouteConstraints['TPath'] = '/', + in out TFullPath extends RouteConstraints['TFullPath'] = ResolveFullPath< + TParentRoute, + TPath + >, + in out TCustomId extends RouteConstraints['TCustomId'] = string, + in out TId extends RouteConstraints['TId'] = ResolveId< + TParentRoute, + TCustomId, + TPath + >, + in out TSearchValidator = undefined, + in out TParams = ResolveParams, + in out TRouterContext = AnyContext, + in out TRouteContextFn = AnyContext, + in out TBeforeLoadFn = AnyContext, + in out TLoaderDeps extends Record = {}, + in out TLoaderFn = undefined, + in out TChildren = unknown, + in out TFileRouteTypes = unknown, + > + extends BaseRoute< TParentRoute, + TPath, + TFullPath, TCustomId, - TPath - >, - in out TSearchValidator = undefined, - in out TParams = ResolveParams, - in out TRouterContext = AnyContext, - in out TRouteContextFn = AnyContext, - in out TBeforeLoadFn = AnyContext, - in out TLoaderDeps extends Record = {}, - in out TLoaderFn = undefined, - in out TChildren = unknown, - in out TFileRouteTypes = unknown, -> extends BaseRoute< - TParentRoute, - TPath, - TFullPath, - TCustomId, - TId, - TSearchValidator, - TParams, - TRouterContext, - TRouteContextFn, - TBeforeLoadFn, - TLoaderDeps, - TLoaderFn, - TChildren, - TFileRouteTypes -> { + TId, + TSearchValidator, + TParams, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > + implements + RouteCore< + TParentRoute, + TPath, + TFullPath, + TCustomId, + TId, + TSearchValidator, + TParams, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > +{ /** * @deprecated Use the `createRoute` function instead. */ @@ -364,24 +385,37 @@ export function createRootRouteWithContext() { export const rootRouteWithContext = createRootRouteWithContext export class RootRoute< - in out TSearchValidator = undefined, - in out TRouterContext = {}, - in out TRouteContextFn = AnyContext, - in out TBeforeLoadFn = AnyContext, - in out TLoaderDeps extends Record = {}, - in out TLoaderFn = undefined, - in out TChildren = unknown, - in out TFileRouteTypes = unknown, -> extends BaseRootRoute< - TSearchValidator, - TRouterContext, - TRouteContextFn, - TBeforeLoadFn, - TLoaderDeps, - TLoaderFn, - TChildren, - TFileRouteTypes -> { + in out TSearchValidator = undefined, + in out TRouterContext = {}, + in out TRouteContextFn = AnyContext, + in out TBeforeLoadFn = AnyContext, + in out TLoaderDeps extends Record = {}, + in out TLoaderFn = undefined, + in out TChildren = unknown, + in out TFileRouteTypes = unknown, + > + extends BaseRootRoute< + TSearchValidator, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > + implements + RootRouteCore< + TSearchValidator, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > +{ /** * @deprecated `RootRoute` is now an internal implementation detail. Use `createRootRoute()` instead. */ diff --git a/packages/react-router/tests/Scripts.test.tsx b/packages/react-router/tests/Scripts.test.tsx index 13a9f0571c..972a4753b1 100644 --- a/packages/react-router/tests/Scripts.test.tsx +++ b/packages/react-router/tests/Scripts.test.tsx @@ -123,7 +123,7 @@ describe('ssr HeadContent', () => { }, { name: 'description', - content: loaderData.description, + content: loaderData?.description, }, { name: 'image', @@ -160,7 +160,7 @@ describe('ssr HeadContent', () => { }, { name: 'description', - content: loaderData.description, + content: loaderData?.description, }, { name: 'last-modified', diff --git a/packages/react-router/tests/link.test-d.tsx b/packages/react-router/tests/link.test-d.tsx index 7c58c25837..84491a2c92 100644 --- a/packages/react-router/tests/link.test-d.tsx +++ b/packages/react-router/tests/link.test-d.tsx @@ -1,4 +1,5 @@ import { expectTypeOf, test } from 'vitest' +import React from 'react' import { Link, createLink, @@ -9,6 +10,7 @@ import { } from '../src' import type { CreateLinkProps, + LinkComponent, ResolveRelativePath, SearchSchemaInput, } from '../src' @@ -4005,6 +4007,39 @@ test('when passing a component with props to createLink and navigating to the ro createLink((props) => expectTypeOf(props).toEqualTypeOf()) }) +test('that createLink refs forward correctly', () => { + // copied from: https://tanstack.com/router/latest/docs/framework/react/guide/custom-link#basic-example + interface BasicLinkProps + extends React.AnchorHTMLAttributes {} + const BasicLinkComponent = React.forwardRef< + HTMLAnchorElement, + BasicLinkProps + >((props, ref) => { + return ( + + ) + }) + const CreatedLinkComponent = createLink(BasicLinkComponent) + const CustomLink: LinkComponent = (props) => { + return + } + + expectTypeOf(BasicLinkComponent) + .parameter(0) + .toHaveProperty('ref') + .toEqualTypeOf | undefined>() + + expectTypeOf(CreatedLinkComponent) + .parameter(0) + .toHaveProperty('ref') + .toEqualTypeOf[0]['ref']>() + + expectTypeOf(CustomLink) + .parameter(0) + .toHaveProperty('ref') + .toEqualTypeOf[0]['ref']>() +}) + test('ResolveRelativePath', () => { expectTypeOf>().toEqualTypeOf<'/posts'>() diff --git a/packages/react-router/tests/route.test.tsx b/packages/react-router/tests/route.test.tsx index 9c6fbbfc0e..9190d1269d 100644 --- a/packages/react-router/tests/route.test.tsx +++ b/packages/react-router/tests/route.test.tsx @@ -8,6 +8,7 @@ import { createRoute, createRouter, getRouteApi, + notFound, } from '../src' afterEach(() => { @@ -272,6 +273,85 @@ describe('route.head', () => { ]) }) + test('meta is set when loader throws notFound', async () => { + const rootRoute = createRootRoute({ + head: () => ({ + meta: [ + { title: 'Root' }, + { + charSet: 'utf-8', + }, + ], + }), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + head: () => ({ + meta: [{ title: 'Index' }], + }), + loader: async () => { + throw notFound() + }, + component: () =>
Index
, + }) + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ routeTree }) + render() + expect(await screen.findByText('Not Found')).toBeInTheDocument() + + const metaState = router.state.matches.map((m) => m.meta) + expect(metaState).toEqual([ + [ + { title: 'Root' }, + { + charSet: 'utf-8', + }, + ], + [{ title: 'Index' }], + ]) + }) + + test('meta is set when loader throws an error', async () => { + const rootRoute = createRootRoute({ + head: () => ({ + meta: [ + { title: 'Root' }, + { + charSet: 'utf-8', + }, + ], + }), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + head: () => ({ + meta: [{ title: 'Index' }], + }), + loader: async () => { + throw new Error('Fly, you fools!') + }, + component: () =>
Index
, + }) + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ routeTree }) + render() + + expect(await screen.findByText('Fly, you fools!')).toBeInTheDocument() + + const metaState = router.state.matches.map((m) => m.meta) + expect(metaState).toEqual([ + [ + { title: 'Root' }, + { + charSet: 'utf-8', + }, + ], + [{ title: 'Index' }], + ]) + }) + test('scripts', async () => { const rootRoute = createRootRoute({ head: () => ({ diff --git a/packages/react-start-client/package.json b/packages/react-start-client/package.json index a83ed07e55..a0820cbf17 100644 --- a/packages/react-start-client/package.json +++ b/packages/react-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-client", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-config/package.json b/packages/react-start-config/package.json index 23efef7bfd..51065635e4 100644 --- a/packages/react-start-config/package.json +++ b/packages/react-start-config/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-config", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-router-manifest/package.json b/packages/react-start-router-manifest/package.json index f367442a9c..443df44ddb 100644 --- a/packages/react-start-router-manifest/package.json +++ b/packages/react-start-router-manifest/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-router-manifest", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-server/package.json b/packages/react-start-server/package.json index 9bbf439abe..f358769d62 100644 --- a/packages/react-start-server/package.json +++ b/packages/react-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-server", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/package.json b/packages/react-start/package.json index a08b503980..0ab7b07801 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-cli/package.json b/packages/router-cli/package.json index 3d620859f8..5249b59b71 100644 --- a/packages/router-cli/package.json +++ b/packages/router-cli/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-cli", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-core/package.json b/packages/router-core/package.json index ce60e3f821..a10ebb3254 100644 --- a/packages/router-core/package.json +++ b/packages/router-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-core", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-core/src/RouterProvider.ts b/packages/router-core/src/RouterProvider.ts index 1c2b42ef21..972596bb12 100644 --- a/packages/router-core/src/RouterProvider.ts +++ b/packages/router-core/src/RouterProvider.ts @@ -1,11 +1,7 @@ import type { NavigateOptions, ToOptions } from './link' import type { ParsedLocation } from './location' import type { RoutePaths } from './routeInfo' -import type { - AnyRouter, - RegisteredRouter, - ViewTransitionOptions, -} from './router' +import type { RegisteredRouter, ViewTransitionOptions } from './router' export interface MatchLocation { to?: string | number | null @@ -37,7 +33,7 @@ export type NavigateFn = < ) => Promise | void export type BuildLocationFn = < - TRouter extends AnyRouter, + TRouter extends RegisteredRouter, TTo extends string | undefined, TFrom extends RoutePaths | string = string, TMaskFrom extends RoutePaths | string = TFrom, diff --git a/packages/router-core/src/index.ts b/packages/router-core/src/index.ts index 4988400855..6759c0e22e 100644 --- a/packages/router-core/src/index.ts +++ b/packages/router-core/src/index.ts @@ -198,6 +198,7 @@ export type { RouteAddChildrenFn, RouteAddFileChildrenFn, RouteAddFileTypesFn, + RootRoute, } from './route' export { diff --git a/packages/router-core/src/not-found.ts b/packages/router-core/src/not-found.ts index 370a9c43f9..6415563b75 100644 --- a/packages/router-core/src/not-found.ts +++ b/packages/router-core/src/not-found.ts @@ -27,3 +27,14 @@ export function notFound(options: NotFoundError = {}) { export function isNotFound(obj: any): obj is NotFoundError { return !!obj?.isNotFound } + +export const INVARIANT_SOURCE_NOT_FOUND = 'notFound' + +export function isVariantNotFoundError(error: any) { + return ( + error && + typeof error === 'object' && + 'invariantSource' in error && + error.invariantSource === INVARIANT_SOURCE_NOT_FOUND + ) +} diff --git a/packages/router-core/src/qss.ts b/packages/router-core/src/qss.ts index cb1912b846..2611389f37 100644 --- a/packages/router-core/src/qss.ts +++ b/packages/router-core/src/qss.ts @@ -7,7 +7,6 @@ * (namely URLSearchParams) and TypeScript while still * maintaining the original functionality and interface. */ -import { hasUriEncodedChars } from './utils' /** * Encodes an object into a query string. @@ -42,11 +41,8 @@ export function encode(obj: any, pfx?: string) { * // Example input: toValue("123") * // Expected output: 123 */ -function toValue(mix: any) { - if (!mix) return '' - const str = hasUriEncodedChars(mix) - ? decodeURIComponent(mix) - : decodeURIComponent(encodeURIComponent(mix)) +function toValue(str: unknown) { + if (!str) return '' if (str === 'false') return false if (str === 'true') return true diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index 6344357790..773dc91bcf 100644 --- a/packages/router-core/src/route.ts +++ b/packages/router-core/src/route.ts @@ -621,7 +621,24 @@ export interface Route< TBeforeLoadFn >, ) => this - lazy: RouteLazyFn + lazy: RouteLazyFn< + Route< + TParentRoute, + TPath, + TFullPath, + TCustomId, + TId, + TSearchValidator, + TParams, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > + > addChildren: RouteAddChildrenFn< TParentRoute, TPath, @@ -960,7 +977,7 @@ type AssetFnContextOptions< TLoaderDeps > params: ResolveAllParamsFromParent - loaderData: ResolveLoaderData + loaderData?: ResolveLoaderData } export interface DefaultUpdatableRouteOptionsExtensions { @@ -1070,9 +1087,20 @@ export interface UpdatableRouteOptions< TLoaderDeps >, ) => void - headers?: (ctx: { - loaderData: ResolveLoaderData - }) => Record + headers?: ( + ctx: AssetFnContextOptions< + TRouteId, + TFullPath, + TParentRoute, + TParams, + TSearchValidator, + TLoaderFn, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps + >, + ) => Record head?: ( ctx: AssetFnContextOptions< TRouteId, @@ -1270,24 +1298,7 @@ export class BaseRoute< in out TLoaderFn = undefined, in out TChildren = unknown, in out TFileRouteTypes = unknown, -> implements - Route< - TParentRoute, - TPath, - TFullPath, - TCustomId, - TId, - TSearchValidator, - TParams, - TRouterContext, - TRouteContextFn, - TBeforeLoadFn, - TLoaderDeps, - TLoaderFn, - TChildren, - TFileRouteTypes - > -{ +> { isRoot: TParentRoute extends AnyRoute ? true : false options: RouteOptions< TParentRoute, @@ -1449,6 +1460,16 @@ export class BaseRoute< this._ssr = options?.ssr ?? opts.defaultSsr ?? true } + clone = (other: typeof this) => { + this._path = other._path + this._id = other._id + this._fullPath = other._fullPath + this._to = other._to + this._ssr = other._ssr + this.options.getParentRoute = other.options.getParentRoute + this.children = other.children + } + addChildren: RouteAddChildrenFn< TParentRoute, TPath, @@ -1562,7 +1583,24 @@ export class BaseRoute< return this } - lazy: RouteLazyFn = (lazyFn) => { + lazy: RouteLazyFn< + Route< + TParentRoute, + TPath, + TFullPath, + TCustomId, + TId, + TSearchValidator, + TParams, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > + > = (lazyFn) => { this.lazyFn = lazyFn return this } @@ -1580,6 +1618,32 @@ export class BaseRouteApi { } } +export interface RootRoute< + in out TSearchValidator = undefined, + in out TRouterContext = {}, + in out TRouteContextFn = AnyContext, + in out TBeforeLoadFn = AnyContext, + in out TLoaderDeps extends Record = {}, + in out TLoaderFn = undefined, + in out TChildren = unknown, + in out TFileRouteTypes = unknown, +> extends Route< + any, // TParentRoute + '/', // TPath + '/', // TFullPath + string, // TCustomId + RootRouteId, // TId + TSearchValidator, // TSearchValidator + {}, // TParams + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, // TChildren + TFileRouteTypes + > {} + export class BaseRootRoute< in out TSearchValidator = undefined, in out TRouterContext = {}, diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index 3a510d6506..d265600894 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -24,7 +24,11 @@ import { trimPathLeft, trimPathRight, } from './path' -import { isNotFound } from './not-found' +import { + INVARIANT_SOURCE_NOT_FOUND, + isNotFound, + isVariantNotFoundError, +} from './not-found' import { setupScrollRestoration } from './scroll-restoration' import { defaultParseSearch, defaultStringifySearch } from './searchParams' import { rootRouteId } from './root' @@ -390,7 +394,7 @@ export interface RouterOptions< * * @default ['window'] */ - scrollToTopSelectors?: Array + scrollToTopSelectors?: Array Element | null | undefined)> } export interface RouterState< @@ -882,7 +886,6 @@ export class RouterCore< } if ( - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition !this.history || (this.options.history && this.options.history !== this.history) ) { @@ -901,7 +904,6 @@ export class RouterCore< this.buildRouteTree() } - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!this.__store) { this.__store = new Store(getInitialRouterState(this.latestLocation), { onUpdate: () => { @@ -920,7 +922,6 @@ export class RouterCore< if ( typeof window !== 'undefined' && 'CSS' in window && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition typeof window.CSS?.supports === 'function' ) { this.isViewTransitionTypesSupported = window.CSS.supports( @@ -1447,26 +1448,6 @@ export class RouterCore< ...match.__beforeLoadContext, } } - - // If it's already a success, update headers and head content - // These may get updated again if the match is refreshed - // due to being stale - if (match.status === 'success') { - match.headers = route.options.headers?.({ - loaderData: match.loaderData, - }) - const assetContext = { - matches, - match, - params: match.params, - loaderData: match.loaderData, - } - const headFnContent = route.options.head?.(assetContext) - match.links = headFnContent?.links - match.headScripts = headFnContent?.scripts - match.meta = headFnContent?.meta - match.scripts = route.options.scripts?.(assetContext) - } }) return matches @@ -2274,14 +2255,16 @@ export class RouterCore< return !!(allPreload && !this.state.matches.find((d) => d.id === matchId)) } - const handleRedirectAndNotFound = (match: AnyRouteMatch, err: any) => { + const handleRouteError = (match: AnyRouteMatch, err: any) => { if (isResolvedRedirect(err)) { if (!err.reloadDocument) { throw err } } - if (isRedirect(err) || isNotFound(err)) { + const isError = + err && err instanceof Error && !isVariantNotFoundError(err) + if (isRedirect(err) || isNotFound(err) || isError) { updateMatch(match.id, (prev) => ({ ...prev, status: isRedirect(err) @@ -2295,8 +2278,8 @@ export class RouterCore< loaderPromise: undefined, })) - if (!(err as any).routeId) { - ;(err as any).routeId = match.routeId + if (!err.routeId) { + err.routeId = match.routeId } match.beforeLoadPromise?.resolve() @@ -2316,6 +2299,11 @@ export class RouterCore< match: this.getMatch(match.id)!, }) throw err + } else if (isError) { + this.serverSsr?.onMatchSettled({ + router: this, + match: this.getMatch(match.id)!, + }) } } } @@ -2341,13 +2329,13 @@ export class RouterCore< err.routerCode = routerCode firstBadMatchIndex = firstBadMatchIndex ?? index - handleRedirectAndNotFound(this.getMatch(matchId)!, err) + handleRouteError(this.getMatch(matchId)!, err) try { route.options.onError?.(err) } catch (errorHandlerErr) { err = errorHandlerErr - handleRedirectAndNotFound(this.getMatch(matchId)!, err) + handleRouteError(this.getMatch(matchId)!, err) } updateMatch(matchId, (prev) => { @@ -2546,7 +2534,7 @@ export class RouterCore< await prevLoaderPromise const match = this.getMatch(matchId)! if (match.error) { - handleRedirectAndNotFound(match, match.error) + handleRouteError(match, match.error) } } else { const parentMatchPromise = matchPromises[index - 1] as any @@ -2609,6 +2597,35 @@ export class RouterCore< !this.state.matches.find((d) => d.id === matchId), })) + const executeHead = () => { + const match = this.getMatch(matchId) + // in case of a redirecting match during preload, the match does not exist + if (!match) { + return + } + const assetContext = { + matches, + match, + params: match.params, + loaderData: match.loaderData, + } + const headFnContent = route.options.head?.(assetContext) + const meta = headFnContent?.meta + const links = headFnContent?.links + const headScripts = headFnContent?.scripts + + const scripts = route.options.scripts?.(assetContext) + const headers = route.options.headers?.(assetContext) + updateMatch(matchId, (prev) => ({ + ...prev, + meta, + links, + headScripts, + headers, + scripts, + })) + } + const runLoader = async () => { try { // If the Matches component rendered @@ -2637,10 +2654,7 @@ export class RouterCore< const loaderData = await route.options.loader?.(getLoaderContext()) - handleRedirectAndNotFound( - this.getMatch(matchId)!, - loaderData, - ) + handleRouteError(this.getMatch(matchId)!, loaderData) // Lazy option can modify the route options, // so we need to wait for it to resolve before @@ -2649,63 +2663,47 @@ export class RouterCore< await potentialPendingMinPromise() - const assetContext = { - matches, - match: this.getMatch(matchId)!, - params: this.getMatch(matchId)!.params, - loaderData, - } - const headFnContent = - route.options.head?.(assetContext) - const meta = headFnContent?.meta - const links = headFnContent?.links - const headScripts = headFnContent?.scripts - - const scripts = route.options.scripts?.(assetContext) - const headers = route.options.headers?.({ - loaderData, - }) - // Last but not least, wait for the the components // to be preloaded before we resolve the match await route._componentsPromise - updateMatch(matchId, (prev) => ({ - ...prev, - error: undefined, - status: 'success', - isFetching: false, - updatedAt: Date.now(), - loaderData, - meta, - links, - headScripts, - headers, - scripts, - })) + batch(() => { + updateMatch(matchId, (prev) => ({ + ...prev, + error: undefined, + status: 'success', + isFetching: false, + updatedAt: Date.now(), + loaderData, + })) + executeHead() + }) } catch (e) { let error = e await potentialPendingMinPromise() - handleRedirectAndNotFound(this.getMatch(matchId)!, e) + handleRouteError(this.getMatch(matchId)!, e) try { route.options.onError?.(e) } catch (onErrorError) { error = onErrorError - handleRedirectAndNotFound( + handleRouteError( this.getMatch(matchId)!, onErrorError, ) } - updateMatch(matchId, (prev) => ({ - ...prev, - error, - status: 'error', - isFetching: false, - })) + batch(() => { + updateMatch(matchId, (prev) => ({ + ...prev, + error, + status: 'error', + isFetching: false, + })) + executeHead() + }) } this.serverSsr?.onMatchSettled({ @@ -2713,11 +2711,14 @@ export class RouterCore< match: this.getMatch(matchId)!, }) } catch (err) { - updateMatch(matchId, (prev) => ({ - ...prev, - loaderPromise: undefined, - })) - handleRedirectAndNotFound(this.getMatch(matchId)!, err) + batch(() => { + updateMatch(matchId, (prev) => ({ + ...prev, + loaderPromise: undefined, + })) + executeHead() + }) + handleRouteError(this.getMatch(matchId)!, err) } } @@ -2752,6 +2753,11 @@ export class RouterCore< (loaderShouldRunAsync && sync) ) { await runLoader() + } else { + // if the loader did not run, still update head. + // reason: parent's beforeLoad may have changed the route context + // and only now do we know the route context (and that the loader would not run) + executeHead() } } if (!loaderIsRunningAsync) { @@ -3090,10 +3096,16 @@ export class RouterCore< } // Ensure we have a notFoundComponent - invariant( - routeCursor.options.notFoundComponent, - 'No notFoundComponent found. Please set a notFoundComponent on your route or provide a defaultNotFoundComponent to the router.', - ) + // generic Error instead of a NotFoundError when notFoundComponent doesn't exist, is this a bug? + try { + invariant( + routeCursor.options.notFoundComponent, + 'No notFoundComponent found. Please set a notFoundComponent on your route or provide a defaultNotFoundComponent to the router.', + ) + } catch (error) { + ;(error as any).invariantSource = INVARIANT_SOURCE_NOT_FOUND + throw error + } // Find the match for this route const matchForRoute = matchesByRouteId[routeCursor.id] diff --git a/packages/router-core/src/scroll-restoration.ts b/packages/router-core/src/scroll-restoration.ts index 7efa94a908..280df14e6a 100644 --- a/packages/router-core/src/scroll-restoration.ts +++ b/packages/router-core/src/scroll-restoration.ts @@ -18,12 +18,22 @@ export type ScrollRestorationOptions = { scrollBehavior?: ScrollToOptions['behavior'] } +function getSafeSessionStorage() { + try { + if ( + typeof window !== 'undefined' && + typeof window.sessionStorage === 'object' + ) { + return window.sessionStorage + } + } catch { + return undefined + } + return undefined +} + export const storageKey = 'tsr-scroll-restoration-v1_3' -let sessionsStorage = false -try { - sessionsStorage = - typeof window !== 'undefined' && typeof window.sessionStorage === 'object' -} catch {} + const throttle = (fn: (...args: Array) => void, wait: number) => { let timeout: any return (...args: Array) => { @@ -35,28 +45,32 @@ const throttle = (fn: (...args: Array) => void, wait: number) => { } } } -export const scrollRestorationCache: ScrollRestorationCache = sessionsStorage - ? (() => { - const state: ScrollRestorationByKey = - JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {} - - return { - state, - // This setter is simply to make sure that we set the sessionStorage right - // after the state is updated. It doesn't necessarily need to be a functional - // update. - set: (updater) => ( - (scrollRestorationCache.state = - functionalUpdate(updater, scrollRestorationCache.state) || - scrollRestorationCache.state), - window.sessionStorage.setItem( - storageKey, - JSON.stringify(scrollRestorationCache.state), - ) - ), - } - })() - : (undefined as any) + +function createScrollRestorationCache(): ScrollRestorationCache | undefined { + const safeSessionStorage = getSafeSessionStorage() + if (!safeSessionStorage) { + return undefined + } + + const persistedState = safeSessionStorage.getItem(storageKey) + let state: ScrollRestorationByKey = persistedState + ? JSON.parse(persistedState) + : {} + + return { + state, + // This setter is simply to make sure that we set the sessionStorage right + // after the state is updated. It doesn't necessarily need to be a functional + // update. + set: (updater) => ( + (state = functionalUpdate(updater, state) || state), + safeSessionStorage.setItem(storageKey, JSON.stringify(state)) + ), + } +} + +export const scrollRestorationCache = createScrollRestorationCache() + /** * The default `getKey` function for `useScrollRestoration`. * It returns the `key` from the location state or the `href` of the location. @@ -91,7 +105,9 @@ export function restoreScroll( key: string | undefined, behavior: ScrollToOptions['behavior'] | undefined, shouldScrollRestoration: boolean | undefined, - scrollToTopSelectors: Array | undefined, + scrollToTopSelectors: + | Array Element | null | undefined)> + | undefined, ) { let byKey: ScrollRestorationByKey @@ -160,7 +176,11 @@ export function restoreScroll( ...(scrollToTopSelectors?.filter((d) => d !== 'window') ?? []), ].forEach((selector) => { const element = - selector === 'window' ? window : document.querySelector(selector) + selector === 'window' + ? window + : typeof selector === 'function' + ? selector() + : document.querySelector(selector) if (element) { element.scrollTo({ top: 0, @@ -176,6 +196,9 @@ export function restoreScroll( } export function setupScrollRestoration(router: AnyRouter, force?: boolean) { + if (scrollRestorationCache === undefined) { + return + } const shouldScrollRestoration = force ?? router.options.scrollRestoration ?? false diff --git a/packages/router-core/src/utils.ts b/packages/router-core/src/utils.ts index 1df674e8b9..5beb9d5bb9 100644 --- a/packages/router-core/src/utils.ts +++ b/packages/router-core/src/utils.ts @@ -213,10 +213,18 @@ export function replaceEqualDeep(prev: any, _next: T): T { const array = isPlainArray(prev) && isPlainArray(next) - if (array || (isPlainObject(prev) && isPlainObject(next))) { - const prevItems = array ? prev : Object.keys(prev) + if (array || (isSimplePlainObject(prev) && isSimplePlainObject(next))) { + const prevItems = array + ? prev + : (Object.keys(prev) as Array).concat( + Object.getOwnPropertySymbols(prev), + ) const prevSize = prevItems.length - const nextItems = array ? next : Object.keys(next) + const nextItems = array + ? next + : (Object.keys(next) as Array).concat( + Object.getOwnPropertySymbols(next), + ) const nextSize = nextItems.length const copy: any = array ? [] : {} @@ -245,6 +253,19 @@ export function replaceEqualDeep(prev: any, _next: T): T { return next } +/** + * A wrapper around `isPlainObject` with additional checks to ensure that it is not + * only a plain object, but also one that is "clone-friendly" (doesn't have any + * non-enumerable properties). + */ +function isSimplePlainObject(o: any) { + return ( + // all the checks from isPlainObject are more likely to hit so we perform them first + isPlainObject(o) && + Object.getOwnPropertyNames(o).length === Object.keys(o).length + ) +} + // Copied from: https://github.com/jonschlinkert/is-plain-object export function isPlainObject(o: any) { if (!hasObjectPrototype(o)) { @@ -425,20 +446,3 @@ export function shallow(objA: T, objB: T) { } return true } - -/** - * Checks if a string contains URI-encoded special characters (e.g., %3F, %20). - * - * @param {string} inputString The string to check. - * @returns {boolean} True if the string contains URI-encoded characters, false otherwise. - * @example - * ```typescript - * const str1 = "foo%3Fbar"; - * const hasEncodedChars = hasUriEncodedChars(str1); // returns true - * ``` - */ -export function hasUriEncodedChars(inputString: string): boolean { - // This regex looks for a percent sign followed by two hexadecimal digits - const pattern = /%[0-9A-Fa-f]{2}/ - return pattern.test(inputString) -} diff --git a/packages/router-core/tests/qss.test.ts b/packages/router-core/tests/qss.test.ts index 8bff5c3a64..ae2516c1fe 100644 --- a/packages/router-core/tests/qss.test.ts +++ b/packages/router-core/tests/qss.test.ts @@ -104,4 +104,10 @@ describe('decode function', () => { const decodedObj = decode(queryString) expect(decodedObj).toEqual({ q: 'red+yellow orange' }) }) + + it('should decode once percent characters (%) encoded twice', () => { + const queryString = 'q=%2540' + const decodedObj = decode(queryString) + expect(decodedObj).toEqual({ q: '%40' }) + }) }) diff --git a/packages/router-core/tests/utils.test.ts b/packages/router-core/tests/utils.test.ts index 486ccf0410..562716b231 100644 --- a/packages/router-core/tests/utils.test.ts +++ b/packages/router-core/tests/utils.test.ts @@ -29,6 +29,44 @@ describe('replaceEqualDeep', () => { expect(result).toStrictEqual(obj2) }) + describe('symbol properties', () => { + it('should look at symbol properties in the object comparison', () => { + const propertyKey = Symbol('property') + const obj1 = { a: 1, [propertyKey]: 2 } + const obj2 = { a: 1, [propertyKey]: 3 } + const result = replaceEqualDeep(obj1, obj2) + expect(result).toStrictEqual(obj2) + }) + + it('should copy over symbol properties when creating a new object', () => { + const propertyKey = Symbol('property') + const obj1 = { a: 1, [propertyKey]: 2 } + const obj2 = { a: 3, [propertyKey]: 2 } + const result = replaceEqualDeep(obj1, obj2) + expect(result).toStrictEqual(obj2) + }) + }) + + describe('non-enumerable properties', () => { + it('should treat objects with non-enumerable properties as non-plain (no need for property comparisons)', () => { + const obj1: { a: number; b?: number } = { a: 1 } + Object.defineProperty(obj1, 'b', { enumerable: false, value: 2 }) + const obj2: { a: number; b?: number } = { a: 1 } + Object.defineProperty(obj2, 'b', { enumerable: false, value: 3 }) + const result = replaceEqualDeep(obj1, obj2) + expect(result).toBe(obj2) + }) + + it("should treat objects with non-enumerable properties as non-plain (copying doesn't happen)", () => { + const obj1: { a: number; b?: number } = { a: 1 } + Object.defineProperty(obj1, 'b', { enumerable: false, value: 2 }) + const obj2: { a: number; b?: number } = { a: 3 } + Object.defineProperty(obj2, 'b', { enumerable: false, value: 2 }) + const result = replaceEqualDeep(obj1, obj2) + expect(result).toBe(obj2) + }) + }) + it('should properly handle non-existent keys', () => { const obj1 = { a: 2, c: 123 } const obj2 = { a: 2, c: 123, b: undefined } diff --git a/packages/router-devtools-core/package.json b/packages/router-devtools-core/package.json index 59cc80782b..a184361472 100644 --- a/packages/router-devtools-core/package.json +++ b/packages/router-devtools-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-devtools-core", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Web applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-devtools-core/src/TanStackRouterDevtoolsCore.tsx b/packages/router-devtools-core/src/TanStackRouterDevtoolsCore.tsx index 38a99c583e..4fcd27c931 100644 --- a/packages/router-devtools-core/src/TanStackRouterDevtoolsCore.tsx +++ b/packages/router-devtools-core/src/TanStackRouterDevtoolsCore.tsx @@ -1,5 +1,6 @@ import { createSignal, lazy } from 'solid-js' import { render } from 'solid-js/web' +import { ShadowDomTargetContext } from './context' import type { AnyRouter } from '@tanstack/router-core' import type { Signal } from 'solid-js' @@ -58,6 +59,7 @@ class TanStackRouterDevtoolsCore { #panelProps: any #closeButtonProps: any #toggleButtonProps: any + #containerElement?: string | any #isMounted = false #Component: any @@ -73,6 +75,7 @@ class TanStackRouterDevtoolsCore { this.#panelProps = config.panelProps this.#closeButtonProps = config.closeButtonProps this.#toggleButtonProps = config.toggleButtonProps + this.#containerElement = config.containerElement } mount(el: T) { @@ -90,6 +93,7 @@ class TanStackRouterDevtoolsCore { const panelProps = this.#panelProps const closeButtonProps = this.#closeButtonProps const toggleButtonProps = this.#toggleButtonProps + const containerElement = this.#containerElement let Devtools @@ -101,16 +105,19 @@ class TanStackRouterDevtoolsCore { } return ( - + + + ) }, el) @@ -146,6 +153,10 @@ class TanStackRouterDevtoolsCore { if (options.shadowDOMTarget !== undefined) { this.#shadowDOMTarget = options.shadowDOMTarget } + + if (options.containerElement !== undefined) { + this.#containerElement = options.containerElement + } } } diff --git a/packages/router-devtools/package.json b/packages/router-devtools/package.json index 82405a50bb..5e616aa0cc 100644 --- a/packages/router-devtools/package.json +++ b/packages/router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-devtools", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-generator/package.json b/packages/router-generator/package.json index 6a86263abf..4e8195af41 100644 --- a/packages/router-generator/package.json +++ b/packages/router-generator/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-generator", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index 1b0e9ebefc..f1d91b2f49 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-plugin", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/src/core/code-splitter/compilers.ts b/packages/router-plugin/src/core/code-splitter/compilers.ts index 91ca8ccf79..f223f89c87 100644 --- a/packages/router-plugin/src/core/code-splitter/compilers.ts +++ b/packages/router-plugin/src/core/code-splitter/compilers.ts @@ -7,6 +7,7 @@ import { } from 'babel-dead-code-elimination' import { generateFromAst, parseAst } from '@tanstack/router-utils' import { tsrSplit } from '../constants' +import { routeHmrStatement } from '../route-hmr-statement' import { createIdentifier } from './path-ids' import { getFrameworkOptions } from './framework-options' import type { GeneratorResult, ParseAstOptions } from '@tanstack/router-utils' @@ -288,18 +289,9 @@ export function compileCodeSplitReferenceRoute( )() } - // If the TSRDummyComponent is not defined, define it - if ( - opts.runtimeEnv !== 'prod' && // only in development - !hasImportedOrDefinedIdentifier( - frameworkOptions.idents.dummyHMRComponent, - ) - ) { - programPath.pushContainer('body', [ - template.statement( - frameworkOptions.dummyHMRComponent, - )(), - ]) + // add HMR handling + if (opts.runtimeEnv !== 'prod') { + programPath.pushContainer('body', routeHmrStatement) } } diff --git a/packages/router-plugin/src/core/code-splitter/framework-options.ts b/packages/router-plugin/src/core/code-splitter/framework-options.ts index 909f7607d6..860178b1f1 100644 --- a/packages/router-plugin/src/core/code-splitter/framework-options.ts +++ b/packages/router-plugin/src/core/code-splitter/framework-options.ts @@ -4,9 +4,7 @@ type FrameworkOptions = { createFileRoute: string lazyFn: string lazyRouteComponent: string - dummyHMRComponent: string } - dummyHMRComponent: string } export function getFrameworkOptions(framework: string): FrameworkOptions { @@ -20,9 +18,7 @@ export function getFrameworkOptions(framework: string): FrameworkOptions { createFileRoute: 'createFileRoute', lazyFn: 'lazyFn', lazyRouteComponent: 'lazyRouteComponent', - dummyHMRComponent: 'TSRDummyComponent', }, - dummyHMRComponent: `export function TSRDummyComponent() { return null }`, } break case 'solid': @@ -32,9 +28,7 @@ export function getFrameworkOptions(framework: string): FrameworkOptions { createFileRoute: 'createFileRoute', lazyFn: 'lazyFn', lazyRouteComponent: 'lazyRouteComponent', - dummyHMRComponent: 'TSRDummyComponent', }, - dummyHMRComponent: `export function TSRDummyComponent() { return null }`, } break default: diff --git a/packages/router-plugin/src/core/route-hmr-statement.ts b/packages/router-plugin/src/core/route-hmr-statement.ts new file mode 100644 index 0000000000..2815b89272 --- /dev/null +++ b/packages/router-plugin/src/core/route-hmr-statement.ts @@ -0,0 +1,13 @@ +import * as template from '@babel/template' + +export const routeHmrStatement = template.statement( + ` +if (import.meta.hot) { + import.meta.hot.accept((newModule) => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route) + } + }) +} +`, +)() diff --git a/packages/router-plugin/src/core/router-code-splitter-plugin.ts b/packages/router-plugin/src/core/router-code-splitter-plugin.ts index 7c1ecc17a8..d15e60fdd0 100644 --- a/packages/router-plugin/src/core/router-code-splitter-plugin.ts +++ b/packages/router-plugin/src/core/router-code-splitter-plugin.ts @@ -3,7 +3,6 @@ * https://github.com/TanStack/router/pull/3355 */ -import { isAbsolute, join, normalize } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' import { logDiff } from '@tanstack/router-utils' import { getConfig, splitGroupingsSchema } from './config' @@ -18,6 +17,7 @@ import { tsrSplit, } from './constants' import { decodeIdentifier } from './code-splitter/path-ids' +import { debug, fileIsInRoutesDirectory } from './utils' import type { CodeSplitGroupings, SplitRouteIdentNodes } from './constants' import type { Config } from './config' @@ -27,27 +27,10 @@ import type { TransformResult as UnpluginTransformResult, } from 'unplugin' -const debug = - process.env.TSR_VITE_DEBUG && - ['true', 'router-plugin'].includes(process.env.TSR_VITE_DEBUG) - function capitalizeFirst(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1) } -function fileIsInRoutesDirectory( - filePath: string, - routesDirectory: string, -): boolean { - const routesDirectoryPath = isAbsolute(routesDirectory) - ? routesDirectory - : join(process.cwd(), routesDirectory) - - const path = normalize(filePath) - - return path.startsWith(routesDirectoryPath) -} - type BannedBeforeExternalPlugin = { identifier: string pkg: string diff --git a/packages/router-plugin/src/core/router-composed-plugin.ts b/packages/router-plugin/src/core/router-composed-plugin.ts index a8256f8b4b..fe384be10f 100644 --- a/packages/router-plugin/src/core/router-composed-plugin.ts +++ b/packages/router-plugin/src/core/router-composed-plugin.ts @@ -1,22 +1,30 @@ import { unpluginRouterGeneratorFactory } from './router-generator-plugin' import { unpluginRouterCodeSplitterFactory } from './router-code-splitter-plugin' - +import { unpluginRouterHmrFactory } from './router-hmr-plugin' import type { Config } from './config' import type { UnpluginFactory } from 'unplugin' export const unpluginRouterComposedFactory: UnpluginFactory< Partial | undefined > = (options = {}, meta) => { - const routerGenerator = unpluginRouterGeneratorFactory(options, meta) + const getPlugin = (pluginFactory: UnpluginFactory>) => { + const plugin = pluginFactory(options, meta) + if (!Array.isArray(plugin)) { + return [plugin] + } + return plugin + } + + const routerGenerator = getPlugin(unpluginRouterGeneratorFactory) + const routerCodeSplitter = getPlugin(unpluginRouterCodeSplitterFactory) - const routerGeneratorOptions = Array.isArray(routerGenerator) - ? routerGenerator - : [routerGenerator] + const result = [...routerGenerator, ...routerCodeSplitter] - const routerCodeSplitter = unpluginRouterCodeSplitterFactory(options, meta) - const routerCodeSplitterOptions = Array.isArray(routerCodeSplitter) - ? routerCodeSplitter - : [routerCodeSplitter] + const isProduction = process.env.NODE_ENV === 'production' - return [...routerGeneratorOptions, ...routerCodeSplitterOptions] + if (!isProduction && !options.autoCodeSplitting) { + const routerHmr = getPlugin(unpluginRouterHmrFactory) + result.push(...routerHmr) + } + return result } diff --git a/packages/router-plugin/src/core/router-hmr-plugin.ts b/packages/router-plugin/src/core/router-hmr-plugin.ts new file mode 100644 index 0000000000..38e29bbb11 --- /dev/null +++ b/packages/router-plugin/src/core/router-hmr-plugin.ts @@ -0,0 +1,67 @@ +import { generateFromAst, logDiff, parseAst } from '@tanstack/router-utils' +import { getConfig } from './config' +import { routeHmrStatement } from './route-hmr-statement' +import { debug, fileIsInRoutesDirectory } from './utils' +import type { Config } from './config' +import type { UnpluginFactory } from 'unplugin' + +/** + * This plugin adds HMR support for file routes. + * It is only added to the composed plugin in dev when autoCodeSplitting is disabled, since the code splitting plugin + * handles HMR for code-split routes itself. + */ +export const unpluginRouterHmrFactory: UnpluginFactory< + Partial | undefined +> = (options = {}) => { + let ROOT: string = process.cwd() + let userConfig = options as Config + + return { + name: 'router-hmr-plugin', + enforce: 'pre', + + transform(code, id) { + if (!code.includes('export const Route = createFileRoute(')) { + return null + } + + if (debug) console.info('Adding HMR handling to route ', id) + + const ast = parseAst({ code, filename: id, root: ROOT }) + ast.program.body.push(routeHmrStatement) + const result = generateFromAst(ast, { + sourceMaps: true, + filename: id, + sourceFileName: id, + }) + if (debug) { + logDiff(code, result.code) + console.log('Output:\n', result.code + '\n\n') + } + return result + }, + + transformInclude(id) { + return fileIsInRoutesDirectory(id, userConfig.routesDirectory) + }, + + vite: { + configResolved(config) { + ROOT = config.root + config.mode + + userConfig = getConfig(options, ROOT) + }, + }, + + rspack() { + ROOT = process.cwd() + userConfig = getConfig(options, ROOT) + }, + + webpack() { + ROOT = process.cwd() + userConfig = getConfig(options, ROOT) + }, + } +} diff --git a/packages/router-plugin/src/core/utils.ts b/packages/router-plugin/src/core/utils.ts new file mode 100644 index 0000000000..d866a2241f --- /dev/null +++ b/packages/router-plugin/src/core/utils.ts @@ -0,0 +1,18 @@ +import { isAbsolute, join, normalize } from 'node:path' + +export const debug = + process.env.TSR_VITE_DEBUG && + ['true', 'router-plugin'].includes(process.env.TSR_VITE_DEBUG) + +export function fileIsInRoutesDirectory( + filePath: string, + routesDirectory: string, +): boolean { + const routesDirectoryPath = isAbsolute(routesDirectory) + ? routesDirectory + : join(process.cwd(), routesDirectory) + + const path = normalize(filePath) + + return path.startsWith(routesDirectoryPath) +} diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/arrow-function.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/arrow-function.tsx index 0e48584a4a..a2d9da61f4 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/arrow-function.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/arrow-function.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/chinese.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/chinese.tsx index dffbd70c4d..1d0242f18d 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/chinese.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/chinese.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/')({ interface DemoProps { title: string; } -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/conditional-properties.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/conditional-properties.tsx index 26de4ca825..1d93313deb 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/conditional-properties.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/conditional-properties.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/posts')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: isEnabled ? TrueImport.loader : falseLoader }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/destructured-react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/destructured-react-memo-imported-component.tsx index 0fbc312626..2f21f20798 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/destructured-react-memo-imported-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/destructured-react-memo-imported-component.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: importedLoader }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/export-default-component-and-normal-notFound.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/export-default-component-and-normal-notFound.tsx index 0c88bfe0ad..31f5b194df 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/export-default-component-and-normal-notFound.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/export-default-component-and-normal-notFound.tsx @@ -12,6 +12,10 @@ export default function Home() {

{one}

; } -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/function-declaration.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/function-declaration.tsx index c395bc44ee..ca069fa2f5 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/function-declaration.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/function-declaration.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/importAttribute.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/importAttribute.tsx index 8a221122d7..db52ba8d9a 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/importAttribute.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/importAttribute.tsx @@ -4,6 +4,10 @@ import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component-destructured-loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component-destructured-loader.tsx index e23a496a74..62cf180237 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component-destructured-loader.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component-destructured-loader.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: importedLoader }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component.tsx index 968e6633d4..6bb5d1dd3f 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-default-component.tsx @@ -4,6 +4,10 @@ import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-errorComponent.tsx index 0701029aa3..ea85b109d8 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-errorComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-errorComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-notFoundComponent.tsx index 63787a44d6..dc7a28a287 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-notFoundComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-notFoundComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), notFoundComponent: lazyRouteComponent($$splitNotFoundComponentImporter, 'notFoundComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-pendingComponent.tsx index 13cfc22f7b..2038f1b9eb 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-pendingComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported-pendingComponent.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), pendingComponent: importedPendingComponent }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported.tsx index c4a5d9dbc4..87908a42d6 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/imported.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: importedLoader }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/inline.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/inline.tsx index 931f6bce74..63ad73ff1a 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/inline.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/inline.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ }); Route.addChildren([]); export const test = 'test'; -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/random-number.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/random-number.tsx index b229826efb..8f4f8d793f 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/random-number.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/random-number.tsx @@ -30,6 +30,10 @@ export const Route = createFileRoute('/')({ }, component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-component.tsx index dc266fa611..c0c93ee373 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-component.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: importedLoader }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-imported-component.tsx index b1df7f4cf9..4a4531d299 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-imported-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/react-memo-imported-component.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: importedLoader }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/retain-exports-loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/retain-exports-loader.tsx index 2d40731c37..22e3875ec9 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/retain-exports-loader.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/development/retain-exports-loader.tsx @@ -14,6 +14,10 @@ export const Route = createFileRoute('/_layout')({ export const SIDEBAR_WIDTH = '150px'; export const SIDEBAR_MINI_WIDTH = '80px'; const ASIDE_WIDTH = '250px'; -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/arrow-function.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/arrow-function.tsx index 7104674b73..fcbd9b7ada 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/arrow-function.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/arrow-function.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/posts')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/chinese.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/chinese.tsx index 3efbe3595d..01d06fa136 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/chinese.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/chinese.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/')({ interface DemoProps { title: string; } -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/conditional-properties.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/conditional-properties.tsx index a126bb6807..8724f2d0f7 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/conditional-properties.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/conditional-properties.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/posts')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/destructured-react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/destructured-react-memo-imported-component.tsx index 65f2bfb918..2c59955a0e 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/destructured-react-memo-imported-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/destructured-react-memo-imported-component.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/export-default-component-and-normal-notFound.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/export-default-component-and-normal-notFound.tsx index 762ac36b1d..f5593e1acc 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/export-default-component-and-normal-notFound.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/export-default-component-and-normal-notFound.tsx @@ -12,6 +12,10 @@ export default function Home() {

{one}

; } -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/function-declaration.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/function-declaration.tsx index d6e6fa8152..1d1c7fcdda 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/function-declaration.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/function-declaration.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/posts')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/importAttribute.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/importAttribute.tsx index b89aede427..c7e907cc2f 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/importAttribute.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/importAttribute.tsx @@ -4,6 +4,10 @@ import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component-destructured-loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component-destructured-loader.tsx index 9169f0b966..5b1c3e2573 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component-destructured-loader.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component-destructured-loader.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component.tsx index 9b38429966..44a02f3be3 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-default-component.tsx @@ -4,6 +4,10 @@ import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-errorComponent.tsx index 108ad65fde..24948c383b 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-errorComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-errorComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-notFoundComponent.tsx index 4df3a0edc2..0ec7ed1f1b 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-notFoundComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-notFoundComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), notFoundComponent: lazyRouteComponent($$splitNotFoundComponentImporter, 'notFoundComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-pendingComponent.tsx index 8a12860d09..555fd15f64 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-pendingComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported-pendingComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), pendingComponent: lazyRouteComponent($$splitPendingComponentImporter, 'pendingComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported.tsx index b8c6e8c0ff..2aced9ac60 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/imported.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/inline.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/inline.tsx index a179d36c0b..8c6d154c5a 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/inline.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/inline.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ }); Route.addChildren([]); export const test = 'test'; -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/random-number.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/random-number.tsx index 92f502edde..deefd430c2 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/random-number.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/random-number.tsx @@ -26,6 +26,10 @@ export const Route = createFileRoute('/')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-component.tsx index bf8ae8af25..bc089cafdb 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-component.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-imported-component.tsx index 5987f84c0a..8320df5ddb 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-imported-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/react-memo-imported-component.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/retain-exports-loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/retain-exports-loader.tsx index c8da9752f0..78bc9de29f 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/retain-exports-loader.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/development/retain-exports-loader.tsx @@ -14,6 +14,10 @@ export const Route = createFileRoute('/_layout')({ export const SIDEBAR_WIDTH = '150px'; export const SIDEBAR_MINI_WIDTH = '80px'; const ASIDE_WIDTH = '250px'; -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/arrow-function.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/arrow-function.tsx index 25bae96925..dc548e7925 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/arrow-function.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/arrow-function.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/posts')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/chinese.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/chinese.tsx index 9abb4a9d61..d1c8625b32 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/chinese.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/chinese.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/')({ interface DemoProps { title: string; } -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/conditional-properties.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/conditional-properties.tsx index 279773de15..d843c9e66c 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/conditional-properties.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/conditional-properties.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/posts')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/destructured-react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/destructured-react-memo-imported-component.tsx index 0fe0b8418d..44808e3845 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/destructured-react-memo-imported-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/destructured-react-memo-imported-component.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/export-default-component-and-normal-notFound.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/export-default-component-and-normal-notFound.tsx index 77e009583c..0caf3ba2e0 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/export-default-component-and-normal-notFound.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/export-default-component-and-normal-notFound.tsx @@ -12,6 +12,10 @@ export default function Home() {

{one}

; } -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/function-declaration.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/function-declaration.tsx index 01360ef869..f968bb9837 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/function-declaration.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/function-declaration.tsx @@ -8,6 +8,10 @@ export const Route = createFileRoute('/posts')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/importAttribute.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/importAttribute.tsx index d8355986bf..b5c4aca679 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/importAttribute.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/importAttribute.tsx @@ -4,6 +4,10 @@ import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component-destructured-loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component-destructured-loader.tsx index 4e0beddab5..0d4eafe7ca 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component-destructured-loader.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component-destructured-loader.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component.tsx index b749bed2ea..334b09ca21 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-default-component.tsx @@ -4,6 +4,10 @@ import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-errorComponent.tsx index 26951c2d0a..d524d0135c 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-errorComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-errorComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-notFoundComponent.tsx index 54565ca6f1..f12bd5c684 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-notFoundComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-notFoundComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), notFoundComponent: lazyRouteComponent($$splitNotFoundComponentImporter, 'notFoundComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-pendingComponent.tsx index 02997dc053..0c67f5297f 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-pendingComponent.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported-pendingComponent.tsx @@ -6,6 +6,17 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), pendingComponent: lazyRouteComponent($$splitPendingComponentImporter, 'pendingComponent') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); +} +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported.tsx index c397f9dccc..a2b6769679 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/imported.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/inline.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/inline.tsx index ff044594ad..0035767b18 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/inline.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/inline.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ }); Route.addChildren([]); export const test = 'test'; -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/random-number.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/random-number.tsx index 05f26ecf0d..65b04dc77b 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/random-number.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/random-number.tsx @@ -26,6 +26,10 @@ export const Route = createFileRoute('/')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-component.tsx index 66da0c232a..7d2b882112 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-component.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-imported-component.tsx index 8db833f30c..ea123154b3 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-imported-component.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/react-memo-imported-component.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/')({ component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr), loader: lazyFn($$splitLoaderImporter, 'loader') }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/retain-exports-loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/retain-exports-loader.tsx index eb4fd485bb..351de845e6 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/retain-exports-loader.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/development/retain-exports-loader.tsx @@ -14,6 +14,10 @@ export const Route = createFileRoute('/_layout')({ export const SIDEBAR_WIDTH = '150px'; export const SIDEBAR_MINI_WIDTH = '80px'; const ASIDE_WIDTH = '250px'; -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/solid/1-default/development/arrow-function.tsx b/packages/router-plugin/tests/code-splitter/snapshots/solid/1-default/development/arrow-function.tsx index f62b65b5eb..8e9404d802 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/solid/1-default/development/arrow-function.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/solid/1-default/development/arrow-function.tsx @@ -6,6 +6,10 @@ export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/solid/2-components-combined-loader-separate/development/arrow-function.tsx b/packages/router-plugin/tests/code-splitter/snapshots/solid/2-components-combined-loader-separate/development/arrow-function.tsx index 5f36162818..86cc7faf7f 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/solid/2-components-combined-loader-separate/development/arrow-function.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/solid/2-components-combined-loader-separate/development/arrow-function.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/posts')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/solid/3-all-combined-errorComponent-separate/development/arrow-function.tsx b/packages/router-plugin/tests/code-splitter/snapshots/solid/3-all-combined-errorComponent-separate/development/arrow-function.tsx index 56e28dce37..0a2e167802 100644 --- a/packages/router-plugin/tests/code-splitter/snapshots/solid/3-all-combined-errorComponent-separate/development/arrow-function.tsx +++ b/packages/router-plugin/tests/code-splitter/snapshots/solid/3-all-combined-errorComponent-separate/development/arrow-function.tsx @@ -7,6 +7,10 @@ export const Route = createFileRoute('/posts')({ loader: lazyFn($$splitLoaderImporter, 'loader'), component: lazyRouteComponent($$splitComponentImporter, 'component', () => Route.ssr) }); -export function TSRDummyComponent() { - return null; +if (import.meta.hot) { + import.meta.hot.accept(newModule => { + if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { + newModule.Route.clone(Route); + } + }); } \ No newline at end of file diff --git a/packages/router-vite-plugin/package.json b/packages/router-vite-plugin/package.json index d3768c86fd..bd432ebad5 100644 --- a/packages/router-vite-plugin/package.json +++ b/packages/router-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-vite-plugin", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-router-devtools/package.json b/packages/solid-router-devtools/package.json index 2970a3554a..a18b758e95 100644 --- a/packages/solid-router-devtools/package.json +++ b/packages/solid-router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-router-devtools", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-router/package.json b/packages/solid-router/package.json index 931c967fcc..c7f2836c03 100644 --- a/packages/solid-router/package.json +++ b/packages/solid-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-router", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-router/src/ScrollRestoration.tsx b/packages/solid-router/src/ScrollRestoration.tsx index 063d93914f..71d472723d 100644 --- a/packages/solid-router/src/ScrollRestoration.tsx +++ b/packages/solid-router/src/ScrollRestoration.tsx @@ -64,6 +64,6 @@ export function useElementScrollRestoration( } const restoreKey = getKey(router.latestLocation) - const byKey = scrollRestorationCache.state[restoreKey] + const byKey = scrollRestorationCache?.state[restoreKey] return byKey?.[elementSelector] } diff --git a/packages/solid-router/src/route.tsx b/packages/solid-router/src/route.tsx index a1c2762f69..5e61e3926b 100644 --- a/packages/solid-router/src/route.tsx +++ b/packages/solid-router/src/route.tsx @@ -24,9 +24,11 @@ import type { ResolveFullPath, ResolveId, ResolveParams, + RootRoute as RootRouteCore, RootRouteId, RootRouteOptions, RouteConstraints, + Route as RouteCore, RouteIds, RouteMask, RouteOptions, @@ -142,43 +144,62 @@ export class RouteApi< } export class Route< - in out TParentRoute extends RouteConstraints['TParentRoute'] = AnyRoute, - in out TPath extends RouteConstraints['TPath'] = '/', - in out TFullPath extends RouteConstraints['TFullPath'] = ResolveFullPath< - TParentRoute, - TPath - >, - in out TCustomId extends RouteConstraints['TCustomId'] = string, - in out TId extends RouteConstraints['TId'] = ResolveId< + in out TParentRoute extends RouteConstraints['TParentRoute'] = AnyRoute, + in out TPath extends RouteConstraints['TPath'] = '/', + in out TFullPath extends RouteConstraints['TFullPath'] = ResolveFullPath< + TParentRoute, + TPath + >, + in out TCustomId extends RouteConstraints['TCustomId'] = string, + in out TId extends RouteConstraints['TId'] = ResolveId< + TParentRoute, + TCustomId, + TPath + >, + in out TSearchValidator = undefined, + in out TParams = ResolveParams, + in out TRouterContext = AnyContext, + in out TRouteContextFn = AnyContext, + in out TBeforeLoadFn = AnyContext, + in out TLoaderDeps extends Record = {}, + in out TLoaderFn = undefined, + in out TChildren = unknown, + in out TFileRouteTypes = unknown, + > + extends BaseRoute< TParentRoute, + TPath, + TFullPath, TCustomId, - TPath - >, - in out TSearchValidator = undefined, - in out TParams = ResolveParams, - in out TRouterContext = AnyContext, - in out TRouteContextFn = AnyContext, - in out TBeforeLoadFn = AnyContext, - in out TLoaderDeps extends Record = {}, - in out TLoaderFn = undefined, - in out TChildren = unknown, - in out TFileRouteTypes = unknown, -> extends BaseRoute< - TParentRoute, - TPath, - TFullPath, - TCustomId, - TId, - TSearchValidator, - TParams, - TRouterContext, - TRouteContextFn, - TBeforeLoadFn, - TLoaderDeps, - TLoaderFn, - TChildren, - TFileRouteTypes -> { + TId, + TSearchValidator, + TParams, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > + implements + RouteCore< + TParentRoute, + TPath, + TFullPath, + TCustomId, + TId, + TSearchValidator, + TParams, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > +{ /** * @deprecated Use the `createRoute` function instead. */ @@ -352,24 +373,37 @@ export function createRootRouteWithContext() { export const rootRouteWithContext = createRootRouteWithContext export class RootRoute< - in out TSearchValidator = undefined, - in out TRouterContext = {}, - in out TRouteContextFn = AnyContext, - in out TBeforeLoadFn = AnyContext, - in out TLoaderDeps extends Record = {}, - in out TLoaderFn = undefined, - in out TChildren = unknown, - in out TFileRouteTypes = unknown, -> extends BaseRootRoute< - TSearchValidator, - TRouterContext, - TRouteContextFn, - TBeforeLoadFn, - TLoaderDeps, - TLoaderFn, - TChildren, - TFileRouteTypes -> { + in out TSearchValidator = undefined, + in out TRouterContext = {}, + in out TRouteContextFn = AnyContext, + in out TBeforeLoadFn = AnyContext, + in out TLoaderDeps extends Record = {}, + in out TLoaderFn = undefined, + in out TChildren = unknown, + in out TFileRouteTypes = unknown, + > + extends BaseRootRoute< + TSearchValidator, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > + implements + RootRouteCore< + TSearchValidator, + TRouterContext, + TRouteContextFn, + TBeforeLoadFn, + TLoaderDeps, + TLoaderFn, + TChildren, + TFileRouteTypes + > +{ /** * @deprecated `RootRoute` is now an internal implementation detail. Use `createRootRoute()` instead. */ diff --git a/packages/solid-start-client/package.json b/packages/solid-start-client/package.json index 63e7524577..614d26614c 100644 --- a/packages/solid-start-client/package.json +++ b/packages/solid-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-client", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-config/package.json b/packages/solid-start-config/package.json index 0e730177d0..f9d91cefdc 100644 --- a/packages/solid-start-config/package.json +++ b/packages/solid-start-config/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-config", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-router-manifest/package.json b/packages/solid-start-router-manifest/package.json index 3ee031b8e0..d318be01d1 100644 --- a/packages/solid-start-router-manifest/package.json +++ b/packages/solid-start-router-manifest/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-router-manifest", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-server/package.json b/packages/solid-start-server/package.json index 4aea00c279..b3673e7c57 100644 --- a/packages/solid-start-server/package.json +++ b/packages/solid-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-server", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index 228c931c61..345b89025d 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-api-routes/package.json b/packages/start-api-routes/package.json index 3c2acd77ee..940dfa58a3 100644 --- a/packages/start-api-routes/package.json +++ b/packages/start-api-routes/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-api-routes", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-client-core/package.json b/packages/start-client-core/package.json index e4ce1dbd0d..f26e2131cf 100644 --- a/packages/start-client-core/package.json +++ b/packages/start-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-client-core", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-config/package.json b/packages/start-config/package.json index 8c5bb24691..7fee2fd1b6 100644 --- a/packages/start-config/package.json +++ b/packages/start-config/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-config", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-core/package.json b/packages/start-server-core/package.json index 95f7e6799a..c46e2eca5e 100644 --- a/packages/start-server-core/package.json +++ b/packages/start-server-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-core", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-functions-client/package.json b/packages/start-server-functions-client/package.json index 8878f1ea62..367e3e6bb8 100644 --- a/packages/start-server-functions-client/package.json +++ b/packages/start-server-functions-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-functions-client", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-functions-fetcher/package.json b/packages/start-server-functions-fetcher/package.json index e8e5ec5828..3848fef931 100644 --- a/packages/start-server-functions-fetcher/package.json +++ b/packages/start-server-functions-fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-functions-fetcher", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-functions-handler/package.json b/packages/start-server-functions-handler/package.json index cec8c3d19a..86c8f1d48d 100644 --- a/packages/start-server-functions-handler/package.json +++ b/packages/start-server-functions-handler/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-functions-handler", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-functions-ssr/package.json b/packages/start-server-functions-ssr/package.json index b9feda56bc..9bf741faff 100644 --- a/packages/start-server-functions-ssr/package.json +++ b/packages/start-server-functions-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-functions-ssr", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start/package.json b/packages/start/package.json index 2a4f92a117..629624b0ee 100644 --- a/packages/start/package.json +++ b/packages/start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/valibot-adapter/package.json b/packages/valibot-adapter/package.json index ce55411e1e..dbdb3a1de2 100644 --- a/packages/valibot-adapter/package.json +++ b/packages/valibot-adapter/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/valibot-adapter", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/zod-adapter/package.json b/packages/zod-adapter/package.json index 93af7a8aa8..e92788101a 100644 --- a/packages/zod-adapter/package.json +++ b/packages/zod-adapter/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/zod-adapter", - "version": "1.120.3", + "version": "1.120.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93f882fd74..b1cba5e259 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,9 +98,15 @@ importers: eslint-plugin-unused-imports: specifier: ^4.1.4 version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)) + fast-glob: + specifier: ^3.3.3 + version: 3.3.3 jsdom: specifier: ^25.0.1 version: 25.0.1 + markdown-link-extractor: + specifier: ^4.0.2 + version: 4.0.2 nx: specifier: 20.8.1 version: 20.8.1(@swc/core@1.10.15(@swc/helpers@0.5.15)) @@ -5244,7 +5250,7 @@ importers: examples/solid/basic: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5284,7 +5290,7 @@ importers: examples/solid/basic-devtools-panel: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5318,7 +5324,7 @@ importers: examples/solid/basic-file-based: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5358,7 +5364,7 @@ importers: examples/solid/basic-non-nested-devtools: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5404,7 +5410,7 @@ importers: specifier: ^5.71.9 version: 5.71.9(@tanstack/solid-query@5.71.9(solid-js@1.9.5))(solid-js@1.9.5) '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5447,7 +5453,7 @@ importers: specifier: ^5.71.9 version: 5.71.9(@tanstack/solid-query@5.71.9(solid-js@1.9.5))(solid-js@1.9.5) '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5487,7 +5493,7 @@ importers: examples/solid/kitchen-sink-file-based: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5530,7 +5536,7 @@ importers: examples/solid/quickstart-file-based: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5570,7 +5576,7 @@ importers: examples/solid/start-bare: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5619,7 +5625,7 @@ importers: examples/solid/start-basic: dependencies: '@tanstack/solid-router': - specifier: ^1.120.3 + specifier: ^1.120.15 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -5795,6 +5801,9 @@ importers: react-dom: specifier: ^19.0.0 version: 19.0.0(react@19.0.0) + vibe-rules: + specifier: ^0.2.55 + version: 0.2.55 zod: specifier: ^3.24.2 version: 3.24.2 @@ -11371,6 +11380,13 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -11480,6 +11496,10 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@13.1.0: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} @@ -11642,6 +11662,9 @@ packages: css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -11854,6 +11877,9 @@ packages: dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -11861,9 +11887,16 @@ packages: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -11916,6 +11949,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + encoding-sniffer@0.2.0: + resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -12590,6 +12626,9 @@ packages: html-entities@2.5.2: resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + html-link-extractor@1.0.5: + resolution: {integrity: sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==} + html-minifier-terser@6.1.0: resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} engines: {node: '>=12'} @@ -12614,6 +12653,9 @@ packages: htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + http-deceiver@1.2.7: resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} @@ -13261,12 +13303,20 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + markdown-link-extractor@4.0.2: + resolution: {integrity: sha512-5cUOu4Vwx1wenJgxaudsJ8xwLUMN7747yDJX3V/L7+gi3e4MsCm7w5nbrDQQy8nEfnl4r5NV3pDXMAjhGXYXAw==} + marked-terminal@7.3.0: resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} engines: {node: '>=16.0.0'} peerDependencies: marked: '>=1 <16' + marked@12.0.2: + resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==} + engines: {node: '>= 18'} + hasBin: true + marked@9.1.6: resolution: {integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==} engines: {node: '>= 16'} @@ -13503,6 +13553,7 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead node-emoji@2.2.0: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} @@ -13711,6 +13762,12 @@ packages: parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + parse5@5.1.1: resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} @@ -14861,6 +14918,10 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici@6.21.3: + resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} + engines: {node: '>=18.17'} + unenv@1.10.0: resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} @@ -15059,6 +15120,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vibe-rules@0.2.55: + resolution: {integrity: sha512-2CdweOc2B6U5ML2XhMMdS/s0mvJggHWkdZykJFth80N0mlrg0XTVL8vUYBbyiF1VJFQfzOVcInhv6lOoG6f2qg==} + hasBin: true + vinxi@0.5.1: resolution: {integrity: sha512-jvl2hJ0fyWwfDVQdDDHCJiVxqU4k0A6kFAnljS0kIjrGfhdTvKEWIoj0bcJgMyrKhxNMoZZGmHZsstQgjDIL3g==} hasBin: true @@ -15443,6 +15508,9 @@ packages: zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@3.25.32: + resolution: {integrity: sha512-OSm2xTIRfW8CV5/QKgngwmQW/8aPfGdaQFlrGoErlgg/Epm7cjb6K6VEyExfe65a3VybUOnu381edLb0dfJl0g==} + snapshots: '@adobe/css-tools@4.4.1': {} @@ -20091,6 +20159,29 @@ snapshots: check-error@2.1.1: {} + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.0 + htmlparser2: 9.1.0 + parse5: 7.2.1 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.21.3 + whatwg-mimetype: 4.0.0 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -20198,6 +20289,8 @@ snapshots: commander@10.0.1: {} + commander@11.1.0: {} + commander@13.1.0: {} commander@2.20.3: {} @@ -20349,6 +20442,14 @@ snapshots: domutils: 2.8.0 nth-check: 2.1.1 + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + css-what@6.1.0: {} css.escape@1.5.1: {} @@ -20484,18 +20585,34 @@ snapshots: domhandler: 4.3.1 entities: 2.2.0 + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + domelementtype@2.3.0: {} domhandler@4.3.1: dependencies: domelementtype: 2.3.0 + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + domutils@2.8.0: dependencies: dom-serializer: 1.4.1 domelementtype: 2.3.0 domhandler: 4.3.1 + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dot-case@3.0.4: dependencies: no-case: 3.0.4 @@ -20539,6 +20656,11 @@ snapshots: encodeurl@2.0.0: {} + encoding-sniffer@0.2.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -21493,6 +21615,10 @@ snapshots: html-entities@2.5.2: {} + html-link-extractor@1.0.5: + dependencies: + cheerio: 1.0.0 + html-minifier-terser@6.1.0: dependencies: camel-case: 4.1.2 @@ -21523,6 +21649,13 @@ snapshots: domutils: 2.8.0 entities: 2.2.0 + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + http-deceiver@1.2.7: {} http-errors@1.6.3: @@ -22121,6 +22254,11 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + markdown-link-extractor@4.0.2: + dependencies: + html-link-extractor: 1.0.5 + marked: 12.0.2 + marked-terminal@7.3.0(marked@9.1.6): dependencies: ansi-escapes: 7.0.0 @@ -22132,6 +22270,8 @@ snapshots: node-emoji: 2.2.0 supports-hyperlinks: 3.1.0 + marked@12.0.2: {} + marked@9.1.6: {} math-intrinsics@1.1.0: {} @@ -22681,6 +22821,15 @@ snapshots: dependencies: parse5: 6.0.1 + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.2.1 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.2.1 + parse5@5.1.1: {} parse5@6.0.1: {} @@ -23867,6 +24016,8 @@ snapshots: undici-types@6.20.0: {} + undici@6.21.3: {} + unenv@1.10.0: dependencies: consola: 3.4.0 @@ -24027,6 +24178,13 @@ snapshots: vary@1.1.2: {} + vibe-rules@0.2.55: + dependencies: + chalk: 4.1.2 + commander: 11.1.0 + fs-extra: 11.3.0 + zod: 3.25.32 + vinxi@0.5.1(@types/node@22.13.4)(db0@0.2.3)(ioredis@5.4.2)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(typescript@5.8.2)(yaml@2.7.0): dependencies: '@babel/core': 7.26.8 @@ -24655,3 +24813,5 @@ snapshots: readable-stream: 4.7.0 zod@3.24.2: {} + + zod@3.25.32: {} diff --git a/scripts/llms-generate.mjs b/scripts/llms-generate.mjs new file mode 100755 index 0000000000..e3f5b6155d --- /dev/null +++ b/scripts/llms-generate.mjs @@ -0,0 +1,159 @@ +#!/usr/bin/env node +import fs from 'node:fs' +import path from 'node:path' + +const DOCS_DIR = '../../docs' +const LLMS_DIR = './llms' +const RULES_DIR = './llms/rules' + +const packages = { + 'react-router': [ + { + paths: [`${DOCS_DIR}/router/framework/react/api/router`], + description: 'TanStack Router: API', + name: 'api', + globs: ['src/**/*.ts', 'src/**/*.tsx'], + }, + { + paths: [`${DOCS_DIR}/router/framework/react/guide`], + description: 'TanStack Router: Guide', + name: 'guide', + globs: ['src/**/*.ts', 'src/**/*.tsx'], + }, + { + paths: [`${DOCS_DIR}/router/framework/react/routing`], + description: 'TanStack Router: Routing', + name: 'routing', + globs: ['src/**/*.ts', 'src/**/*.tsx'], + }, + { + paths: [ + `${DOCS_DIR}/router/framework/react/overview.md`, + `${DOCS_DIR}/router/framework/react/quick-start.md`, + `${DOCS_DIR}/router/framework/react/devtools.md`, + `${DOCS_DIR}/router/framework/react/migrate-from-react-router.md`, + `${DOCS_DIR}/router/framework/react/migrate-from-react-location.md`, + `${DOCS_DIR}/router/framework/react/faq.md`, + ], + description: 'TanStack Router: Setup and Architecture', + name: 'setup-and-architecture', + globs: [ + 'package.json', + 'vite.config.ts', + 'tsconfig.json', + 'src/**/*.ts', + 'src/**/*.tsx', + ], + }, + ], +} + +const pkg = process.argv[2] +if (!pkg) { + console.error('Usage: node scripts/llms-generate.mjs ') + process.exit(1) +} +if (!packages[pkg]) { + console.error(`Package '${pkg}' not found`) + process.exit(1) +} + +function camelCase(str) { + return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) +} + +function extractFrontMatter(content) { + const frontMatterEndIndex = content.indexOf('---', 3) + 3 + const frontMatter = content.slice(0, frontMatterEndIndex) + const bodyContent = content.slice(frontMatterEndIndex).trim() + return { frontMatter, bodyContent } +} + +function convertMarkdownToTypeScript(markdownContent) { + const sanitizedContent = markdownContent + .replace(/`/g, '\\`') + .replace(/\$\{/g, '\\${') + return `const content = \`${sanitizedContent}\`;\n\nexport default content;\n` +} + +function mergeFiles(files, outputFile) { + let mergedContent = '' + for (const file of files) { + const content = fs.readFileSync(file, 'utf-8') + const { frontMatter, bodyContent } = extractFrontMatter(content) + const title = frontMatter.match(/title:\s*(.+)/)[1].trim() + mergedContent += `# ${title}\n\n${bodyContent}\n\n` + } + fs.writeFileSync( + outputFile, + convertMarkdownToTypeScript(mergedContent), + 'utf-8', + ) +} + +if (!fs.existsSync(RULES_DIR)) { + fs.mkdirSync(RULES_DIR, { recursive: true }) +} + +// Create the rules files +const imports = [] +const rules = [] +for (const { paths, name, description, globs } of packages[pkg]) { + const files = [] + for (const p of paths) { + if (fs.existsSync(p) && fs.statSync(p).isDirectory()) { + files.push( + ...fs + .readdirSync(p) + .filter((file) => file.endsWith('.md')) + .map((file) => path.join(p, file)), + ) + } else { + files.push(p) + } + } + mergeFiles(files.flat(), path.join(RULES_DIR, `${name}.ts`)) + imports.push(`import ${camelCase(name)} from './rules/${name}.js'`) + rules.push(`{ + name: '${name}', + description: '${description}', + rule: ${camelCase(name)}, + alwaysApply: false, + globs: [${globs.map((glob) => `'${glob}'`).join(', ')}], +}`) +} + +// Create the index.ts file +const indexFile = path.join(LLMS_DIR, 'index.ts') +const indexContent = `${imports.join('\n')} + +import type { PackageRuleItem } from 'vibe-rules' + +const rules: Array = [ + ${rules.join(',\n')} +] + +export default rules +` +fs.writeFileSync(indexFile, indexContent, 'utf-8') + +fs.writeFileSync( + path.join(LLMS_DIR, 'tsconfig.json'), + JSON.stringify( + { + compilerOptions: { + module: 'ESNext', + moduleResolution: 'bundler', + target: 'ESNext', + lib: ['ESNext', 'DOM'], + declaration: true, + outDir: '../dist/llms', + }, + include: ['./index.ts', './rules/*.ts'], + exclude: ['node_modules', 'dist'], + }, + null, + 2, + ), + 'utf-8', +) diff --git a/scripts/verify-links.ts b/scripts/verify-links.ts new file mode 100644 index 0000000000..2297d9814f --- /dev/null +++ b/scripts/verify-links.ts @@ -0,0 +1,133 @@ +import { existsSync, readFileSync, statSync } from 'node:fs' +import path, { resolve } from 'node:path' +import fg from 'fast-glob' +// @ts-ignore +import markdownLinkExtractor from 'markdown-link-extractor' + +function isRelativeLink(link: string) { + return ( + link && + !link.startsWith('/') && + !link.startsWith('http://') && + !link.startsWith('https://') && + !link.startsWith('//') && + !link.startsWith('#') && + !link.startsWith('mailto:') + ) +} + +function normalizePath(p: string): string { + // Remove any trailing .md + p = p.replace(`${path.extname(p)}`, '') + return p +} + +function fileExistsForLink( + link: string, + markdownFile: string, + errors: Array, +): boolean { + // Remove hash if present + const filePart = link.split('#')[0] + // If the link is empty after removing hash, it's not a file + if (!filePart) return false + + // Normalize the markdown file path + markdownFile = normalizePath(markdownFile) + + // Normalize the path + const normalizedPath = normalizePath(filePart) + + // Resolve the path relative to the markdown file's directory + let absPath = resolve(markdownFile, normalizedPath) + + // Ensure the resolved path is within /docs + const docsRoot = resolve('docs') + if (!absPath.startsWith(docsRoot)) { + errors.push({ + link, + markdownFile, + resolvedPath: absPath, + reason: 'navigates above /docs, invalid', + }) + return false + } + + // Check if this is an example path + const isExample = absPath.includes('/examples/') + + let exists = false + + if (isExample) { + // Transform /docs/framework/{framework}/examples/ to /examples/{framework}/ + absPath = absPath.replace( + /\/docs\/framework\/([^/]+)\/examples\//, + '/examples/$1/', + ) + // For examples, we want to check if the directory exists + exists = existsSync(absPath) && statSync(absPath).isDirectory() + } else { + // For non-examples, we want to check if the .md file exists + if (!absPath.endsWith('.md')) { + absPath = `${absPath}.md` + } + exists = existsSync(absPath) + } + + if (!exists) { + errors.push({ + link, + markdownFile, + resolvedPath: absPath, + reason: 'not found', + }) + } + return exists +} + +async function findMarkdownLinks() { + // Find all markdown files in docs directory + const markdownFiles = await fg('docs/**/*.md', { + ignore: ['**/node_modules/**'], + }) + + console.log(`Found ${markdownFiles.length} markdown files\n`) + + const errors: Array = [] + + // Process each file + for (const file of markdownFiles) { + const content = readFileSync(file, 'utf-8') + const links: Array = markdownLinkExtractor(content) + + const filteredLinks = links.filter((link: any) => { + if (typeof link === 'string') { + return isRelativeLink(link) + } else if (link && typeof link.href === 'string') { + return isRelativeLink(link.href) + } + return false + }) + + if (filteredLinks.length > 0) { + filteredLinks.forEach((link: any) => { + const href = typeof link === 'string' ? link : link.href + fileExistsForLink(href, file, errors) + }) + } + } + + if (errors.length > 0) { + console.log(`\n❌ Found ${errors.length} broken links:`) + errors.forEach((err) => { + console.log( + `${err.link}\n in: ${err.markdownFile}\n path: ${err.resolvedPath}\n why: ${err.reason}\n`, + ) + }) + process.exit(1) + } else { + console.log('\n✅ No broken links found!') + } +} + +findMarkdownLinks().catch(console.error)