Repository: TanStack/router Branch: main Commit: 21e39bde0832 Files: 8559 Total size: 17.6 MB Directory structure: gitextract_94dotqtg/ ├── .changeset/ │ └── config.json ├── .devcontainer/ │ └── devcontainer.json ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ └── config.yml │ ├── renovate.json │ └── workflows/ │ ├── autofix.yml │ ├── bundle-size.yml │ ├── check-skills.yml │ ├── client-nav-benchmarks.yml │ ├── labeler.yml │ ├── notify-playbooks.yml │ ├── pr.yml │ ├── release.yml │ └── validate-skills.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .nx/ │ └── workflows/ │ └── dynamic-changesets.yaml ├── .prettierignore ├── AGENTS.md ├── CONTRIBUTING.md ├── DEBUGGING.md ├── LICENSE ├── README.md ├── _artifacts/ │ ├── domain_map.yaml │ ├── skill_spec.md │ ├── skill_tree.yaml │ ├── start_domain_map.yaml │ └── start_skill_tree.yaml ├── benchmarks/ │ ├── bundle-size/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── results/ │ │ │ └── .gitkeep │ │ ├── scenarios/ │ │ │ ├── react-router-full/ │ │ │ │ ├── index.html │ │ │ │ ├── src/ │ │ │ │ │ ├── main.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── react-router-minimal/ │ │ │ │ ├── index.html │ │ │ │ ├── src/ │ │ │ │ │ ├── main.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── react-start-full/ │ │ │ │ ├── src/ │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── react-start-minimal/ │ │ │ │ ├── src/ │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── solid-router-full/ │ │ │ │ ├── index.html │ │ │ │ ├── src/ │ │ │ │ │ ├── main.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── solid-router-minimal/ │ │ │ │ ├── index.html │ │ │ │ ├── src/ │ │ │ │ │ ├── main.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── solid-start-full/ │ │ │ │ ├── src/ │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── solid-start-minimal/ │ │ │ │ ├── src/ │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ ├── vue-router-full/ │ │ │ │ ├── index.html │ │ │ │ ├── src/ │ │ │ │ │ ├── main.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite.config.ts │ │ │ └── vue-router-minimal/ │ │ │ ├── index.html │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ └── vite.config.ts │ │ └── tsconfig.json │ ├── client-nav/ │ │ ├── README.md │ │ ├── jsdom.ts │ │ ├── package.json │ │ ├── react/ │ │ │ ├── app.tsx │ │ │ ├── setup.ts │ │ │ ├── speed.bench.ts │ │ │ ├── speed.flame.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── solid/ │ │ │ ├── app.tsx │ │ │ ├── setup.ts │ │ │ ├── speed.bench.ts │ │ │ ├── speed.flame.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── tsconfig.json │ │ ├── vitest.config.ts │ │ ├── vitest.setup.ts │ │ └── vue/ │ │ ├── app.tsx │ │ ├── setup.ts │ │ ├── speed.bench.ts │ │ ├── speed.flame.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── ssr/ │ ├── README.md │ ├── bench-utils.ts │ ├── package.json │ ├── react/ │ │ ├── speed.bench.ts │ │ ├── src/ │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── $a.$b.$c.$d.tsx │ │ │ │ ├── $a.$b.$c.tsx │ │ │ │ ├── $a.$b.tsx │ │ │ │ ├── $a.tsx │ │ │ │ └── __root.tsx │ │ │ └── workload.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid/ │ │ ├── speed.bench.ts │ │ ├── src/ │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── $a.$b.$c.$d.tsx │ │ │ │ ├── $a.$b.$c.tsx │ │ │ │ ├── $a.$b.tsx │ │ │ │ ├── $a.tsx │ │ │ │ └── __root.tsx │ │ │ └── workload.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── tsconfig.json │ ├── vitest.config.ts │ └── vue/ │ ├── speed.bench.ts │ ├── src/ │ │ ├── routeTree.gen.ts │ │ ├── router.tsx │ │ ├── routes/ │ │ │ ├── $a.$b.$c.$d.tsx │ │ │ ├── $a.$b.$c.tsx │ │ │ ├── $a.$b.tsx │ │ │ ├── $a.tsx │ │ │ └── __root.tsx │ │ └── workload.tsx │ ├── tsconfig.json │ └── vite.config.ts ├── docs/ │ ├── router/ │ │ ├── api/ │ │ │ ├── file-based-routing.md │ │ │ ├── router/ │ │ │ │ ├── ActiveLinkOptionsType.md │ │ │ │ ├── AsyncRouteComponentType.md │ │ │ │ ├── FileRouteClass.md │ │ │ │ ├── LinkOptionsType.md │ │ │ │ ├── LinkPropsType.md │ │ │ │ ├── MatchRouteOptionsType.md │ │ │ │ ├── NavigateOptionsType.md │ │ │ │ ├── NotFoundErrorType.md │ │ │ │ ├── NotFoundRouteClass.md │ │ │ │ ├── ParsedHistoryStateType.md │ │ │ │ ├── ParsedLocationType.md │ │ │ │ ├── RedirectType.md │ │ │ │ ├── RegisterType.md │ │ │ │ ├── RootRouteClass.md │ │ │ │ ├── RouteApiClass.md │ │ │ │ ├── RouteApiType.md │ │ │ │ ├── RouteClass.md │ │ │ │ ├── RouteMaskType.md │ │ │ │ ├── RouteMatchType.md │ │ │ │ ├── RouteOptionsType.md │ │ │ │ ├── RouteType.md │ │ │ │ ├── RouterClass.md │ │ │ │ ├── RouterEventsType.md │ │ │ │ ├── RouterOptionsType.md │ │ │ │ ├── RouterStateType.md │ │ │ │ ├── RouterType.md │ │ │ │ ├── ToMaskOptionsType.md │ │ │ │ ├── ToOptionsType.md │ │ │ │ ├── UseMatchRouteOptionsType.md │ │ │ │ ├── ViewTransitionOptionsType.md │ │ │ │ ├── awaitComponent.md │ │ │ │ ├── catchBoundaryComponent.md │ │ │ │ ├── catchNotFoundComponent.md │ │ │ │ ├── clientOnlyComponent.md │ │ │ │ ├── createFileRouteFunction.md │ │ │ │ ├── createLazyFileRouteFunction.md │ │ │ │ ├── createLazyRouteFunction.md │ │ │ │ ├── createRootRouteFunction.md │ │ │ │ ├── createRootRouteWithContextFunction.md │ │ │ │ ├── createRouteFunction.md │ │ │ │ ├── createRouteMaskFunction.md │ │ │ │ ├── createRouterFunction.md │ │ │ │ ├── defaultGlobalNotFoundComponent.md │ │ │ │ ├── deferFunction.md │ │ │ │ ├── errorComponentComponent.md │ │ │ │ ├── getRouteApiFunction.md │ │ │ │ ├── historyStateInterface.md │ │ │ │ ├── isNotFoundFunction.md │ │ │ │ ├── isRedirectFunction.md │ │ │ │ ├── lazyRouteComponentFunction.md │ │ │ │ ├── linkComponent.md │ │ │ │ ├── linkOptions.md │ │ │ │ ├── matchRouteComponent.md │ │ │ │ ├── navigateComponent.md │ │ │ │ ├── notFoundComponentComponent.md │ │ │ │ ├── notFoundFunction.md │ │ │ │ ├── outletComponent.md │ │ │ │ ├── redirectFunction.md │ │ │ │ ├── retainSearchParamsFunction.md │ │ │ │ ├── rootRouteWithContextFunction.md │ │ │ │ ├── stripSearchParamsFunction.md │ │ │ │ ├── useAwaitedHook.md │ │ │ │ ├── useBlockerHook.md │ │ │ │ ├── useCanGoBack.md │ │ │ │ ├── useChildMatchesHook.md │ │ │ │ ├── useLinkPropsHook.md │ │ │ │ ├── useLoaderDataHook.md │ │ │ │ ├── useLoaderDepsHook.md │ │ │ │ ├── useLocationHook.md │ │ │ │ ├── useMatchHook.md │ │ │ │ ├── useMatchRouteHook.md │ │ │ │ ├── useMatchesHook.md │ │ │ │ ├── useNavigateHook.md │ │ │ │ ├── useParamsHook.md │ │ │ │ ├── useParentMatchesHook.md │ │ │ │ ├── useRouteContextHook.md │ │ │ │ ├── useRouterHook.md │ │ │ │ ├── useRouterStateHook.md │ │ │ │ └── useSearchHook.md │ │ │ └── router.md │ │ ├── comparison.md │ │ ├── config.json │ │ ├── decisions-on-dx.md │ │ ├── devtools.md │ │ ├── eslint/ │ │ │ ├── create-route-property-order.md │ │ │ └── eslint-plugin-router.md │ │ ├── faq.md │ │ ├── guide/ │ │ │ ├── authenticated-routes.md │ │ │ ├── automatic-code-splitting.md │ │ │ ├── code-splitting.md │ │ │ ├── creating-a-router.md │ │ │ ├── custom-link.md │ │ │ ├── custom-search-param-serialization.md │ │ │ ├── data-loading.md │ │ │ ├── data-mutations.md │ │ │ ├── deferred-data-loading.md │ │ │ ├── document-head-management.md │ │ │ ├── external-data-loading.md │ │ │ ├── history-types.md │ │ │ ├── internationalization-i18n.md │ │ │ ├── link-options.md │ │ │ ├── navigation-blocking.md │ │ │ ├── navigation.md │ │ │ ├── not-found-errors.md │ │ │ ├── outlets.md │ │ │ ├── parallel-routes.md │ │ │ ├── path-params.md │ │ │ ├── preloading.md │ │ │ ├── render-optimizations.md │ │ │ ├── route-masking.md │ │ │ ├── router-context.md │ │ │ ├── scroll-restoration.md │ │ │ ├── search-params.md │ │ │ ├── ssr.md │ │ │ ├── static-route-data.md │ │ │ ├── type-safety.md │ │ │ ├── type-utilities.md │ │ │ └── url-rewrites.md │ │ ├── how-to/ │ │ │ ├── README.md │ │ │ ├── arrays-objects-dates-search-params.md │ │ │ ├── debug-router-issues.md │ │ │ ├── deploy-to-production.md │ │ │ ├── drafts/ │ │ │ │ ├── README.md │ │ │ │ ├── build-search-filtering-systems.draft.md │ │ │ │ ├── optimize-search-param-performance.draft.md │ │ │ │ └── search-params-in-forms.draft.md │ │ │ ├── install.md │ │ │ ├── integrate-chakra-ui.md │ │ │ ├── integrate-framer-motion.md │ │ │ ├── integrate-material-ui.md │ │ │ ├── integrate-shadcn-ui.md │ │ │ ├── migrate-from-react-router.md │ │ │ ├── navigate-with-search-params.md │ │ │ ├── setup-auth-providers.md │ │ │ ├── setup-authentication.md │ │ │ ├── setup-basic-search-params.md │ │ │ ├── setup-rbac.md │ │ │ ├── setup-ssr.md │ │ │ ├── setup-testing.md │ │ │ ├── share-search-params-across-routes.md │ │ │ ├── test-file-based-routing.md │ │ │ ├── use-environment-variables.md │ │ │ └── validate-search-params.md │ │ ├── installation/ │ │ │ ├── manual.md │ │ │ ├── migrate-from-react-location.md │ │ │ ├── migrate-from-react-router.md │ │ │ ├── with-esbuild.md │ │ │ ├── with-router-cli.md │ │ │ ├── with-rspack.md │ │ │ ├── with-vite.md │ │ │ └── with-webpack.md │ │ ├── integrations/ │ │ │ └── query.md │ │ ├── llm-support.md │ │ ├── overview.md │ │ ├── quick-start.md │ │ └── routing/ │ │ ├── code-based-routing.md │ │ ├── file-based-routing.md │ │ ├── file-naming-conventions.md │ │ ├── route-matching.md │ │ ├── route-trees.md │ │ ├── routing-concepts.md │ │ └── virtual-file-routes.md │ └── start/ │ ├── config.json │ └── framework/ │ ├── react/ │ │ ├── build-from-scratch.md │ │ ├── comparison.md │ │ ├── getting-started.md │ │ ├── guide/ │ │ │ ├── authentication-overview.md │ │ │ ├── authentication.md │ │ │ ├── cdn-asset-urls.md │ │ │ ├── client-entry-point.md │ │ │ ├── code-execution-patterns.md │ │ │ ├── databases.md │ │ │ ├── environment-functions.md │ │ │ ├── environment-variables.md │ │ │ ├── error-boundaries.md │ │ │ ├── execution-model.md │ │ │ ├── hosting.md │ │ │ ├── hydration-errors.md │ │ │ ├── import-protection.md │ │ │ ├── isr.md │ │ │ ├── llmo.md │ │ │ ├── middleware.md │ │ │ ├── observability.md │ │ │ ├── path-aliases.md │ │ │ ├── rendering-markdown.md │ │ │ ├── routing.md │ │ │ ├── selective-ssr.md │ │ │ ├── seo.md │ │ │ ├── server-entry-point.md │ │ │ ├── server-functions.md │ │ │ ├── server-routes.md │ │ │ ├── spa-mode.md │ │ │ ├── static-prerendering.md │ │ │ ├── static-server-functions.md │ │ │ ├── streaming-data-from-server-functions.md │ │ │ └── tailwind-integration.md │ │ ├── migrate-from-next-js.md │ │ ├── overview.md │ │ ├── quick-start.md │ │ ├── start-vs-nextjs.md │ │ └── tutorial/ │ │ ├── fetching-external-api.md │ │ └── reading-writing-file.md │ └── solid/ │ ├── build-from-scratch.md │ ├── getting-started.md │ ├── guide/ │ │ ├── authentication-overview.md │ │ ├── authentication.md │ │ ├── client-entry-point.md │ │ ├── code-execution-patterns.md │ │ ├── databases.md │ │ ├── environment-functions.md │ │ ├── environment-variables.md │ │ ├── error-boundaries.md │ │ ├── execution-model.md │ │ ├── hosting.md │ │ ├── hydration-errors.md │ │ ├── import-protection.md │ │ ├── llmo.md │ │ ├── middleware.md │ │ ├── observability.md │ │ ├── path-aliases.md │ │ ├── reading-writing-file.md │ │ ├── routing.md │ │ ├── selective-ssr.md │ │ ├── seo.md │ │ ├── server-entry-point.md │ │ ├── server-functions.md │ │ ├── server-routes.md │ │ ├── spa-mode.md │ │ ├── static-prerendering.md │ │ ├── static-server-functions.md │ │ └── tailwind-integration.md │ ├── overview.md │ ├── quick-start.md │ └── tutorial/ │ ├── fetching-external-api.md │ └── reading-writing-file.md ├── e2e/ │ ├── e2e-utils/ │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── derivePort.ts │ │ │ ├── e2eSetupTeardown.ts │ │ │ ├── fixture.ts │ │ │ ├── index.ts │ │ │ ├── localDummyServer.ts │ │ │ ├── posts.ts │ │ │ ├── resolve-runtime-suffix.ts │ │ │ ├── to-runtime-path.ts │ │ │ └── users.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── react-router/ │ │ ├── basepath-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── redirect.tsx │ │ │ │ ├── redirectReload.tsx │ │ │ │ └── scroll-error.tsx │ │ │ ├── tests/ │ │ │ │ ├── reload-document.test.ts │ │ │ │ ├── scroll-restoration-session-storage-error.test.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── params.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-esbuild-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── esbuild.config.js │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── (group)/ │ │ │ │ │ ├── _layout.inside.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── lazyinside.lazy.tsx │ │ │ │ │ └── lazyinside.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── _layout/ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ └── _layout-2.tsx │ │ │ │ ├── _layout.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ └── posts.tsx │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ └── RenderCounter.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (another-group)/ │ │ │ │ │ │ └── onlyrouteinside.tsx │ │ │ │ │ ├── (group)/ │ │ │ │ │ │ ├── _layout.insidelayout.tsx │ │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ │ ├── inside.tsx │ │ │ │ │ │ ├── lazyinside.lazy.tsx │ │ │ │ │ │ ├── lazyinside.tsx │ │ │ │ │ │ └── subfolder/ │ │ │ │ │ │ └── inside.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── anchor.tsx │ │ │ │ │ ├── component-types-test.tsx │ │ │ │ │ ├── editing-a.tsx │ │ │ │ │ ├── editing-b.tsx │ │ │ │ │ ├── fullpath-test/ │ │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ │ ├── $id.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── hover-preload-hash.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── lazy-error.lazy.tsx │ │ │ │ │ ├── lazy-error.tsx │ │ │ │ │ ├── masks.admin.$userId.tsx │ │ │ │ │ ├── masks.public.$username.tsx │ │ │ │ │ ├── masks.tsx │ │ │ │ │ ├── non-nested/ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ ├── $baz.index.tsx │ │ │ │ │ │ │ ├── $baz.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar.$foo.index.tsx │ │ │ │ │ │ │ ├── $baz_.bar.$foo.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar.$foo_.qux.tsx │ │ │ │ │ │ │ ├── $baz_.bar.index.tsx │ │ │ │ │ │ │ ├── $baz_.bar.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar_.qux.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── named/ │ │ │ │ │ │ │ ├── $baz.foo.tsx │ │ │ │ │ │ │ ├── $baz.index.tsx │ │ │ │ │ │ │ ├── $baz.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── path/ │ │ │ │ │ │ │ ├── baz.foo.tsx │ │ │ │ │ │ │ ├── baz.index.tsx │ │ │ │ │ │ │ ├── baz.route.tsx │ │ │ │ │ │ │ ├── baz_.bar.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── prefix/ │ │ │ │ │ │ │ ├── prefix{$baz}.foo.tsx │ │ │ │ │ │ │ ├── prefix{$baz}.index.tsx │ │ │ │ │ │ │ ├── prefix{$baz}.route.tsx │ │ │ │ │ │ │ ├── prefix{$baz}_.bar.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ └── suffix/ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── {$baz}suffix.foo.tsx │ │ │ │ │ │ ├── {$baz}suffix.index.tsx │ │ │ │ │ │ ├── {$baz}suffix.route.tsx │ │ │ │ │ │ └── {$baz}suffix_.bar.tsx │ │ │ │ │ ├── notRemountDeps.tsx │ │ │ │ │ ├── params-ps/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── named/ │ │ │ │ │ │ │ ├── $foo/ │ │ │ │ │ │ │ │ ├── $bar.$baz.tsx │ │ │ │ │ │ │ │ ├── $bar.route.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── prefix{$foo}.tsx │ │ │ │ │ │ │ └── {$foo}suffix.tsx │ │ │ │ │ │ ├── non-nested/ │ │ │ │ │ │ │ ├── $foo_/ │ │ │ │ │ │ │ │ ├── $bar.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── strict-false/ │ │ │ │ │ │ │ ├── $version.route.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ └── wildcard/ │ │ │ │ │ │ ├── $.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── prefix@대{$}.tsx │ │ │ │ │ │ ├── prefix{$}.tsx │ │ │ │ │ │ ├── {$}suffix.tsx │ │ │ │ │ │ └── {$}suffix@대.tsx │ │ │ │ │ ├── params.single.$value.tsx │ │ │ │ │ ├── pathless-layout/ │ │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ │ ├── child.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.edit.tsx │ │ │ │ │ ├── redirect/ │ │ │ │ │ │ ├── $target/ │ │ │ │ │ │ │ ├── destination.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ ├── via-loader.tsx │ │ │ │ │ │ │ ├── via-route-redirect-beforeLoad.tsx │ │ │ │ │ │ │ ├── via-route-redirect-loader.tsx │ │ │ │ │ │ │ ├── via-routeApi-redirect-beforeLoad.tsx │ │ │ │ │ │ │ └── via-routeApi-redirect-loader.tsx │ │ │ │ │ │ ├── $target.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── preload/ │ │ │ │ │ │ ├── first.tsx │ │ │ │ │ │ ├── second.tsx │ │ │ │ │ │ └── third.tsx │ │ │ │ │ ├── relative/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── link/ │ │ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── path/ │ │ │ │ │ │ │ │ ├── $path/ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── relative-link-a.tsx │ │ │ │ │ │ │ ├── relative-link-b.tsx │ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ │ └── with-search/ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── useNavigate/ │ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── path/ │ │ │ │ │ │ │ ├── $path/ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── relative-useNavigate-a.tsx │ │ │ │ │ │ ├── relative-useNavigate-b.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ └── with-search/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── remountDeps.tsx │ │ │ │ │ ├── search-params/ │ │ │ │ │ │ ├── default.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── structural-sharing.$enabled.tsx │ │ │ │ │ └── 대한민국/ │ │ │ │ │ ├── route.tsx │ │ │ │ │ ├── wildcard.$.tsx │ │ │ │ │ └── 🚀.$id.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── fullpath-types.spec.ts │ │ │ │ ├── hover-preload-hash.spec.ts │ │ │ │ ├── mask.spec.ts │ │ │ │ ├── non-nested-paths.spec.ts │ │ │ │ ├── open-redirect-prevention.spec.ts │ │ │ │ ├── params.spec.ts │ │ │ │ ├── redirect.spec.ts │ │ │ │ ├── relative.spec.ts │ │ │ │ ├── scroll-into-view.spec.ts │ │ │ │ ├── search-params.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-file-based-code-splitting/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── shared-singleton.tsx │ │ │ │ │ ├── viewport-test.tsx │ │ │ │ │ └── without-loader.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── preload.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ └── shared-bindings.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-react-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-react-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── postsQueryOptions.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── router-events.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-virtual-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-virtual-named-export-config-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── escaped-special-strings/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── [_]layout.tsx │ │ │ │ ├── [index].tsx │ │ │ │ ├── [lazy].tsx │ │ │ │ ├── [route].tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── blog[_].tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ ├── escaped-routes.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── generator-cli-only/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ ├── tsr.config.json │ │ │ └── vite.config.js │ │ ├── i18n-paraglide/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── messages/ │ │ │ │ ├── de.json │ │ │ │ └── en.json │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── project.inlang/ │ │ │ │ ├── .gitignore │ │ │ │ ├── project_id │ │ │ │ └── settings.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ └── navigation.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── js-only-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.jsx │ │ │ │ ├── posts.js │ │ │ │ ├── routeTree.gen.js │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.jsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.jsx │ │ │ │ │ │ │ └── route-b.jsx │ │ │ │ │ │ └── _nested-layout.jsx │ │ │ │ │ ├── _pathlessLayout.jsx │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── posts.$postId.jsx │ │ │ │ │ ├── posts.index.jsx │ │ │ │ │ └── posts.route.jsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── rspack-basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── rspack-basic-virtual-named-export-config-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── routes.ts │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── tsr.config.json │ │ ├── scroll-restoration-sandbox-vite/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.browser.config.ts │ │ │ ├── playwright.hash.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (tests)/ │ │ │ │ │ │ ├── lazy-page.lazy.tsx │ │ │ │ │ │ ├── lazy-page.tsx │ │ │ │ │ │ ├── lazy-with-loader-page.lazy.tsx │ │ │ │ │ │ ├── lazy-with-loader-page.tsx │ │ │ │ │ │ ├── normal-page.tsx │ │ │ │ │ │ ├── page-with-search.tsx │ │ │ │ │ │ └── virtual-page.lazy.tsx │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── scroll-block.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── sentry-integration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ └── view-transitions/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── main.tsx │ │ │ ├── posts.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── explore.tsx │ │ │ │ ├── how-it-works.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ └── posts.route.tsx │ │ │ └── styles.css │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ └── setup/ │ │ │ ├── global.setup.ts │ │ │ └── global.teardown.ts │ │ ├── tsconfig.json │ │ └── vite.config.js │ ├── react-start/ │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── async-user-script.js │ │ │ │ ├── before-scripts-async-script.js │ │ │ │ ├── before-scripts-script.js │ │ │ │ ├── head-async-script.js │ │ │ │ ├── head-script.js │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ ├── site.webmanifest │ │ │ │ └── user-script.js │ │ │ ├── server.js │ │ │ ├── src/ │ │ │ │ ├── client.tsx │ │ │ │ ├── components/ │ │ │ │ │ ├── CustomMessage.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── RedirectOnClick.tsx │ │ │ │ │ ├── WindowSize.tsx │ │ │ │ │ └── throwRedirect.ts │ │ │ │ ├── raw-stream-fns.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── users.$id.ts │ │ │ │ │ ├── api.users.ts │ │ │ │ │ ├── async-scripts.tsx │ │ │ │ │ ├── client-only.tsx │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── foo/ │ │ │ │ │ │ └── $bar/ │ │ │ │ │ │ └── $qux/ │ │ │ │ │ │ ├── _here/ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── _here.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── inline-scripts.tsx │ │ │ │ │ ├── links.tsx │ │ │ │ │ ├── multi-cookie-redirect/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.tsx │ │ │ │ │ ├── not-found/ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ ├── b/ │ │ │ │ │ │ │ │ ├── c/ │ │ │ │ │ │ │ │ │ ├── d.tsx │ │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── parent-boundary/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ │ └── via-beforeLoad.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── via-beforeLoad-target-root.tsx │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ └── via-loader.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── raw-stream/ │ │ │ │ │ │ ├── client-call.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── ssr-binary-hint.tsx │ │ │ │ │ │ ├── ssr-mixed.tsx │ │ │ │ │ │ ├── ssr-multiple.tsx │ │ │ │ │ │ ├── ssr-single.tsx │ │ │ │ │ │ └── ssr-text-hint.tsx │ │ │ │ │ ├── raw-stream.tsx │ │ │ │ │ ├── redirect/ │ │ │ │ │ │ ├── $target/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── serverFn/ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ │ ├── via-loader.tsx │ │ │ │ │ │ │ │ └── via-useServerFn.tsx │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ └── via-loader.tsx │ │ │ │ │ │ ├── $target.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── scripts.tsx │ │ │ │ │ ├── search-params/ │ │ │ │ │ │ ├── default.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── loader-throws-redirect.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── specialChars/ │ │ │ │ │ │ ├── $param.tsx │ │ │ │ │ │ ├── hash.tsx │ │ │ │ │ │ ├── malformed/ │ │ │ │ │ │ │ ├── $param.tsx │ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ │ └── search.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── search.tsx │ │ │ │ │ │ └── 대한민국.tsx │ │ │ │ │ ├── stream.tsx │ │ │ │ │ ├── type-only-reexport.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── shared-lib/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── middleware.ts │ │ │ │ │ └── typedefs/ │ │ │ │ │ ├── actions.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── client-only.spec.ts │ │ │ │ ├── navigation.spec.ts │ │ │ │ ├── not-found.spec.ts │ │ │ │ ├── open-redirect-prevention.spec.ts │ │ │ │ ├── prerendering.spec.ts │ │ │ │ ├── raw-stream.spec.ts │ │ │ │ ├── redirect.spec.ts │ │ │ │ ├── root-scripts.spec.ts │ │ │ │ ├── script-duplication.spec.ts │ │ │ │ ├── search-params.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ ├── special-characters.spec.ts │ │ │ │ ├── streaming.spec.ts │ │ │ │ ├── type-only-reexport.spec.ts │ │ │ │ └── utils/ │ │ │ │ ├── isPrerender.ts │ │ │ │ ├── isPreview.ts │ │ │ │ └── isSpaMode.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-auth/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── prisma/ │ │ │ │ ├── migrations/ │ │ │ │ │ ├── 20240811183753_init/ │ │ │ │ │ │ └── migration.sql │ │ │ │ │ └── migration_lock.toml │ │ │ │ └── schema.prisma │ │ │ ├── prisma.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Auth.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useMutation.ts │ │ │ │ ├── prisma-generated/ │ │ │ │ │ ├── browser.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── commonInputTypes.ts │ │ │ │ │ ├── enums.ts │ │ │ │ │ ├── internal/ │ │ │ │ │ │ ├── class.ts │ │ │ │ │ │ ├── prismaNamespace.ts │ │ │ │ │ │ └── prismaNamespaceBrowser.ts │ │ │ │ │ ├── models/ │ │ │ │ │ │ └── User.ts │ │ │ │ │ └── models.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ └── signup.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ ├── prisma.ts │ │ │ │ ├── seo.ts │ │ │ │ └── session.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── mock-db-setup.test.ts │ │ │ │ └── mock-db-teardown.test.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-cloudflare/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── static.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ └── seo.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ ├── vite.config.ts │ │ │ ├── worker-configuration.d.ts │ │ │ └── wrangler.jsonc │ │ ├── basic-react-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── users.$id.ts │ │ │ │ │ ├── api.users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── suspense-transition.tsx │ │ │ │ │ ├── transition/ │ │ │ │ │ │ └── count/ │ │ │ │ │ │ └── query.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ └── transition.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-rsc/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ └── posts_.$postId.deep.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── renderPosts.tsx │ │ │ │ └── seo.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-tsr-config/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── clerk-basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ ├── posts.tsx │ │ │ │ │ │ └── profile.$.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ └── seo.ts │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── csp/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── external.css │ │ │ │ └── external.js │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ └── csp.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── css-modules/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── shared-widget-lazy.tsx │ │ │ │ │ └── shared-widget.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── lazy-css-lazy.tsx │ │ │ │ │ ├── lazy-css-static.tsx │ │ │ │ │ ├── modules.tsx │ │ │ │ │ ├── quotes.tsx │ │ │ │ │ └── sass-mixin.tsx │ │ │ │ └── styles/ │ │ │ │ ├── app.scss │ │ │ │ ├── card.module.css │ │ │ │ ├── center-mixin.scss │ │ │ │ ├── global.css │ │ │ │ ├── mixin-consumer.scss │ │ │ │ ├── quotes.css │ │ │ │ └── shared-widget.module.css │ │ │ ├── tests/ │ │ │ │ ├── css.dev.spec.ts │ │ │ │ ├── css.prod.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ ├── vite.config.cloudflare.ts │ │ │ ├── vite.config.ts │ │ │ └── wrangler.jsonc │ │ ├── custom-basepath/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── express-server.ts │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CustomMessage.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── users.$id.ts │ │ │ │ │ ├── api.users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ ├── navigate-test.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── throw-it.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── basepath.ts │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── navigation.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── dev-ssr-styles/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── env.ts │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── flamegraph-bench/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── client.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── nested/ │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.$w.$x.$y.$z.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.$w.$x.$y.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.$w.$x.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.$w.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.$j.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.$i.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.$h.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.$g.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.$f.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.$e.tsx │ │ │ │ │ │ ├── $a.$b.$c.$d.tsx │ │ │ │ │ │ ├── $a.$b.$c.tsx │ │ │ │ │ │ ├── $a.$b.tsx │ │ │ │ │ │ └── $a.tsx │ │ │ │ │ ├── page.$id.tsx │ │ │ │ │ └── search.tsx │ │ │ │ └── server.ts │ │ │ ├── tests/ │ │ │ │ └── bench.js │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── i18n-paraglide/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ ├── extensions.json │ │ │ │ └── settings.json │ │ │ ├── messages/ │ │ │ │ ├── de.json │ │ │ │ └── en.json │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── project.inlang/ │ │ │ │ ├── .gitignore │ │ │ │ ├── project_id │ │ │ │ └── settings.json │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── prerender.ts │ │ │ │ ├── seo.ts │ │ │ │ └── translated-pathnames.ts │ │ │ ├── tests/ │ │ │ │ └── navigation.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── import-protection/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── alias-path-leak.tsx │ │ │ │ │ ├── alias-path-namespace-leak.tsx │ │ │ │ │ ├── barrel-false-positive.tsx │ │ │ │ │ ├── beforeload-leak.tsx │ │ │ │ │ ├── client-only-jsx.tsx │ │ │ │ │ ├── client-only-violations.tsx │ │ │ │ │ ├── component-server-leak.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── leaky-server-import.tsx │ │ │ │ │ ├── noexternal-client-pkg.tsx │ │ │ │ │ └── non-alias-namespace-leak.tsx │ │ │ │ └── violations/ │ │ │ │ ├── barrel-reexport/ │ │ │ │ │ ├── db.server.ts │ │ │ │ │ ├── foo.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── shared.ts │ │ │ │ ├── beforeload-server-leak.ts │ │ │ │ ├── boundary-safe.ts │ │ │ │ ├── browser-api.client.ts │ │ │ │ ├── compiler-leak.ts │ │ │ │ ├── cross-boundary-leak/ │ │ │ │ │ ├── leaky-consumer.ts │ │ │ │ │ ├── safe-consumer.ts │ │ │ │ │ └── shared-util.ts │ │ │ │ ├── cross-boundary-safe/ │ │ │ │ │ ├── auth-wrapper.ts │ │ │ │ │ ├── session-util.ts │ │ │ │ │ └── usage.ts │ │ │ │ ├── db-credentials.server.ts │ │ │ │ ├── edge-1.ts │ │ │ │ ├── edge-2.ts │ │ │ │ ├── edge-3.ts │ │ │ │ ├── edge-a.ts │ │ │ │ ├── factory-safe/ │ │ │ │ │ ├── createSecretFactory.ts │ │ │ │ │ └── usage.ts │ │ │ │ ├── leaky-server-import.ts │ │ │ │ ├── marked-client-only-edge.ts │ │ │ │ ├── marked-client-only.ts │ │ │ │ ├── marked-server-only-edge.ts │ │ │ │ ├── marked-server-only.ts │ │ │ │ ├── secret.server.ts │ │ │ │ └── window-size.client.tsx │ │ │ ├── tests/ │ │ │ │ ├── error-mode.setup.ts │ │ │ │ ├── error-mode.spec.ts │ │ │ │ ├── import-protection.spec.ts │ │ │ │ ├── utils/ │ │ │ │ │ └── isErrorMode.ts │ │ │ │ ├── violations.setup.ts │ │ │ │ └── violations.utils.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── import-protection-custom-config/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── backend-leak.tsx │ │ │ │ ├── frontend-leak.tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ ├── custom-config.spec.ts │ │ │ │ ├── error-mode.setup.ts │ │ │ │ ├── error-mode.spec.ts │ │ │ │ ├── utils/ │ │ │ │ │ └── isErrorMode.ts │ │ │ │ ├── violations.setup.ts │ │ │ │ └── violations.utils.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── query-integration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── queryOptions.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── loader-fetchQuery/ │ │ │ │ │ │ └── $type.tsx │ │ │ │ │ ├── useQuery.tsx │ │ │ │ │ └── useSuspenseQuery.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── (tests)/ │ │ │ │ │ │ ├── normal-page.tsx │ │ │ │ │ │ ├── with-loader.tsx │ │ │ │ │ │ └── with-search.tsx │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── scroll-block.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── selective-ssr/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ ├── search.ts │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── serialization-adapters/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── CustomError.ts │ │ │ │ ├── data.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── server-function/ │ │ │ │ │ │ ├── custom-error.tsx │ │ │ │ │ │ └── nested.tsx │ │ │ │ │ └── ssr/ │ │ │ │ │ ├── data-only.tsx │ │ │ │ │ ├── nested.tsx │ │ │ │ │ └── stream.tsx │ │ │ │ ├── start.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── server-functions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── functions/ │ │ │ │ │ └── fnOnlyCalledByServer.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── abort-signal/ │ │ │ │ │ │ ├── $method.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── async-validation.tsx │ │ │ │ │ ├── consistent.tsx │ │ │ │ │ ├── cookies/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── set.tsx │ │ │ │ │ ├── custom-fetch.tsx │ │ │ │ │ ├── dead-code-preserve.tsx │ │ │ │ │ ├── env-only.tsx │ │ │ │ │ ├── factory/ │ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ │ ├── createBarServerFn.ts │ │ │ │ │ │ │ ├── createFakeFn.ts │ │ │ │ │ │ │ ├── createFooServerFn.ts │ │ │ │ │ │ │ ├── functions.ts │ │ │ │ │ │ │ ├── nestedReexportA.ts │ │ │ │ │ │ │ ├── nestedReexportB.ts │ │ │ │ │ │ │ ├── nestedReexportC.ts │ │ │ │ │ │ │ ├── reexportIndex.ts │ │ │ │ │ │ │ ├── reexportWrapper.ts │ │ │ │ │ │ │ ├── starReexportIndex.ts │ │ │ │ │ │ │ └── starReexportWrapper.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── formdata-context.tsx │ │ │ │ │ ├── formdata-redirect/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.$name.tsx │ │ │ │ │ ├── function-metadata/ │ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ │ ├── normalServerFn.ts │ │ │ │ │ │ │ └── serverFnCallingServerFn.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── function-method/ │ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ │ └── serverFnCallingServerFn.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── headers.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── isomorphic-fns.tsx │ │ │ │ │ ├── method-not-allowed/ │ │ │ │ │ │ ├── $method.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── middleware/ │ │ │ │ │ │ ├── catch-handler-error.tsx │ │ │ │ │ │ ├── client-middleware-router.tsx │ │ │ │ │ │ ├── function-metadata.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── middleware-factory.tsx │ │ │ │ │ │ ├── redirect-with-middleware/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── target.tsx │ │ │ │ │ │ ├── request-middleware.tsx │ │ │ │ │ │ ├── send-serverFn.tsx │ │ │ │ │ │ ├── server-import-middleware.tsx │ │ │ │ │ │ └── unhandled-exception.tsx │ │ │ │ │ ├── multipart.tsx │ │ │ │ │ ├── primitives/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── raw-response.tsx │ │ │ │ │ ├── redirect-test/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.tsx │ │ │ │ │ ├── redirect-test-ssr/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.tsx │ │ │ │ │ ├── return-null.tsx │ │ │ │ │ ├── serialize-form-data.tsx │ │ │ │ │ ├── server-fn-in-client-only-fn.tsx │ │ │ │ │ ├── server-only-fn.tsx │ │ │ │ │ ├── status.tsx │ │ │ │ │ └── submit-post-formdata.tsx │ │ │ │ ├── start.ts │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── server-functions.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── server-functions-global-middleware/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── multiple-server-functions.tsx │ │ │ │ │ ├── pathname-middleware.tsx │ │ │ │ │ └── simple.tsx │ │ │ │ ├── start.ts │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── global-middleware.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── server-routes/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── middleware-context.ts │ │ │ │ │ │ ├── only-any.ts │ │ │ │ │ │ └── params/ │ │ │ │ │ │ └── $foo/ │ │ │ │ │ │ ├── $bar.ts │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── merge-middleware-context.tsx │ │ │ │ │ └── methods/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── only-any.tsx │ │ │ │ │ └── route.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── server-routes.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── server-routes-global-middleware/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── server-route-with-middleware.tsx │ │ │ │ ├── start.ts │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── server-routes-global-middleware.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── spa-mode/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── split-base-and-basepath/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── prod-server.js │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── static-server-functions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ └── posts.tsx │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── streaming-ssr/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── concurrent.tsx │ │ │ │ ├── deferred.tsx │ │ │ │ ├── fast-serial.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── many-promises.tsx │ │ │ │ ├── nested-deferred.tsx │ │ │ │ ├── query-heavy.tsx │ │ │ │ ├── slow-render.tsx │ │ │ │ ├── stream.tsx │ │ │ │ └── sync-only.tsx │ │ │ ├── tests/ │ │ │ │ ├── client-navigation.spec.ts │ │ │ │ ├── concurrent.spec.ts │ │ │ │ ├── deferred.spec.ts │ │ │ │ ├── fast-serial.spec.ts │ │ │ │ ├── fixtures.ts │ │ │ │ ├── home.spec.ts │ │ │ │ ├── many-promises.spec.ts │ │ │ │ ├── nested-deferred.spec.ts │ │ │ │ ├── query-heavy.spec.ts │ │ │ │ ├── slow-render.spec.ts │ │ │ │ ├── stream.spec.ts │ │ │ │ └── sync-only.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── transform-asset-urls/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.module.css │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── server.ts │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ ├── cdn-server.mjs │ │ │ │ └── transform-asset-urls.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── virtual-routes/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ └── site.webmanifest │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── pipe.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ └── special-characters.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ └── website/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── $project.$version.docs.framework.$framework.$.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.examples.$.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.index.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.{$}[.]md.tsx │ │ │ │ ├── $project.$version.docs.index.tsx │ │ │ │ ├── $project.index.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── _library.$project.$version.index.tsx │ │ │ │ ├── _library.$project.tsx │ │ │ │ ├── _library.index.tsx │ │ │ │ └── _library.tsx │ │ │ ├── server/ │ │ │ │ ├── document.tsx │ │ │ │ └── projects.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── utils/ │ │ │ └── seo.ts │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid-router/ │ │ ├── basepath-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ ├── reload-document.test.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── params.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-esbuild-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── esbuild.config.js │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── (group)/ │ │ │ │ │ ├── _layout.inside.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── lazyinside.lazy.tsx │ │ │ │ │ └── lazyinside.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── _layout/ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ └── _layout-2.tsx │ │ │ │ ├── _layout.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ └── posts.tsx │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ └── tsconfig.json │ │ ├── basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (another-group)/ │ │ │ │ │ │ └── onlyrouteinside.tsx │ │ │ │ │ ├── (group)/ │ │ │ │ │ │ ├── _layout.insidelayout.tsx │ │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ │ ├── inside.tsx │ │ │ │ │ │ ├── lazyinside.lazy.tsx │ │ │ │ │ │ ├── lazyinside.tsx │ │ │ │ │ │ └── subfolder/ │ │ │ │ │ │ └── inside.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── anchor.tsx │ │ │ │ │ ├── component-types-test.tsx │ │ │ │ │ ├── editing-a.tsx │ │ │ │ │ ├── editing-b.tsx │ │ │ │ │ ├── hover-preload-hash.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── masks.admin.$userId.tsx │ │ │ │ │ ├── masks.public.$username.tsx │ │ │ │ │ ├── masks.tsx │ │ │ │ │ ├── non-nested/ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ ├── $baz.index.tsx │ │ │ │ │ │ │ ├── $baz.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar.$foo.index.tsx │ │ │ │ │ │ │ ├── $baz_.bar.$foo.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar.$foo_.qux.tsx │ │ │ │ │ │ │ ├── $baz_.bar.index.tsx │ │ │ │ │ │ │ ├── $baz_.bar.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar_.qux.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── named/ │ │ │ │ │ │ │ ├── $baz.foo.tsx │ │ │ │ │ │ │ ├── $baz.index.tsx │ │ │ │ │ │ │ ├── $baz.route.tsx │ │ │ │ │ │ │ ├── $baz_.bar.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── path/ │ │ │ │ │ │ │ ├── baz.foo.tsx │ │ │ │ │ │ │ ├── baz.index.tsx │ │ │ │ │ │ │ ├── baz.route.tsx │ │ │ │ │ │ │ ├── baz_.bar.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── prefix/ │ │ │ │ │ │ │ ├── prefix{$baz}.foo.tsx │ │ │ │ │ │ │ ├── prefix{$baz}.index.tsx │ │ │ │ │ │ │ ├── prefix{$baz}.route.tsx │ │ │ │ │ │ │ ├── prefix{$baz}_.bar.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ └── suffix/ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── {$baz}suffix.foo.tsx │ │ │ │ │ │ ├── {$baz}suffix.index.tsx │ │ │ │ │ │ ├── {$baz}suffix.route.tsx │ │ │ │ │ │ └── {$baz}suffix_.bar.tsx │ │ │ │ │ ├── notRemountDeps.tsx │ │ │ │ │ ├── params-ps/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── named/ │ │ │ │ │ │ │ ├── $foo/ │ │ │ │ │ │ │ │ ├── $bar.$baz.tsx │ │ │ │ │ │ │ │ ├── $bar.route.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── prefix{$foo}.tsx │ │ │ │ │ │ │ └── {$foo}suffix.tsx │ │ │ │ │ │ ├── non-nested/ │ │ │ │ │ │ │ ├── $foo_/ │ │ │ │ │ │ │ │ ├── $bar.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ └── wildcard/ │ │ │ │ │ │ ├── $.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── prefix@대{$}.tsx │ │ │ │ │ │ ├── prefix{$}.tsx │ │ │ │ │ │ ├── {$}suffix.tsx │ │ │ │ │ │ └── {$}suffix@대.tsx │ │ │ │ │ ├── params.single.$value.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.edit.tsx │ │ │ │ │ ├── redirect/ │ │ │ │ │ │ ├── $target/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ └── via-loader.tsx │ │ │ │ │ │ ├── $target.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── preload/ │ │ │ │ │ │ ├── first.tsx │ │ │ │ │ │ ├── second.tsx │ │ │ │ │ │ └── third.tsx │ │ │ │ │ ├── relative/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── link/ │ │ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── path/ │ │ │ │ │ │ │ │ ├── $path/ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── relative-link-a.tsx │ │ │ │ │ │ │ ├── relative-link-b.tsx │ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ │ └── with-search/ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── useNavigate/ │ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ │ ├── deep/ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── path/ │ │ │ │ │ │ │ ├── $path/ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── relative-useNavigate-a.tsx │ │ │ │ │ │ ├── relative-useNavigate-b.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ └── with-search/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── remountDeps.tsx │ │ │ │ │ ├── search-params/ │ │ │ │ │ │ ├── default.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── transition/ │ │ │ │ │ │ ├── count/ │ │ │ │ │ │ │ └── create-resource.tsx │ │ │ │ │ │ └── typing/ │ │ │ │ │ │ └── create-resource.tsx │ │ │ │ │ └── 대한민국/ │ │ │ │ │ ├── route.tsx │ │ │ │ │ ├── wildcard.$.tsx │ │ │ │ │ └── 🚀.$id.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── hover-preload-hash.spec.ts │ │ │ │ ├── mask.spec.ts │ │ │ │ ├── non-nested-paths.spec.ts │ │ │ │ ├── params.spec.ts │ │ │ │ ├── redirect.spec.ts │ │ │ │ ├── relative.spec.ts │ │ │ │ ├── scroll-into-view.spec.ts │ │ │ │ ├── search-params.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ └── transition.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-file-based-code-splitting/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── viewport-test.tsx │ │ │ │ │ └── without-loader.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── preload.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── router-events.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-solid-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-solid-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── postsQueryOptions.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-virtual-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-virtual-named-export-config-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── generator-cli-only/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ ├── tsr.config.json │ │ │ └── vite.config.js │ │ ├── js-only-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.jsx │ │ │ │ ├── posts.js │ │ │ │ ├── routeTree.gen.js │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.jsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.jsx │ │ │ │ │ │ │ └── route-b.jsx │ │ │ │ │ │ └── _nested-layout.jsx │ │ │ │ │ ├── _pathlessLayout.jsx │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── posts.$postId.jsx │ │ │ │ │ ├── posts.index.jsx │ │ │ │ │ └── posts.route.jsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── rspack-basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── rspack-basic-virtual-named-export-config-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── routes.ts │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── scroll-restoration-sandbox-vite/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.browser.config.ts │ │ │ ├── playwright.hash.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (tests)/ │ │ │ │ │ │ ├── lazy-page.lazy.tsx │ │ │ │ │ │ ├── lazy-page.tsx │ │ │ │ │ │ ├── lazy-with-loader-page.lazy.tsx │ │ │ │ │ │ ├── lazy-with-loader-page.tsx │ │ │ │ │ │ ├── normal-page.tsx │ │ │ │ │ │ ├── page-with-search.tsx │ │ │ │ │ │ └── virtual-page.lazy.tsx │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── scroll-block.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── sentry-integration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ └── view-transitions/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── main.tsx │ │ │ ├── posts.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── explore.tsx │ │ │ │ ├── how-it-works.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ └── posts.route.tsx │ │ │ └── styles.css │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ └── setup/ │ │ │ ├── global.setup.ts │ │ │ └── global.teardown.ts │ │ ├── tsconfig.json │ │ └── vite.config.js │ ├── solid-start/ │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ └── site.webmanifest │ │ │ ├── server.js │ │ │ ├── src/ │ │ │ │ ├── client.tsx │ │ │ │ ├── components/ │ │ │ │ │ ├── CustomMessage.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostErrorComponent.tsx │ │ │ │ │ ├── RedirectOnClick.tsx │ │ │ │ │ ├── UserErrorComponent.tsx │ │ │ │ │ └── throwRedirect.ts │ │ │ │ ├── raw-stream-fns.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── inline-scripts.tsx │ │ │ │ │ ├── links.tsx │ │ │ │ │ ├── multi-cookie-redirect/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.tsx │ │ │ │ │ ├── not-found/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── parent-boundary/ │ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ │ └── via-beforeLoad.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── via-beforeLoad-target-root.tsx │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ └── via-loader.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── raw-stream/ │ │ │ │ │ │ ├── client-call.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── ssr-binary-hint.tsx │ │ │ │ │ │ ├── ssr-mixed.tsx │ │ │ │ │ │ ├── ssr-multiple.tsx │ │ │ │ │ │ ├── ssr-single.tsx │ │ │ │ │ │ └── ssr-text-hint.tsx │ │ │ │ │ ├── raw-stream.tsx │ │ │ │ │ ├── redirect/ │ │ │ │ │ │ ├── $target/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── serverFn/ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ │ ├── via-loader.tsx │ │ │ │ │ │ │ │ └── via-useServerFn.tsx │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ └── via-loader.tsx │ │ │ │ │ │ ├── $target.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── scripts.tsx │ │ │ │ │ ├── search-params/ │ │ │ │ │ │ ├── default.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── loader-throws-redirect.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── specialChars/ │ │ │ │ │ │ ├── $param.tsx │ │ │ │ │ │ ├── hash.tsx │ │ │ │ │ │ ├── malformed/ │ │ │ │ │ │ │ ├── $param.tsx │ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ │ └── search.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── search.tsx │ │ │ │ │ │ └── 대한민국.tsx │ │ │ │ │ ├── stream.tsx │ │ │ │ │ ├── transition/ │ │ │ │ │ │ ├── count/ │ │ │ │ │ │ │ └── create-resource.tsx │ │ │ │ │ │ └── typing/ │ │ │ │ │ │ └── create-resource.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ ├── utils/ │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── seo.ts │ │ │ │ │ └── users.tsx │ │ │ │ └── vite-env.d.ts │ │ │ ├── tests/ │ │ │ │ ├── navigation.spec.ts │ │ │ │ ├── not-found.spec.ts │ │ │ │ ├── prerendering.spec.ts │ │ │ │ ├── redirect.spec.ts │ │ │ │ ├── script-duplication.spec.ts │ │ │ │ ├── search-params.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ ├── special-characters.spec.ts │ │ │ │ ├── streaming.spec.ts │ │ │ │ ├── transition.spec.ts │ │ │ │ └── utils/ │ │ │ │ ├── isPrerender.ts │ │ │ │ ├── isPreview.ts │ │ │ │ └── isSpaMode.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-auth/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── prisma/ │ │ │ │ ├── migrations/ │ │ │ │ │ ├── 20240811183753_init/ │ │ │ │ │ │ └── migration.sql │ │ │ │ │ └── migration_lock.toml │ │ │ │ └── schema.prisma │ │ │ ├── prisma.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Auth.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useMutation.ts │ │ │ │ ├── prisma-generated/ │ │ │ │ │ ├── browser.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── commonInputTypes.ts │ │ │ │ │ ├── enums.ts │ │ │ │ │ ├── internal/ │ │ │ │ │ │ ├── class.ts │ │ │ │ │ │ ├── prismaNamespace.ts │ │ │ │ │ │ └── prismaNamespaceBrowser.ts │ │ │ │ │ ├── models/ │ │ │ │ │ │ └── User.ts │ │ │ │ │ └── models.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ └── signup.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ ├── prisma.ts │ │ │ │ ├── seo.ts │ │ │ │ └── session.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── mock-db-setup.test.ts │ │ │ │ └── mock-db-teardown.test.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-cloudflare/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── static.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ └── seo.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ ├── vite.config.ts │ │ │ ├── worker-configuration.d.ts │ │ │ └── wrangler.jsonc │ │ ├── basic-solid-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── users.$id.ts │ │ │ │ │ ├── api.users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── suspense-transition.tsx │ │ │ │ │ ├── transition/ │ │ │ │ │ │ └── count/ │ │ │ │ │ │ └── query.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ └── transition.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-tsr-config/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ └── app/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── vite-env.d.ts │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── csp/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── external.css │ │ │ │ └── external.js │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ └── csp.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── css-modules/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── modules.tsx │ │ │ │ │ └── sass-mixin.tsx │ │ │ │ └── styles/ │ │ │ │ ├── app.scss │ │ │ │ ├── card.module.css │ │ │ │ ├── center-mixin.scss │ │ │ │ ├── global.css │ │ │ │ └── mixin-consumer.scss │ │ │ ├── tests/ │ │ │ │ ├── css.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── custom-basepath/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── express-server.ts │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CustomMessage.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostErrorComponent.tsx │ │ │ │ │ └── UserErrorComponent.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ ├── navigate-test.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.index.tsx │ │ │ │ │ ├── redirect.throw-it.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ ├── utils/ │ │ │ │ │ ├── basepath.ts │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── seo.ts │ │ │ │ │ └── users.tsx │ │ │ │ └── vite-env.d.ts │ │ │ ├── tests/ │ │ │ │ ├── navigation.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── query-integration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── queryOptions.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── loader-fetchQuery/ │ │ │ │ │ │ └── $type.tsx │ │ │ │ │ └── useQuery.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── (tests)/ │ │ │ │ │ │ ├── normal-page.tsx │ │ │ │ │ │ ├── with-loader.tsx │ │ │ │ │ │ └── with-search.tsx │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── scroll-block.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ ├── utils/ │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── seo.ts │ │ │ │ │ └── users.tsx │ │ │ │ └── vite-env.d.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── selective-ssr/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ ├── search.ts │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── serialization-adapters/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── CustomError.ts │ │ │ │ ├── data.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── server-function/ │ │ │ │ │ │ ├── custom-error.tsx │ │ │ │ │ │ └── nested.tsx │ │ │ │ │ └── ssr/ │ │ │ │ │ ├── data-only.tsx │ │ │ │ │ ├── nested.tsx │ │ │ │ │ └── stream.tsx │ │ │ │ ├── start.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── server-functions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── abort-signal/ │ │ │ │ │ │ ├── $method.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── consistent.tsx │ │ │ │ │ ├── cookies/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── set.tsx │ │ │ │ │ ├── dead-code-preserve.tsx │ │ │ │ │ ├── env-only.tsx │ │ │ │ │ ├── factory/ │ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ │ ├── createBarServerFn.ts │ │ │ │ │ │ │ ├── createFakeFn.ts │ │ │ │ │ │ │ ├── createFooServerFn.ts │ │ │ │ │ │ │ └── functions.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── formdata-redirect/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.$name.tsx │ │ │ │ │ ├── function-metadata/ │ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ │ ├── normalServerFn.ts │ │ │ │ │ │ │ └── serverFnCallingServerFn.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── function-method/ │ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ │ └── serverFnCallingServerFn.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── headers.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── isomorphic-fns.tsx │ │ │ │ │ ├── middleware/ │ │ │ │ │ │ ├── client-middleware-router.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── request-middleware.tsx │ │ │ │ │ │ └── send-serverFn.tsx │ │ │ │ │ ├── multipart.tsx │ │ │ │ │ ├── primitives/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── raw-response.tsx │ │ │ │ │ ├── redirect-test/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.tsx │ │ │ │ │ ├── redirect-test-ssr/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── target.tsx │ │ │ │ │ ├── return-null.tsx │ │ │ │ │ ├── serialize-form-data.tsx │ │ │ │ │ ├── status.tsx │ │ │ │ │ └── submit-post-formdata.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── vite-env.d.ts │ │ │ ├── tests/ │ │ │ │ └── server-functions.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── server-routes/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── middleware-context.ts │ │ │ │ │ │ ├── only-any.ts │ │ │ │ │ │ └── params/ │ │ │ │ │ │ └── $foo/ │ │ │ │ │ │ ├── $bar.ts │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── merge-middleware-context.tsx │ │ │ │ │ └── methods/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── only-any.tsx │ │ │ │ │ └── route.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── vite-env.d.ts │ │ │ ├── tests/ │ │ │ │ └── server-routes.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── spa-mode/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── virtual-routes/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── public/ │ │ │ │ ├── script.js │ │ │ │ ├── script2.js │ │ │ │ └── site.webmanifest │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── pipe.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── setup/ │ │ │ │ │ ├── global.setup.ts │ │ │ │ │ └── global.teardown.ts │ │ │ │ └── special-characters.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ └── website/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── $project.$version.docs.framework.$framework.$.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.examples.$.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.index.tsx │ │ │ │ ├── $project.$version.docs.framework.$framework.tsx │ │ │ │ ├── $project.$version.docs.index.tsx │ │ │ │ ├── $project.index.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── _library.$project.$version.index.tsx │ │ │ │ ├── _library.$project.tsx │ │ │ │ ├── _library.index.tsx │ │ │ │ └── _library.tsx │ │ │ ├── server/ │ │ │ │ ├── document.tsx │ │ │ │ └── projects.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ ├── utils/ │ │ │ │ └── seo.ts │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vue-router/ │ │ ├── basepath-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ └── index.tsx │ │ │ ├── tests/ │ │ │ │ ├── reload-document.test.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ ├── params.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-esbuild-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── eslint.config.js │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── EditingAComponent.tsx │ │ │ │ │ ├── EditingBComponent.tsx │ │ │ │ │ ├── NotFoundComponent.vue │ │ │ │ │ ├── NotRemountDepsComponent.tsx │ │ │ │ │ ├── PostErrorComponent.vue │ │ │ │ │ ├── RemountDepsComponent.tsx │ │ │ │ │ └── VueLogo.vue │ │ │ │ ├── esbuild.config.js │ │ │ │ ├── esbuild.run.js │ │ │ │ ├── jsx-shim.ts │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (another-group)/ │ │ │ │ │ │ └── onlyrouteinside.tsx │ │ │ │ │ ├── (group)/ │ │ │ │ │ │ ├── _layout.insidelayout.tsx │ │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ │ ├── inside.tsx │ │ │ │ │ │ ├── lazyinside.tsx │ │ │ │ │ │ └── subfolder/ │ │ │ │ │ │ └── inside.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── editing-a.tsx │ │ │ │ │ ├── editing-b.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── notRemountDeps.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.edit.tsx │ │ │ │ │ ├── remountDeps.tsx │ │ │ │ │ ├── sfcComponent.component.vue │ │ │ │ │ ├── sfcComponent.tsx │ │ │ │ │ └── 대한민국.tsx │ │ │ │ ├── styles.css │ │ │ │ └── vue-shims.d.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── basic-file-based-jsx/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── eslint.config.js │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── EditingAComponent.tsx │ │ │ │ │ ├── EditingBComponent.tsx │ │ │ │ │ ├── NotFoundComponent.vue │ │ │ │ │ ├── NotRemountDepsComponent.tsx │ │ │ │ │ ├── PostErrorComponent.vue │ │ │ │ │ ├── RemountDepsComponent.tsx │ │ │ │ │ └── VueLogo.vue │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (another-group)/ │ │ │ │ │ │ └── onlyrouteinside.tsx │ │ │ │ │ ├── (group)/ │ │ │ │ │ │ ├── _layout.insidelayout.tsx │ │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ │ ├── inside.tsx │ │ │ │ │ │ ├── lazyinside.tsx │ │ │ │ │ │ └── subfolder/ │ │ │ │ │ │ └── inside.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── editing-a.tsx │ │ │ │ │ ├── editing-b.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── notRemountDeps.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.edit.tsx │ │ │ │ │ ├── remountDeps.tsx │ │ │ │ │ ├── sfcComponent.component.vue │ │ │ │ │ ├── sfcComponent.tsx │ │ │ │ │ └── 대한민국.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-file-based-sfc/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── eslint.config.js │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── EditingAComponent.tsx │ │ │ │ │ ├── EditingBComponent.tsx │ │ │ │ │ ├── NotFoundComponent.vue │ │ │ │ │ ├── NotRemountDepsComponent.tsx │ │ │ │ │ ├── PostErrorComponent.vue │ │ │ │ │ ├── RemountDepsComponent.tsx │ │ │ │ │ └── VueLogo.vue │ │ │ │ ├── main.ts │ │ │ │ ├── posts.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (another-group)/ │ │ │ │ │ │ ├── onlyrouteinside.component.vue │ │ │ │ │ │ └── onlyrouteinside.ts │ │ │ │ │ ├── (group)/ │ │ │ │ │ │ ├── _layout.component.vue │ │ │ │ │ │ ├── _layout.insidelayout.component.vue │ │ │ │ │ │ ├── _layout.insidelayout.ts │ │ │ │ │ │ ├── _layout.ts │ │ │ │ │ │ ├── inside.component.vue │ │ │ │ │ │ ├── inside.ts │ │ │ │ │ │ ├── lazyinside.component.vue │ │ │ │ │ │ ├── lazyinside.ts │ │ │ │ │ │ └── subfolder/ │ │ │ │ │ │ ├── inside.component.vue │ │ │ │ │ │ └── inside.ts │ │ │ │ │ ├── __root.component.vue │ │ │ │ │ ├── __root.notFoundComponent.vue │ │ │ │ │ ├── __root.ts │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.component.vue │ │ │ │ │ │ │ ├── layout-a.ts │ │ │ │ │ │ │ ├── layout-b.component.vue │ │ │ │ │ │ │ └── layout-b.ts │ │ │ │ │ │ ├── _layout-2.component.vue │ │ │ │ │ │ └── _layout-2.ts │ │ │ │ │ ├── _layout.component.vue │ │ │ │ │ ├── _layout.ts │ │ │ │ │ ├── editing-a.component.vue │ │ │ │ │ ├── editing-a.ts │ │ │ │ │ ├── editing-b.component.vue │ │ │ │ │ ├── editing-b.ts │ │ │ │ │ ├── index.component.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── notRemountDeps.component.vue │ │ │ │ │ ├── notRemountDeps.ts │ │ │ │ │ ├── posts.$postId.component.vue │ │ │ │ │ ├── posts.$postId.errorComponent.vue │ │ │ │ │ ├── posts.$postId.ts │ │ │ │ │ ├── posts.component.vue │ │ │ │ │ ├── posts.index.component.vue │ │ │ │ │ ├── posts.index.ts │ │ │ │ │ ├── posts.ts │ │ │ │ │ ├── posts_.$postId.edit.component.vue │ │ │ │ │ ├── posts_.$postId.edit.ts │ │ │ │ │ ├── remountDeps.component.vue │ │ │ │ │ ├── remountDeps.ts │ │ │ │ │ ├── 대한민국.component.vue │ │ │ │ │ └── 대한민국.ts │ │ │ │ ├── styles.css │ │ │ │ └── vue-shims.d.ts │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── router-events.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-virtual-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-virtual-named-export-config-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-vue-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-vue-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── postsQueryOptions.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── generator-cli-only/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ ├── tsr.config.json │ │ │ └── vite.config.js │ │ ├── js-only-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.jsx │ │ │ │ ├── posts.js │ │ │ │ ├── routeTree.gen.js │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.jsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.jsx │ │ │ │ │ │ │ └── route-b.jsx │ │ │ │ │ │ └── _nested-layout.jsx │ │ │ │ │ ├── _pathlessLayout.jsx │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── posts.$postId.jsx │ │ │ │ │ ├── posts.index.jsx │ │ │ │ │ └── posts.route.jsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── rspack-basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── rspack-basic-virtual-named-export-config-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── postcss.config.mjs │ │ │ ├── routes.ts │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── tsconfig.json │ │ ├── scroll-restoration-sandbox-vite/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.browser.config.ts │ │ │ ├── playwright.hash.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (tests)/ │ │ │ │ │ │ ├── lazy-page.lazy.tsx │ │ │ │ │ │ ├── lazy-page.tsx │ │ │ │ │ │ ├── lazy-with-loader-page.lazy.tsx │ │ │ │ │ │ ├── lazy-with-loader-page.tsx │ │ │ │ │ │ ├── normal-page.tsx │ │ │ │ │ │ ├── page-with-search.tsx │ │ │ │ │ │ └── virtual-page.lazy.tsx │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── scroll-block.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── app.spec.ts │ │ │ │ └── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── sentry-integration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── playwright.config.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ └── app.spec.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ └── view-transitions/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── main.tsx │ │ │ ├── posts.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── explore.tsx │ │ │ │ ├── how-it-works.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ └── posts.route.tsx │ │ │ └── styles.css │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ ├── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── view-transitions.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.js │ └── vue-start/ │ ├── basic/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ ├── script.js │ │ │ ├── script2.js │ │ │ └── site.webmanifest │ │ ├── server.js │ │ ├── src/ │ │ │ ├── client.tsx │ │ │ ├── components/ │ │ │ │ ├── CustomMessage.tsx │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ ├── NotFound.tsx │ │ │ │ ├── PostErrorComponent.tsx │ │ │ │ ├── RedirectOnClick.tsx │ │ │ │ ├── UserErrorComponent.tsx │ │ │ │ └── throwRedirect.ts │ │ │ ├── raw-stream-fns.ts │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── _layout/ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ └── _layout-2.tsx │ │ │ │ ├── _layout.tsx │ │ │ │ ├── api/ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ └── users.ts │ │ │ │ ├── deferred.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── inline-scripts.tsx │ │ │ │ ├── links.tsx │ │ │ │ ├── multi-cookie-redirect/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── target.tsx │ │ │ │ ├── not-found/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── parent-boundary/ │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ └── via-beforeLoad.tsx │ │ │ │ │ ├── route.tsx │ │ │ │ │ ├── via-beforeLoad-target-root.tsx │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ └── via-loader.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ ├── raw-stream/ │ │ │ │ │ ├── client-call.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── ssr-binary-hint.tsx │ │ │ │ │ ├── ssr-mixed.tsx │ │ │ │ │ ├── ssr-multiple.tsx │ │ │ │ │ ├── ssr-single.tsx │ │ │ │ │ └── ssr-text-hint.tsx │ │ │ │ ├── raw-stream.tsx │ │ │ │ ├── redirect/ │ │ │ │ │ ├── $target/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── serverFn/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ │ ├── via-loader.tsx │ │ │ │ │ │ │ └── via-useServerFn.tsx │ │ │ │ │ │ ├── via-beforeLoad.tsx │ │ │ │ │ │ └── via-loader.tsx │ │ │ │ │ ├── $target.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── scripts.tsx │ │ │ │ ├── search-params/ │ │ │ │ │ ├── default.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── loader-throws-redirect.tsx │ │ │ │ │ └── route.tsx │ │ │ │ ├── specialChars/ │ │ │ │ │ ├── $param.tsx │ │ │ │ │ ├── hash.tsx │ │ │ │ │ ├── malformed/ │ │ │ │ │ │ ├── $param.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ └── search.tsx │ │ │ │ │ ├── route.tsx │ │ │ │ │ ├── search.tsx │ │ │ │ │ └── 대한민국.tsx │ │ │ │ ├── stream.tsx │ │ │ │ ├── users.$userId.tsx │ │ │ │ ├── users.index.tsx │ │ │ │ └── users.tsx │ │ │ ├── server.ts │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ ├── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ ├── navigation.spec.ts │ │ │ ├── not-found.spec.ts │ │ │ ├── prerendering.spec.ts │ │ │ ├── redirect.spec.ts │ │ │ ├── script-duplication.spec.ts │ │ │ ├── search-params.spec.ts │ │ │ ├── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ ├── special-characters.spec.ts │ │ │ ├── streaming.spec.ts │ │ │ └── utils/ │ │ │ ├── isPrerender.ts │ │ │ ├── isPreview.ts │ │ │ └── isSpaMode.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── basic-auth/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .env │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── prisma/ │ │ │ ├── migrations/ │ │ │ │ ├── 20240811183753_init/ │ │ │ │ │ └── migration.sql │ │ │ │ └── migration_lock.toml │ │ │ └── schema.prisma │ │ ├── prisma.config.ts │ │ ├── public/ │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Auth.tsx │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ ├── Login.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── hooks/ │ │ │ │ └── useMutation.ts │ │ │ ├── prisma-generated/ │ │ │ │ ├── browser.ts │ │ │ │ ├── client.ts │ │ │ │ ├── commonInputTypes.ts │ │ │ │ ├── enums.ts │ │ │ │ ├── internal/ │ │ │ │ │ ├── class.ts │ │ │ │ │ ├── prismaNamespace.ts │ │ │ │ │ └── prismaNamespaceBrowser.ts │ │ │ │ ├── models/ │ │ │ │ │ └── User.ts │ │ │ │ └── models.ts │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── _authed/ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ ├── _authed.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── login.tsx │ │ │ │ ├── logout.tsx │ │ │ │ └── signup.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── utils/ │ │ │ ├── posts.ts │ │ │ ├── prisma.ts │ │ │ ├── seo.ts │ │ │ └── session.ts │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ ├── mock-db-setup.test.ts │ │ │ └── mock-db-teardown.test.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── basic-cloudflare/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── static.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── utils/ │ │ │ └── seo.ts │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ └── setup/ │ │ │ ├── global.setup.ts │ │ │ └── global.teardown.ts │ │ ├── tsconfig.json │ │ ├── vite.config.ts │ │ ├── worker-configuration.d.ts │ │ └── wrangler.jsonc │ ├── basic-tsr-config/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── README.md │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ └── app/ │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── basic-vue-query/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── _layout/ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ └── _layout-2.tsx │ │ │ │ ├── _layout.tsx │ │ │ │ ├── api/ │ │ │ │ │ └── users.$id.ts │ │ │ │ ├── api.users.ts │ │ │ │ ├── deferred.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ ├── suspense-transition.tsx │ │ │ │ ├── transition/ │ │ │ │ │ └── count/ │ │ │ │ │ └── query.tsx │ │ │ │ ├── users.$userId.tsx │ │ │ │ ├── users.index.tsx │ │ │ │ └── users.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── utils/ │ │ │ ├── posts.tsx │ │ │ ├── seo.ts │ │ │ └── users.tsx │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ ├── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── transition.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── css-modules/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── modules.tsx │ │ │ │ └── sass-mixin.tsx │ │ │ └── styles/ │ │ │ ├── app.scss │ │ │ ├── card.module.css │ │ │ ├── center-mixin.scss │ │ │ ├── global.css │ │ │ └── mixin-consumer.scss │ │ ├── tests/ │ │ │ ├── css.spec.ts │ │ │ └── setup/ │ │ │ ├── global.setup.ts │ │ │ └── global.teardown.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── custom-basepath/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── express-server.ts │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ ├── script.js │ │ │ ├── script2.js │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── CustomMessage.tsx │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ ├── NotFound.tsx │ │ │ │ ├── PostErrorComponent.tsx │ │ │ │ └── UserErrorComponent.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── api/ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ └── users.ts │ │ │ │ ├── deferred.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── logout.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ ├── redirect.index.tsx │ │ │ │ ├── redirect.throw-it.tsx │ │ │ │ ├── users.$userId.tsx │ │ │ │ ├── users.index.tsx │ │ │ │ └── users.tsx │ │ │ ├── server.ts │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ ├── utils/ │ │ │ │ ├── basepath.ts │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ ├── navigation.spec.ts │ │ │ └── setup/ │ │ │ ├── global.setup.ts │ │ │ └── global.teardown.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── query-integration/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── queryOptions.ts │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── loader-fetchQuery/ │ │ │ │ │ └── $type.tsx │ │ │ │ └── useQuery.tsx │ │ │ └── styles/ │ │ │ └── app.css │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── scroll-restoration/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ ├── script.js │ │ │ ├── script2.js │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── (tests)/ │ │ │ │ │ ├── normal-page.tsx │ │ │ │ │ ├── with-loader.tsx │ │ │ │ │ └── with-search.tsx │ │ │ │ ├── -components/ │ │ │ │ │ └── scroll-block.tsx │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ ├── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ └── setup/ │ │ │ ├── global.setup.ts │ │ │ └── global.teardown.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── selective-ssr/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ └── posts.tsx │ │ │ ├── search.ts │ │ │ └── styles/ │ │ │ └── app.css │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── serialization-adapters/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── CustomError.ts │ │ │ ├── data.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── server-function/ │ │ │ │ │ ├── custom-error.tsx │ │ │ │ │ └── nested.tsx │ │ │ │ └── ssr/ │ │ │ │ ├── data-only.tsx │ │ │ │ ├── nested.tsx │ │ │ │ └── stream.tsx │ │ │ ├── start.tsx │ │ │ └── styles/ │ │ │ └── app.css │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── server-functions/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── abort-signal.tsx │ │ │ │ ├── consistent.tsx │ │ │ │ ├── cookies/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── set.tsx │ │ │ │ ├── dead-code-preserve.tsx │ │ │ │ ├── env-only.tsx │ │ │ │ ├── factory/ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ ├── createBarServerFn.ts │ │ │ │ │ │ ├── createFakeFn.ts │ │ │ │ │ │ ├── createFooServerFn.ts │ │ │ │ │ │ └── functions.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── formdata-redirect/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── target.$name.tsx │ │ │ │ ├── function-metadata/ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ ├── normalServerFn.ts │ │ │ │ │ │ └── serverFnCallingServerFn.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── function-method/ │ │ │ │ │ ├── -functions/ │ │ │ │ │ │ └── serverFnCallingServerFn.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── headers.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── isomorphic-fns.tsx │ │ │ │ ├── middleware/ │ │ │ │ │ ├── client-middleware-router.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── request-middleware.tsx │ │ │ │ │ └── send-serverFn.tsx │ │ │ │ ├── multipart.tsx │ │ │ │ ├── primitives/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── raw-response.tsx │ │ │ │ ├── redirect-test/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── target.tsx │ │ │ │ ├── redirect-test-ssr/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── target.tsx │ │ │ │ ├── return-null.tsx │ │ │ │ ├── serialize-form-data.tsx │ │ │ │ ├── status.tsx │ │ │ │ └── submit-post-formdata.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ └── server-functions.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── server-routes/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ └── NotFound.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── api/ │ │ │ │ │ ├── middleware-context.ts │ │ │ │ │ ├── only-any.ts │ │ │ │ │ └── params/ │ │ │ │ │ └── $foo/ │ │ │ │ │ ├── $bar.ts │ │ │ │ │ └── route.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── merge-middleware-context.tsx │ │ │ │ └── methods/ │ │ │ │ ├── index.tsx │ │ │ │ ├── only-any.tsx │ │ │ │ └── route.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ └── server-routes.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── spa-mode/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── src/ │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ └── posts.tsx │ │ │ └── styles/ │ │ │ └── app.css │ │ ├── tests/ │ │ │ └── app.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── virtual-routes/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── public/ │ │ │ ├── script.js │ │ │ ├── script2.js │ │ │ └── site.webmanifest │ │ ├── routes.ts │ │ ├── src/ │ │ │ ├── posts.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── a.tsx │ │ │ │ ├── b.tsx │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ └── hello/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── route.tsx │ │ │ │ │ ├── universe.tsx │ │ │ │ │ └── world.tsx │ │ │ │ ├── home.tsx │ │ │ │ ├── layout/ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ └── second-layout.tsx │ │ │ │ ├── pipe.tsx │ │ │ │ ├── posts/ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── root.tsx │ │ │ ├── styles/ │ │ │ │ └── app.css │ │ │ └── utils/ │ │ │ ├── posts.tsx │ │ │ ├── seo.ts │ │ │ └── users.tsx │ │ ├── tests/ │ │ │ ├── app.spec.ts │ │ │ ├── setup/ │ │ │ │ ├── global.setup.ts │ │ │ │ └── global.teardown.ts │ │ │ └── special-characters.spec.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── website/ │ ├── .devcontainer/ │ │ └── devcontainer.json │ ├── .gitignore │ ├── .prettierignore │ ├── package.json │ ├── playwright.config.ts │ ├── public/ │ │ └── site.webmanifest │ ├── src/ │ │ ├── components/ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ └── NotFound.tsx │ │ ├── routeTree.gen.ts │ │ ├── router.tsx │ │ ├── routes/ │ │ │ ├── $project.$version.docs.framework.$framework.$.tsx │ │ │ ├── $project.$version.docs.framework.$framework.examples.$.tsx │ │ │ ├── $project.$version.docs.framework.$framework.index.tsx │ │ │ ├── $project.$version.docs.framework.$framework.tsx │ │ │ ├── $project.$version.docs.index.tsx │ │ │ ├── $project.index.tsx │ │ │ ├── __root.tsx │ │ │ ├── _library.$project.$version.index.tsx │ │ │ ├── _library.$project.tsx │ │ │ ├── _library.index.tsx │ │ │ └── _library.tsx │ │ ├── server/ │ │ │ ├── document.tsx │ │ │ └── projects.tsx │ │ ├── styles/ │ │ │ └── app.css │ │ ├── utils/ │ │ │ └── seo.ts │ │ └── vite-env.d.ts │ ├── tests/ │ │ └── app.spec.ts │ ├── tsconfig.json │ └── vite.config.ts ├── eslint.config.js ├── examples/ │ ├── react/ │ │ ├── authenticated-routes/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.dashboard.tsx │ │ │ │ │ ├── _auth.invoices.$invoiceId.tsx │ │ │ │ │ ├── _auth.invoices.index.tsx │ │ │ │ │ ├── _auth.invoices.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ └── utils.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── authenticated-routes-firebase/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── firebase/ │ │ │ │ │ └── config.ts │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.dashboard.tsx │ │ │ │ │ ├── _auth.invoices.$invoiceId.tsx │ │ │ │ │ ├── _auth.invoices.index.tsx │ │ │ │ │ ├── _auth.invoices.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ ├── utils.ts │ │ │ │ └── vite.d.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-default-search-params/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-devtools-panel/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── App.tsx │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── anchor.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-non-nested-devtools/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-react-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-react-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── postsQueryOptions.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-ssr-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── server.js │ │ │ ├── src/ │ │ │ │ ├── entry-client.tsx │ │ │ │ ├── entry-server.tsx │ │ │ │ ├── fetch-polyfill.js │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routerContext.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── error.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── posts/ │ │ │ │ ├── $postId.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── route.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-ssr-streaming-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── server.js │ │ │ ├── src/ │ │ │ │ ├── entry-client.tsx │ │ │ │ ├── entry-server.tsx │ │ │ │ ├── fetch-polyfill.js │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routerContext.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── error.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── posts/ │ │ │ │ ├── $postId.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── route.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-virtual-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-virtual-inside-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── __virtual.ts │ │ │ │ │ │ ├── details.tsx │ │ │ │ │ │ ├── home.tsx │ │ │ │ │ │ └── lets-go/ │ │ │ │ │ │ ├── deeper/ │ │ │ │ │ │ │ ├── __virtual.ts │ │ │ │ │ │ │ └── home.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── deferred-data/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── i18n-paraglide/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── messages/ │ │ │ │ ├── de.json │ │ │ │ └── en.json │ │ │ ├── package.json │ │ │ ├── project.inlang/ │ │ │ │ ├── .gitignore │ │ │ │ ├── project_id │ │ │ │ └── settings.json │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── kitchen-sink/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── Expensive.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ ├── styles.css │ │ │ │ ├── useMutation.tsx │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── kitchen-sink-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Breadcrumbs.tsx │ │ │ │ │ ├── InvoiceFields.tsx │ │ │ │ │ └── Spinner.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useMutation.tsx │ │ │ │ │ └── useSessionStorage.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (this-folder-is-not-in-the-url)/ │ │ │ │ │ │ └── route-group.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.profile.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── _pathlessLayout.route-a.tsx │ │ │ │ │ ├── _pathlessLayout.route-b.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── dashboard.index.tsx │ │ │ │ │ ├── dashboard.invoices.$invoiceId.tsx │ │ │ │ │ ├── dashboard.invoices.index.tsx │ │ │ │ │ ├── dashboard.invoices.route.tsx │ │ │ │ │ ├── dashboard.route.tsx │ │ │ │ │ ├── dashboard.users.index.tsx │ │ │ │ │ ├── dashboard.users.route.tsx │ │ │ │ │ ├── dashboard.users.user.tsx │ │ │ │ │ ├── expensive/ │ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ │ └── Expensive.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── kitchen-sink-react-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── Expensive.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ ├── styles.css │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── kitchen-sink-react-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── InvoiceFields.tsx │ │ │ │ │ └── Spinner.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useSessionStorage.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.profile.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── _pathlessLayout.route-a.tsx │ │ │ │ │ ├── _pathlessLayout.route-b.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── dashboard.index.tsx │ │ │ │ │ ├── dashboard.invoices.$invoiceId.tsx │ │ │ │ │ ├── dashboard.invoices.index.tsx │ │ │ │ │ ├── dashboard.invoices.route.tsx │ │ │ │ │ ├── dashboard.route.tsx │ │ │ │ │ ├── dashboard.users.index.tsx │ │ │ │ │ ├── dashboard.users.route.tsx │ │ │ │ │ ├── dashboard.users.user.tsx │ │ │ │ │ ├── expensive/ │ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ │ └── Expensive.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── foo/ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ ├── queryOptions.ts │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── large-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── createRoutes.mjs │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── absolute.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── linkProps.tsx │ │ │ │ │ ├── params/ │ │ │ │ │ │ ├── $paramsPlaceholder.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── relative.tsx │ │ │ │ │ └── search/ │ │ │ │ │ ├── route.tsx │ │ │ │ │ └── searchPlaceholder.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── location-masking/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── navigation-blocking/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── quickstart/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── quickstart-esbuild-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── esbuild.config.js │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ └── index.tsx │ │ │ └── tsconfig.json │ │ ├── quickstart-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── quickstart-rspack-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── postcss.config.mjs │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ └── tsconfig.json │ │ ├── quickstart-webpack-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .swcrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── index.html │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ └── index.tsx │ │ │ ├── tsconfig.json │ │ │ └── webpack.config.js │ │ ├── router-monorepo-react-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── packages/ │ │ │ │ ├── app/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── main.tsx │ │ │ │ │ │ ├── rootComponent.tsx │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-feature/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ │ ├── PostIdPage.tsx │ │ │ │ │ │ ├── PostList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-query/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ │ │ ├── posts.tsx │ │ │ │ │ │ └── postsQueryOptions.tsx │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ └── router/ │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $postId.ts │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── vite.config.ts │ │ │ └── pnpm-workspace.yaml.example │ │ ├── router-monorepo-simple/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── packages/ │ │ │ │ ├── app/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── main.tsx │ │ │ │ │ │ ├── rootComponent.tsx │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-feature/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ │ ├── PostIdPage.tsx │ │ │ │ │ │ ├── PostList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ └── router/ │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── fetch/ │ │ │ │ │ │ └── posts.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $postId.ts │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── vite.config.ts │ │ │ └── pnpm-workspace.yaml.example │ │ ├── router-monorepo-simple-lazy/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── packages/ │ │ │ │ ├── app/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── main.tsx │ │ │ │ │ │ ├── rootComponent.tsx │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-feature/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── PostIdPage.tsx │ │ │ │ │ │ └── PostList.tsx │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ └── router/ │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── fetch/ │ │ │ │ │ │ └── posts.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $postId.ts │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── vite.config.ts │ │ │ └── pnpm-workspace.yaml.example │ │ ├── scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── search-validator-adapters/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Content.tsx │ │ │ │ │ ├── Header.tsx │ │ │ │ │ ├── Search.tsx │ │ │ │ │ └── Users.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── users/ │ │ │ │ │ ├── arktype.index.tsx │ │ │ │ │ ├── valibot.index.tsx │ │ │ │ │ └── zod.index.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── arktype.test-d.tsx │ │ │ │ ├── arktype.test.tsx │ │ │ │ ├── valibot.test-d.tsx │ │ │ │ └── valibot.test.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-bare/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Counter.css │ │ │ │ │ └── Counter.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ └── UserError.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── customScript[.]js.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-auth/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── prisma/ │ │ │ │ ├── migrations/ │ │ │ │ │ ├── 20240811183753_init/ │ │ │ │ │ │ └── migration.sql │ │ │ │ │ └── migration_lock.toml │ │ │ │ └── schema.prisma │ │ │ ├── prisma.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Auth.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useMutation.ts │ │ │ │ ├── prisma-generated/ │ │ │ │ │ ├── browser.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── commonInputTypes.ts │ │ │ │ │ ├── enums.ts │ │ │ │ │ ├── internal/ │ │ │ │ │ │ ├── class.ts │ │ │ │ │ │ ├── prismaNamespace.ts │ │ │ │ │ │ └── prismaNamespaceBrowser.ts │ │ │ │ │ ├── models/ │ │ │ │ │ │ └── User.ts │ │ │ │ │ └── models.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ └── posts.route.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ └── signup.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ ├── prisma.ts │ │ │ │ ├── seo.ts │ │ │ │ └── session.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-authjs/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── auth/ │ │ │ │ │ │ └── $.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ └── protected.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ └── auth.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-cloudflare/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ └── UserError.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── customScript[.]js.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ ├── vite.config.ts │ │ │ ├── worker-configuration.d.ts │ │ │ └── wrangler.jsonc │ │ ├── start-basic-react-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$id.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.route.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.route.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-rsc/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ └── posts_.$postId.deep.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── renderPosts.tsx │ │ │ │ └── seo.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-static/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-bun/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── eslint.config.js │ │ │ ├── package.json │ │ │ ├── prettier.config.js │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── server.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ └── Header.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api.demo-names.ts │ │ │ │ │ ├── demo.start.api-request.tsx │ │ │ │ │ ├── demo.start.server-funcs.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-clerk-basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ ├── posts.tsx │ │ │ │ │ │ └── profile.$.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── start.ts │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ └── seo.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-convex-trellaux/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── convex/ │ │ │ │ ├── README.md │ │ │ │ ├── _generated/ │ │ │ │ │ ├── api.d.ts │ │ │ │ │ ├── api.js │ │ │ │ │ ├── dataModel.d.ts │ │ │ │ │ ├── server.d.ts │ │ │ │ │ └── server.js │ │ │ │ ├── board.ts │ │ │ │ ├── crons.ts │ │ │ │ ├── schema.ts │ │ │ │ └── tsconfig.json │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Board.tsx │ │ │ │ │ ├── CancelButton.tsx │ │ │ │ │ ├── Card.tsx │ │ │ │ │ ├── Column.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── EditableText.tsx │ │ │ │ │ ├── IconLink.tsx │ │ │ │ │ ├── Loader.tsx │ │ │ │ │ ├── NewCard.tsx │ │ │ │ │ ├── NewColumn.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ └── SaveButton.tsx │ │ │ │ ├── db/ │ │ │ │ │ └── schema.ts │ │ │ │ ├── hooks/ │ │ │ │ │ └── useOfflineIndicator.tsx │ │ │ │ ├── icons/ │ │ │ │ │ └── icons.tsx │ │ │ │ ├── queries.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── boards.$boardId.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ ├── types.ts │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ └── seo.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-counter/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ └── index.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-i18n-paraglide/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ ├── extensions.json │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── messages/ │ │ │ │ ├── de.json │ │ │ │ └── en.json │ │ │ ├── package.json │ │ │ ├── project.inlang/ │ │ │ │ ├── .gitignore │ │ │ │ ├── project_id │ │ │ │ └── settings.json │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── prerender.ts │ │ │ │ ├── seo.ts │ │ │ │ └── translated-pathnames.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-large/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── createRoutes.mjs │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── absolute.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── linkProps.tsx │ │ │ │ │ ├── params/ │ │ │ │ │ │ ├── $paramsPlaceholder.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── relative.tsx │ │ │ │ │ └── search/ │ │ │ │ │ ├── route.tsx │ │ │ │ │ └── searchPlaceholder.tsx │ │ │ │ ├── styles.css │ │ │ │ └── typePrimitives.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-material-ui/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Counter.tsx │ │ │ │ │ ├── CustomButtonLink.tsx │ │ │ │ │ ├── CustomLink.tsx │ │ │ │ │ └── Header.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── setup/ │ │ │ │ └── theme.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-streaming-data-from-server-functions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-supabase-basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Auth.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useMutation.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ └── signup.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ ├── seo.ts │ │ │ │ └── supabase.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-tailwind-v4/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-trellaux/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Board.tsx │ │ │ │ │ ├── CancelButton.tsx │ │ │ │ │ ├── Card.tsx │ │ │ │ │ ├── Column.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── EditableText.tsx │ │ │ │ │ ├── IconLink.tsx │ │ │ │ │ ├── Loader.tsx │ │ │ │ │ ├── NewCard.tsx │ │ │ │ │ ├── NewColumn.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ └── SaveButton.tsx │ │ │ │ ├── db/ │ │ │ │ │ ├── board.ts │ │ │ │ │ └── schema.ts │ │ │ │ ├── hooks/ │ │ │ │ │ └── useOfflineIndicator.tsx │ │ │ │ ├── icons/ │ │ │ │ │ └── icons.tsx │ │ │ │ ├── queries.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── boards.$boardId.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ ├── types.ts │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ └── seo.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-workos/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── prettier.config.js │ │ │ ├── src/ │ │ │ │ ├── app.css │ │ │ │ ├── components/ │ │ │ │ │ ├── footer.tsx │ │ │ │ │ └── sign-in-button.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authenticated/ │ │ │ │ │ │ └── account.tsx │ │ │ │ │ ├── _authenticated.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── auth/ │ │ │ │ │ │ └── callback.tsx │ │ │ │ │ ├── client.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── logout.tsx │ │ │ │ ├── start.ts │ │ │ │ └── vite-env.d.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── view-transitions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── explore.tsx │ │ │ │ │ ├── how-it-works.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── with-framer-motion/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── with-trpc/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── spinner.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── dashboard.index.tsx │ │ │ │ │ ├── dashboard.posts.$postId.tsx │ │ │ │ │ ├── dashboard.posts.index.tsx │ │ │ │ │ ├── dashboard.posts.tsx │ │ │ │ │ ├── dashboard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── server/ │ │ │ │ │ ├── server.ts │ │ │ │ │ └── trpc.ts │ │ │ │ ├── styles.css │ │ │ │ └── trpc.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ └── with-trpc-react-query/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── main.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── router.tsx │ │ │ ├── routes/ │ │ │ │ ├── -components/ │ │ │ │ │ └── spinner.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── dashboard.index.tsx │ │ │ │ ├── dashboard.posts.$postId.tsx │ │ │ │ ├── dashboard.posts.index.tsx │ │ │ │ ├── dashboard.posts.tsx │ │ │ │ ├── dashboard.tsx │ │ │ │ └── index.tsx │ │ │ ├── server/ │ │ │ │ ├── server.ts │ │ │ │ └── trpc.ts │ │ │ └── styles.css │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid/ │ │ ├── authenticated-routes/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.dashboard.tsx │ │ │ │ │ ├── _auth.invoices.$invoiceId.tsx │ │ │ │ │ ├── _auth.invoices.index.tsx │ │ │ │ │ ├── _auth.invoices.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ └── utils.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── authenticated-routes-firebase/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── firebase/ │ │ │ │ │ └── config.ts │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.dashboard.tsx │ │ │ │ │ ├── _auth.invoices.$invoiceId.tsx │ │ │ │ │ ├── _auth.invoices.index.tsx │ │ │ │ │ ├── _auth.invoices.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ ├── utils.ts │ │ │ │ └── vite.d.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-default-search-params/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-devtools-panel/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── App.tsx │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ └── PostErrorComponent.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-non-nested-devtools/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-solid-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.lazy.tsx │ │ │ │ ├── posts.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-solid-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── postsQueryOptions.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-ssr-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── server.js │ │ │ ├── src/ │ │ │ │ ├── entry-client.tsx │ │ │ │ ├── entry-server.tsx │ │ │ │ ├── fetch-polyfill.js │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routerContext.tsx │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── error.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── posts/ │ │ │ │ ├── $postId.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── route.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-ssr-streaming-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── server.js │ │ │ ├── src/ │ │ │ │ ├── entry-client.tsx │ │ │ │ ├── entry-server.tsx │ │ │ │ ├── fetch-polyfill.js │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routerContext.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ ├── Counter.tsx │ │ │ │ │ │ └── PostErrorComponent.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── counter.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── basic-virtual-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── routes.ts │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── file-based-subtree/ │ │ │ │ │ │ └── hello/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── route.tsx │ │ │ │ │ │ ├── universe.tsx │ │ │ │ │ │ └── world.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ ├── layout/ │ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ │ └── second-layout.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── posts-detail.tsx │ │ │ │ │ │ ├── posts-home.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ └── root.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── basic-virtual-inside-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout/ │ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ │ └── _layout-2.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ ├── __virtual.ts │ │ │ │ │ │ ├── details.tsx │ │ │ │ │ │ ├── home.tsx │ │ │ │ │ │ └── lets-go/ │ │ │ │ │ │ ├── deeper/ │ │ │ │ │ │ │ ├── __virtual.ts │ │ │ │ │ │ │ └── home.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── deferred-data/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── i18n-paraglide/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── messages/ │ │ │ │ ├── de.json │ │ │ │ └── en.json │ │ │ ├── package.json │ │ │ ├── project.inlang/ │ │ │ │ ├── .gitignore │ │ │ │ ├── project_id │ │ │ │ └── settings.json │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── kitchen-sink/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── Expensive.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ ├── styles.css │ │ │ │ ├── useMutation.tsx │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── kitchen-sink-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Breadcrumbs.tsx │ │ │ │ │ ├── InvoiceFields.tsx │ │ │ │ │ └── Spinner.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useMutation.tsx │ │ │ │ │ └── useSessionStorage.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── (this-folder-is-not-in-the-url)/ │ │ │ │ │ │ └── route-group.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.profile.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── _pathlessLayout.route-a.tsx │ │ │ │ │ ├── _pathlessLayout.route-b.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── dashboard.index.tsx │ │ │ │ │ ├── dashboard.invoices.$invoiceId.tsx │ │ │ │ │ ├── dashboard.invoices.index.tsx │ │ │ │ │ ├── dashboard.invoices.route.tsx │ │ │ │ │ ├── dashboard.route.tsx │ │ │ │ │ ├── dashboard.users.index.tsx │ │ │ │ │ ├── dashboard.users.route.tsx │ │ │ │ │ ├── dashboard.users.user.tsx │ │ │ │ │ ├── expensive/ │ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ │ └── Expensive.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── kitchen-sink-solid-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── Expensive.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ ├── styles.css │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.dev.json │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── kitchen-sink-solid-query-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── InvoiceFields.tsx │ │ │ │ │ └── Spinner.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useSessionStorage.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth.profile.tsx │ │ │ │ │ ├── _auth.tsx │ │ │ │ │ ├── _pathlessLayout.route-a.tsx │ │ │ │ │ ├── _pathlessLayout.route-b.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── dashboard.index.tsx │ │ │ │ │ ├── dashboard.invoices.$invoiceId.tsx │ │ │ │ │ ├── dashboard.invoices.index.tsx │ │ │ │ │ ├── dashboard.invoices.route.tsx │ │ │ │ │ ├── dashboard.route.tsx │ │ │ │ │ ├── dashboard.users.index.tsx │ │ │ │ │ ├── dashboard.users.route.tsx │ │ │ │ │ ├── dashboard.users.user.tsx │ │ │ │ │ ├── expensive/ │ │ │ │ │ │ ├── -components/ │ │ │ │ │ │ │ └── Expensive.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── foo/ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── login.tsx │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── auth.tsx │ │ │ │ ├── mockTodos.ts │ │ │ │ ├── queryOptions.ts │ │ │ │ └── utils.tsx │ │ │ ├── tsconfig.dev.json │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── large-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── createRoutes.mjs │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── absolute.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── linkProps.tsx │ │ │ │ │ ├── params/ │ │ │ │ │ │ ├── $paramsPlaceholder.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── relative.tsx │ │ │ │ │ └── search/ │ │ │ │ │ ├── route.tsx │ │ │ │ │ └── searchPlaceholder.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── location-masking/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── navigation-blocking/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── quickstart/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── quickstart-esbuild-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── build.js │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── postcss.config.mjs │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ └── tsconfig.json │ │ ├── quickstart-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── quickstart-rspack-file-based/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── postcss.config.mjs │ │ │ ├── rsbuild.config.ts │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── env.d.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ └── tsconfig.json │ │ ├── quickstart-webpack-file-based/ │ │ │ ├── .babelrc │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── postcss.config.mjs │ │ │ ├── public/ │ │ │ │ └── index.html │ │ │ ├── src/ │ │ │ │ ├── app.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── webpack.config.js │ │ ├── router-monorepo-simple/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── packages/ │ │ │ │ ├── app/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── main.tsx │ │ │ │ │ │ ├── rootComponent.tsx │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-feature/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ │ ├── PostIdPage.tsx │ │ │ │ │ │ ├── PostList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ └── router/ │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── fetch/ │ │ │ │ │ │ └── posts.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $postId.ts │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── vite.config.ts │ │ │ └── pnpm-workspace.yaml.example │ │ ├── router-monorepo-simple-lazy/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── packages/ │ │ │ │ ├── app/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── main.tsx │ │ │ │ │ │ ├── rootComponent.tsx │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-feature/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── PostIdPage.tsx │ │ │ │ │ │ └── PostList.tsx │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ └── router/ │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── fetch/ │ │ │ │ │ │ └── posts.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $postId.ts │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── vite.config.ts │ │ │ └── pnpm-workspace.yaml.example │ │ ├── router-monorepo-solid-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .stackblitzrc │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── packages/ │ │ │ │ ├── app/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── main.tsx │ │ │ │ │ │ ├── rootComponent.tsx │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-feature/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ │ ├── PostIdPage.tsx │ │ │ │ │ │ ├── PostList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ ├── post-query/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── postQueryOptions.tsx │ │ │ │ │ │ ├── posts.tsx │ │ │ │ │ │ └── postsQueryOptions.tsx │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── vite.config.ts │ │ │ │ └── router/ │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ │ ├── router.tsx │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $postId.ts │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── vite.config.ts │ │ │ └── pnpm-workspace.yaml.example │ │ ├── scroll-restoration/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── search-validator-adapters/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Content.tsx │ │ │ │ │ ├── Header.tsx │ │ │ │ │ ├── Search.tsx │ │ │ │ │ └── Users.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── users/ │ │ │ │ │ ├── arktype.index.tsx │ │ │ │ │ ├── valibot.index.tsx │ │ │ │ │ └── zod.index.tsx │ │ │ │ └── styles.css │ │ │ ├── tests/ │ │ │ │ ├── arktype.test-d.tsx │ │ │ │ ├── arktype.test.tsx │ │ │ │ ├── valibot.test-d.tsx │ │ │ │ └── valibot.test.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ └── UserError.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-auth/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .env │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── prisma/ │ │ │ │ ├── migrations/ │ │ │ │ │ ├── 20240811183753_init/ │ │ │ │ │ │ └── migration.sql │ │ │ │ │ └── migration_lock.toml │ │ │ │ └── schema.prisma │ │ │ ├── prisma.config.ts │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Auth.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useMutation.ts │ │ │ │ ├── prisma-generated/ │ │ │ │ │ ├── browser.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── commonInputTypes.ts │ │ │ │ │ ├── enums.ts │ │ │ │ │ ├── internal/ │ │ │ │ │ │ ├── class.ts │ │ │ │ │ │ ├── prismaNamespace.ts │ │ │ │ │ │ └── prismaNamespaceBrowser.ts │ │ │ │ │ ├── models/ │ │ │ │ │ │ └── User.ts │ │ │ │ │ └── models.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ └── posts.route.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ └── signup.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ ├── prisma.ts │ │ │ │ ├── seo.ts │ │ │ │ └── session.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-authjs/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── auth/ │ │ │ │ │ │ └── $.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ └── protected.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ └── auth.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-cloudflare/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ └── UserError.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── customScript[.]js.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ ├── vite.config.ts │ │ │ ├── worker-configuration.d.ts │ │ │ └── wrangler.jsonc │ │ ├── start-basic-netlify/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── netlify.toml │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ └── UserError.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── customScript[.]js.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-nitro/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── NotFound.tsx │ │ │ │ │ ├── PostError.tsx │ │ │ │ │ └── UserError.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$userId.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── customScript[.]js.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-solid-query/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── users.$id.ts │ │ │ │ │ │ └── users.ts │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.route.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.route.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-basic-static/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ ├── _nested-layout/ │ │ │ │ │ │ │ ├── route-a.tsx │ │ │ │ │ │ │ └── route-b.tsx │ │ │ │ │ │ └── _nested-layout.tsx │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ ├── deferred.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ ├── posts_.$postId.deep.tsx │ │ │ │ │ ├── redirect.tsx │ │ │ │ │ ├── users.$userId.tsx │ │ │ │ │ ├── users.index.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── loggingMiddleware.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── seo.ts │ │ │ │ └── users.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-bun/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── eslint.config.js │ │ │ ├── package.json │ │ │ ├── prettier.config.js │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── server.ts │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ └── Header.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api.demo-names.ts │ │ │ │ │ ├── demo.start.api-request.tsx │ │ │ │ │ ├── demo.start.server-funcs.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-convex-better-auth/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── README.md │ │ │ ├── convex/ │ │ │ │ ├── README.md │ │ │ │ ├── _generated/ │ │ │ │ │ ├── api.d.ts │ │ │ │ │ ├── api.js │ │ │ │ │ ├── dataModel.d.ts │ │ │ │ │ ├── server.d.ts │ │ │ │ │ └── server.js │ │ │ │ ├── auth.config.ts │ │ │ │ ├── auth.ts │ │ │ │ ├── convex.config.ts │ │ │ │ ├── http.ts │ │ │ │ ├── myFunctions.ts │ │ │ │ ├── schema.ts │ │ │ │ └── tsconfig.json │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ └── login-signup-form.tsx │ │ │ │ ├── library/ │ │ │ │ │ ├── auth-client.ts │ │ │ │ │ ├── auth-server.ts │ │ │ │ │ ├── convex-client.ts │ │ │ │ │ ├── server.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── providers/ │ │ │ │ │ └── convex.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ └── dashboard.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── auth/ │ │ │ │ │ │ └── $.ts │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-counter/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Counter.css │ │ │ │ │ └── Counter.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-i18n-paraglide/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ ├── extensions.json │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── messages/ │ │ │ │ ├── de.json │ │ │ │ └── en.json │ │ │ ├── package.json │ │ │ ├── project.inlang/ │ │ │ │ ├── .gitignore │ │ │ │ ├── project_id │ │ │ │ └── settings.json │ │ │ ├── public/ │ │ │ │ ├── manifest.json │ │ │ │ └── robots.txt │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── server.ts │ │ │ │ ├── styles.css │ │ │ │ └── utils/ │ │ │ │ ├── prerender.ts │ │ │ │ ├── seo.ts │ │ │ │ └── translated-pathnames.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-large/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── createRoutes.mjs │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── absolute.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── linkProps.tsx │ │ │ │ │ ├── params/ │ │ │ │ │ │ ├── $paramsPlaceholder.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── relative.tsx │ │ │ │ │ └── search/ │ │ │ │ │ ├── route.tsx │ │ │ │ │ └── searchPlaceholder.tsx │ │ │ │ ├── styles.css │ │ │ │ └── typePrimitives.tsx │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-streaming-data-from-server-functions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .prettierignore │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-supabase-basic/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── site.webmanifest │ │ │ ├── src/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Auth.tsx │ │ │ │ │ ├── DefaultCatchBoundary.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ └── NotFound.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ └── useMutation.ts │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _authed/ │ │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ │ └── posts.tsx │ │ │ │ │ ├── _authed.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── login.tsx │ │ │ │ │ ├── logout.tsx │ │ │ │ │ └── signup.tsx │ │ │ │ ├── styles/ │ │ │ │ │ └── app.css │ │ │ │ └── utils/ │ │ │ │ ├── posts.ts │ │ │ │ ├── seo.ts │ │ │ │ └── supabase.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── start-tailwind-v4/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .prettierignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── router.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── styles/ │ │ │ │ └── app.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── view-transitions/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── routeTree.gen.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── explore.tsx │ │ │ │ │ ├── how-it-works.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ ├── with-framer-motion/ │ │ │ ├── .devcontainer/ │ │ │ │ └── devcontainer.json │ │ │ ├── .gitignore │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── main.tsx │ │ │ │ └── styles.css │ │ │ ├── tsconfig.json │ │ │ └── vite.config.js │ │ └── with-trpc/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── main.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── routes/ │ │ │ │ ├── -components/ │ │ │ │ │ └── spinner.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── dashboard.index.tsx │ │ │ │ ├── dashboard.posts.$postId.tsx │ │ │ │ ├── dashboard.posts.index.tsx │ │ │ │ ├── dashboard.posts.tsx │ │ │ │ ├── dashboard.tsx │ │ │ │ └── index.tsx │ │ │ ├── server/ │ │ │ │ ├── server.ts │ │ │ │ └── trpc.ts │ │ │ ├── styles.css │ │ │ └── trpc.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── vue/ │ ├── basic/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ └── VueLogo.vue │ │ │ ├── main.tsx │ │ │ ├── posts.lazy.tsx │ │ │ ├── posts.ts │ │ │ └── styles.css │ │ ├── tsconfig.dev.json │ │ ├── tsconfig.json │ │ └── vite.config.js │ ├── basic-file-based-jsx/ │ │ ├── .devcontainer/ │ │ │ └── devcontainer.json │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── EditingAComponent.tsx │ │ │ │ ├── EditingBComponent.tsx │ │ │ │ ├── NotFoundComponent.vue │ │ │ │ ├── NotRemountDepsComponent.tsx │ │ │ │ ├── PostErrorComponent.vue │ │ │ │ ├── RemountDepsComponent.tsx │ │ │ │ └── VueLogo.vue │ │ │ ├── main.tsx │ │ │ ├── posts.tsx │ │ │ ├── routeTree.gen.ts │ │ │ ├── routes/ │ │ │ │ ├── (another-group)/ │ │ │ │ │ └── onlyrouteinside.tsx │ │ │ │ ├── (group)/ │ │ │ │ │ ├── _layout.insidelayout.tsx │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── inside.tsx │ │ │ │ │ ├── lazyinside.tsx │ │ │ │ │ └── subfolder/ │ │ │ │ │ └── inside.tsx │ │ │ │ ├── __root.tsx │ │ │ │ ├── _layout/ │ │ │ │ │ ├── _layout-2/ │ │ │ │ │ │ ├── layout-a.tsx │ │ │ │ │ │ └── layout-b.tsx │ │ │ │ │ └── _layout-2.tsx │ │ │ │ ├── _layout.tsx │ │ │ │ ├── editing-a.tsx │ │ │ │ ├── editing-b.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── notRemountDeps.tsx │ │ │ │ ├── posts.$postId.tsx │ │ │ │ ├── posts.index.tsx │ │ │ │ ├── posts.tsx │ │ │ │ ├── posts_.$postId.edit.tsx │ │ │ │ ├── remountDeps.tsx │ │ │ │ ├── sfcComponent.component.vue │ │ │ │ ├── sfcComponent.tsx │ │ │ │ └── 대한민국.tsx │ │ │ └── styles.css │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── basic-file-based-sfc/ │ ├── .devcontainer/ │ │ └── devcontainer.json │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── components/ │ │ │ └── VueLogo.vue │ │ ├── main.ts │ │ ├── posts.ts │ │ ├── routeTree.gen.ts │ │ ├── routes/ │ │ │ ├── (another-group)/ │ │ │ │ ├── onlyrouteinside.component.vue │ │ │ │ └── onlyrouteinside.ts │ │ │ ├── (group)/ │ │ │ │ ├── _layout.component.vue │ │ │ │ ├── _layout.insidelayout.component.vue │ │ │ │ ├── _layout.insidelayout.ts │ │ │ │ ├── _layout.ts │ │ │ │ ├── inside.component.vue │ │ │ │ ├── inside.ts │ │ │ │ ├── lazyinside.component.vue │ │ │ │ ├── lazyinside.ts │ │ │ │ └── subfolder/ │ │ │ │ ├── inside.component.vue │ │ │ │ └── inside.ts │ │ │ ├── __root.component.vue │ │ │ ├── __root.notFoundComponent.vue │ │ │ ├── __root.ts │ │ │ ├── _layout/ │ │ │ │ ├── _layout-2/ │ │ │ │ │ ├── layout-a.component.vue │ │ │ │ │ ├── layout-a.ts │ │ │ │ │ ├── layout-b.component.vue │ │ │ │ │ └── layout-b.ts │ │ │ │ ├── _layout-2.component.vue │ │ │ │ └── _layout-2.ts │ │ │ ├── _layout.component.vue │ │ │ ├── _layout.ts │ │ │ ├── editing-a.component.vue │ │ │ ├── editing-a.ts │ │ │ ├── editing-b.component.vue │ │ │ ├── editing-b.ts │ │ │ ├── index.component.vue │ │ │ ├── index.ts │ │ │ ├── notRemountDeps.component.vue │ │ │ ├── notRemountDeps.ts │ │ │ ├── posts.$postId.component.vue │ │ │ ├── posts.$postId.errorComponent.vue │ │ │ ├── posts.$postId.ts │ │ │ ├── posts.component.vue │ │ │ ├── posts.index.component.vue │ │ │ ├── posts.index.ts │ │ │ ├── posts.ts │ │ │ ├── posts_.$postId.edit.component.vue │ │ │ ├── posts_.$postId.edit.ts │ │ │ ├── remountDeps.component.vue │ │ │ ├── remountDeps.ts │ │ │ ├── 대한민국.component.vue │ │ │ └── 대한민국.ts │ │ ├── styles.css │ │ └── vue-shims.d.ts │ ├── tsconfig.json │ └── vite.config.ts ├── gpt/ │ └── generate.js ├── how-to-guides-implementation-plan.md ├── how-to-testing-plan.md ├── knip.json ├── labeler-config.yml ├── media/ │ └── logo.sketch ├── nx.json ├── package.json ├── packages/ │ ├── arktype-adapter/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tests/ │ │ │ ├── index.test-d.ts │ │ │ └── index.test.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── eslint-plugin-router/ │ │ ├── .attw.json │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ ├── create-route-property-order.rule.test.ts │ │ │ │ ├── create-route-property-order.utils.test.ts │ │ │ │ ├── route-param-names.rule.test.ts │ │ │ │ ├── route-param-names.utils.test.ts │ │ │ │ ├── test-utils.test.ts │ │ │ │ └── test-utils.ts │ │ │ ├── index.ts │ │ │ ├── rules/ │ │ │ │ ├── create-route-property-order/ │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── create-route-property-order.rule.ts │ │ │ │ │ └── create-route-property-order.utils.ts │ │ │ │ └── route-param-names/ │ │ │ │ ├── constants.ts │ │ │ │ ├── route-param-names.rule.ts │ │ │ │ └── route-param-names.utils.ts │ │ │ ├── rules.ts │ │ │ ├── types.ts │ │ │ └── utils/ │ │ │ ├── detect-router-imports.ts │ │ │ └── get-docs-url.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── history/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tests/ │ │ │ ├── createHashHistory.test.ts │ │ │ ├── createMemoryHistory.test.ts │ │ │ └── parseHref.test.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── nitro-v2-vite-plugin/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── react-router/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── compositions/ │ │ │ │ └── router-query/ │ │ │ │ └── SKILL.md │ │ │ ├── lifecycle/ │ │ │ │ └── migrate-from-react-router/ │ │ │ │ └── SKILL.md │ │ │ └── react-router/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── Asset.tsx │ │ │ ├── CatchBoundary.tsx │ │ │ ├── ClientOnly.tsx │ │ │ ├── HeadContent.dev.tsx │ │ │ ├── HeadContent.tsx │ │ │ ├── Match.tsx │ │ │ ├── Matches.tsx │ │ │ ├── RouterProvider.tsx │ │ │ ├── SafeFragment.tsx │ │ │ ├── ScriptOnce.tsx │ │ │ ├── Scripts.tsx │ │ │ ├── ScrollRestoration.tsx │ │ │ ├── Transitioner.tsx │ │ │ ├── awaited.tsx │ │ │ ├── fileRoute.ts │ │ │ ├── headContentUtils.tsx │ │ │ ├── history.ts │ │ │ ├── index.dev.tsx │ │ │ ├── index.tsx │ │ │ ├── lazyRouteComponent.tsx │ │ │ ├── link.tsx │ │ │ ├── matchContext.tsx │ │ │ ├── not-found.tsx │ │ │ ├── renderRouteNotFound.tsx │ │ │ ├── route.tsx │ │ │ ├── router.ts │ │ │ ├── routerContext.tsx │ │ │ ├── routerStores.ts │ │ │ ├── scroll-restoration.tsx │ │ │ ├── ssr/ │ │ │ │ ├── RouterClient.tsx │ │ │ │ ├── RouterServer.tsx │ │ │ │ ├── client.ts │ │ │ │ ├── defaultRenderHandler.tsx │ │ │ │ ├── defaultStreamHandler.tsx │ │ │ │ ├── renderRouterToStream.tsx │ │ │ │ ├── renderRouterToString.tsx │ │ │ │ ├── serializer.ts │ │ │ │ └── server.ts │ │ │ ├── structuralSharing.ts │ │ │ ├── typePrimitives.ts │ │ │ ├── useBlocker.tsx │ │ │ ├── useCanGoBack.ts │ │ │ ├── useLoaderData.tsx │ │ │ ├── useLoaderDeps.tsx │ │ │ ├── useLocation.tsx │ │ │ ├── useMatch.tsx │ │ │ ├── useNavigate.tsx │ │ │ ├── useParams.tsx │ │ │ ├── useRouteContext.ts │ │ │ ├── useRouter.tsx │ │ │ ├── useRouterState.tsx │ │ │ ├── useSearch.tsx │ │ │ └── utils.ts │ │ ├── tests/ │ │ │ ├── ClientOnly.test.tsx │ │ │ ├── Matches.test-d.tsx │ │ │ ├── Matches.test.tsx │ │ │ ├── RouterProvider.test-d.tsx │ │ │ ├── Scripts.test.tsx │ │ │ ├── blocker.test.tsx │ │ │ ├── createLazyRoute.test.tsx │ │ │ ├── disableGlobalCatchBoundary.test.tsx │ │ │ ├── errorComponent.test.tsx │ │ │ ├── fileRoute.test-d.tsx │ │ │ ├── fileRoute.test.ts │ │ │ ├── lazy/ │ │ │ │ ├── error.tsx │ │ │ │ ├── heavy.tsx │ │ │ │ ├── mockHeavyDependenciesRoute.tsx │ │ │ │ └── normal.tsx │ │ │ ├── link.bench.tsx │ │ │ ├── link.test-d.tsx │ │ │ ├── link.test.tsx │ │ │ ├── loaders.test.tsx │ │ │ ├── navigate.test.tsx │ │ │ ├── not-found.test.tsx │ │ │ ├── optional-path-params.test-d.tsx │ │ │ ├── optional-path-params.test.tsx │ │ │ ├── redirect.test.tsx │ │ │ ├── redirects.test-d.tsx │ │ │ ├── route.test-d.tsx │ │ │ ├── route.test.tsx │ │ │ ├── routeApi.test-d.tsx │ │ │ ├── routeContext.test.tsx │ │ │ ├── router.test-d.tsx │ │ │ ├── router.test.tsx │ │ │ ├── searchMiddleware.test.tsx │ │ │ ├── setupTests.tsx │ │ │ ├── store-updates-during-navigation.test.tsx │ │ │ ├── useBlocker.test-d.tsx │ │ │ ├── useBlocker.test.tsx │ │ │ ├── useCanGoBack.test.tsx │ │ │ ├── useLoaderData.test-d.tsx │ │ │ ├── useLocation.test-d.tsx │ │ │ ├── useLocation.test.tsx │ │ │ ├── useMatch.test-d.tsx │ │ │ ├── useMatch.test.tsx │ │ │ ├── useNavigate.test-d.tsx │ │ │ ├── useNavigate.test.tsx │ │ │ ├── useParams.test-d.tsx │ │ │ ├── useParams.test.tsx │ │ │ ├── useRouteContext.test-d.tsx │ │ │ ├── useRouterState.test-d.tsx │ │ │ ├── useSearch.test-d.tsx │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.legacy.json │ │ └── vite.config.ts │ ├── react-router-devtools/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── TanStackRouterDevtools.tsx │ │ │ ├── TanStackRouterDevtoolsPanel.tsx │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── react-router-ssr-query/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── react-start/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ ├── lifecycle/ │ │ │ │ └── migrate-from-nextjs/ │ │ │ │ └── SKILL.md │ │ │ └── react-start/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── client-only.ts │ │ │ ├── client-rpc.ts │ │ │ ├── client.tsx │ │ │ ├── default-entry/ │ │ │ │ ├── client.tsx │ │ │ │ ├── server.ts │ │ │ │ └── start.ts │ │ │ ├── index.ts │ │ │ ├── plugin/ │ │ │ │ └── vite.ts │ │ │ ├── server-only.ts │ │ │ ├── server-rpc.ts │ │ │ ├── server.tsx │ │ │ ├── ssr-rpc.ts │ │ │ └── useServerFn.ts │ │ ├── tsconfig.json │ │ ├── vite.config.server-entry.ts │ │ └── vite.config.ts │ ├── react-start-client/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StartClient.tsx │ │ │ ├── hydrateStart.ts │ │ │ ├── index.tsx │ │ │ ├── renderRSC.tsx │ │ │ └── tests/ │ │ │ ├── createServerFn.test-d.tsx │ │ │ └── setupTests.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── react-start-server/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StartServer.tsx │ │ │ ├── defaultRenderHandler.tsx │ │ │ ├── defaultStreamHandler.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-cli/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── tsr.cjs │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── generate.ts │ │ │ ├── index.ts │ │ │ └── watch.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ └── router-core/ │ │ │ ├── SKILL.md │ │ │ ├── auth-and-guards/ │ │ │ │ └── SKILL.md │ │ │ ├── code-splitting/ │ │ │ │ └── SKILL.md │ │ │ ├── data-loading/ │ │ │ │ └── SKILL.md │ │ │ ├── navigation/ │ │ │ │ └── SKILL.md │ │ │ ├── not-found-and-errors/ │ │ │ │ └── SKILL.md │ │ │ ├── path-params/ │ │ │ │ └── SKILL.md │ │ │ ├── search-params/ │ │ │ │ ├── SKILL.md │ │ │ │ └── references/ │ │ │ │ └── validation-patterns.md │ │ │ ├── ssr/ │ │ │ │ └── SKILL.md │ │ │ └── type-safety/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── Matches.ts │ │ │ ├── RouterProvider.ts │ │ │ ├── config.ts │ │ │ ├── defer.ts │ │ │ ├── fileRoute.ts │ │ │ ├── global.ts │ │ │ ├── history.ts │ │ │ ├── index.ts │ │ │ ├── isServer/ │ │ │ │ ├── client.ts │ │ │ │ ├── development.ts │ │ │ │ └── server.ts │ │ │ ├── link.ts │ │ │ ├── load-matches.ts │ │ │ ├── location.ts │ │ │ ├── lru-cache.ts │ │ │ ├── manifest.ts │ │ │ ├── new-process-route-tree.ts │ │ │ ├── not-found.ts │ │ │ ├── path.ts │ │ │ ├── qss.ts │ │ │ ├── redirect.ts │ │ │ ├── rewrite.ts │ │ │ ├── root.ts │ │ │ ├── route.ts │ │ │ ├── routeInfo.ts │ │ │ ├── router.ts │ │ │ ├── scroll-restoration.ts │ │ │ ├── searchMiddleware.ts │ │ │ ├── searchParams.ts │ │ │ ├── ssr/ │ │ │ │ ├── client.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── createRequestHandler.ts │ │ │ │ ├── handlerCallback.ts │ │ │ │ ├── headers.ts │ │ │ │ ├── json.ts │ │ │ │ ├── serializer/ │ │ │ │ │ ├── RawStream.ts │ │ │ │ │ ├── ShallowErrorPlugin.ts │ │ │ │ │ ├── seroval-plugins.ts │ │ │ │ │ └── transformer.ts │ │ │ │ ├── server.ts │ │ │ │ ├── ssr-client.ts │ │ │ │ ├── ssr-match-id.ts │ │ │ │ ├── ssr-server.ts │ │ │ │ ├── transformStreamWithRouter.ts │ │ │ │ ├── tsrScript.ts │ │ │ │ └── types.ts │ │ │ ├── stores.ts │ │ │ ├── structuralSharing.ts │ │ │ ├── typePrimitives.ts │ │ │ ├── useLoaderData.ts │ │ │ ├── useLoaderDeps.ts │ │ │ ├── useNavigate.ts │ │ │ ├── useParams.ts │ │ │ ├── useRouteContext.ts │ │ │ ├── useSearch.ts │ │ │ ├── utils.ts │ │ │ ├── validators.ts │ │ │ └── vite-env.d.ts │ │ ├── tests/ │ │ │ ├── RawStream.test.ts │ │ │ ├── build-location.test.ts │ │ │ ├── callbacks.test.ts │ │ │ ├── closing-tag-detection.bench.ts │ │ │ ├── curly-params-smoke.test.ts │ │ │ ├── dangerous-protocols.test.ts │ │ │ ├── getNormalizedURL.test.ts │ │ │ ├── getOrigin.test.ts │ │ │ ├── granular-stores.test.ts │ │ │ ├── hydrate.test.ts │ │ │ ├── load.test-d.ts │ │ │ ├── load.test.ts │ │ │ ├── lru.test.ts │ │ │ ├── mask.test.ts │ │ │ ├── match-by-path.test.ts │ │ │ ├── new-process-route-tree.test.ts │ │ │ ├── optional-path-params-clean.test.ts │ │ │ ├── optional-path-params.test.ts │ │ │ ├── path.test.ts │ │ │ ├── qss.test.ts │ │ │ ├── redirect.test-d.ts │ │ │ ├── remountDeps.test-d.ts │ │ │ ├── remountDeps.test.ts │ │ │ ├── routerTestUtils.ts │ │ │ ├── searchMiddleware.test.ts │ │ │ ├── searchParams.test.ts │ │ │ ├── serializer-recursion.test-d.ts │ │ │ ├── serializer.test-d.ts │ │ │ ├── skip-route-on-parse-error.test.ts │ │ │ ├── ssr-match-id.test.ts │ │ │ ├── utils.test-d.ts │ │ │ └── utils.test.ts │ │ ├── tsconfig.json │ │ ├── vite-minify-plugin.ts │ │ └── vite.config.ts │ ├── router-devtools/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-devtools-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── media/ │ │ │ └── logo.sketch │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AgeTicker.tsx │ │ │ ├── BaseTanStackRouterDevtoolsPanel.tsx │ │ │ ├── Explorer.tsx │ │ │ ├── FloatingTanStackRouterDevtools.tsx │ │ │ ├── NavigateButton.tsx │ │ │ ├── TanStackRouterDevtoolsCore.tsx │ │ │ ├── TanStackRouterDevtoolsPanelCore.tsx │ │ │ ├── context.ts │ │ │ ├── index.tsx │ │ │ ├── logo.tsx │ │ │ ├── theme.tsx │ │ │ ├── tokens.ts │ │ │ ├── useLocalStorage.ts │ │ │ ├── useMediaQuery.ts │ │ │ ├── useStyles.tsx │ │ │ └── utils.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-generator/ │ │ ├── .prettierignore │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── config.ts │ │ │ ├── filesystem/ │ │ │ │ ├── physical/ │ │ │ │ │ ├── getRouteNodes.ts │ │ │ │ │ └── rootPathId.ts │ │ │ │ └── virtual/ │ │ │ │ ├── config.ts │ │ │ │ ├── getRouteNodes.ts │ │ │ │ └── loadConfigFile.ts │ │ │ ├── generator.ts │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ ├── plugin/ │ │ │ │ └── types.ts │ │ │ ├── template.ts │ │ │ ├── transform/ │ │ │ │ ├── transform.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── types.ts │ │ │ ├── utils.ts │ │ │ └── validate-route-params.ts │ │ ├── tests/ │ │ │ ├── config/ │ │ │ │ └── json-config/ │ │ │ │ └── tsr.config.json │ │ │ ├── config.test.ts │ │ │ ├── deny-route-group-config/ │ │ │ │ ├── flat-flat/ │ │ │ │ │ ├── (group).index.tsx │ │ │ │ │ ├── (group).tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── about.tsx │ │ │ │ ├── flat-nested/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── nested/ │ │ │ │ │ ├── (group).index.tsx │ │ │ │ │ └── (group).tsx │ │ │ │ ├── nested-flat/ │ │ │ │ │ ├── (group)/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── (group).tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── about.tsx │ │ │ │ └── nested-nested/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ └── nested/ │ │ │ │ ├── (group)/ │ │ │ │ │ └── index.tsx │ │ │ │ └── (group).tsx │ │ │ ├── deny-route-group-config.test.ts │ │ │ ├── generator/ │ │ │ │ ├── add-extensions-custom/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ ├── append-and-prepend/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── custom-scaffolding/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── api/ │ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ │ ├── foo.lazy.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── snapshots/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ ├── foo.lazy.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── custom-tokens/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── _1nd3x.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── blog/ │ │ │ │ │ │ ├── $slug.tsx │ │ │ │ │ │ ├── _1nd3x.tsx │ │ │ │ │ │ └── _r0ut3_.tsx │ │ │ │ │ └── posts/ │ │ │ │ │ ├── $postId/ │ │ │ │ │ │ ├── _1nd3x.tsx │ │ │ │ │ │ └── deep.tsx │ │ │ │ │ ├── _1nd3x.tsx │ │ │ │ │ └── _r0ut3_.tsx │ │ │ │ ├── dot-escaped/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── nested[.]js.double[.]ext[.]js.tsx │ │ │ │ │ ├── nested[.]js.script[.]js.tsx │ │ │ │ │ ├── nested[.]js.tsx │ │ │ │ │ └── script[.]js.tsx │ │ │ │ ├── duplicate-fullPath/ │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _auth/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── escaped-custom-tokens/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── [_1nd3x].tsx │ │ │ │ │ ├── _1nd3x.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── blog._r0ut3_.tsx │ │ │ │ │ ├── nested.[_1nd3x].tsx │ │ │ │ │ └── posts.[_r0ut3_].tsx │ │ │ │ ├── escaped-special-strings/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── [_]auth.route.tsx │ │ │ │ │ ├── [_]layout.tsx │ │ │ │ │ ├── [_]prefix[_]middle[_]suffix.tsx │ │ │ │ │ ├── [index].tsx │ │ │ │ │ ├── [lazy].tsx │ │ │ │ │ ├── [route].tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── api[_]v2[_]users.tsx │ │ │ │ │ ├── blog[_].tsx │ │ │ │ │ ├── foo[_]bar.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── nested.[index].tsx │ │ │ │ ├── export-variations/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── export-separate-from-declaration.tsx │ │ │ │ │ └── export-with-as.tsx │ │ │ │ ├── file-modification-verboseFileRoutes-false/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── (test)/ │ │ │ │ │ │ │ └── -keep-directory │ │ │ │ │ │ ├── .gitignore │ │ │ │ │ │ └── __root.tsx │ │ │ │ │ ├── snapshots/ │ │ │ │ │ │ ├── bar.lazy.tsx │ │ │ │ │ │ ├── foo.bar.tsx │ │ │ │ │ │ ├── foo.tsx │ │ │ │ │ │ ├── initiallyEmpty.lazy.tsx │ │ │ │ │ │ ├── initiallyEmpty.tsx │ │ │ │ │ │ └── initiallyLazy.tsx │ │ │ │ │ ├── template-verbose.tsx │ │ │ │ │ ├── template.lazy.tsx │ │ │ │ │ └── template.tsx │ │ │ │ ├── file-modification-verboseFileRoutes-true/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── (test)/ │ │ │ │ │ │ │ └── -keep-directory │ │ │ │ │ │ ├── .gitignore │ │ │ │ │ │ └── __root.tsx │ │ │ │ │ ├── snapshots/ │ │ │ │ │ │ ├── bar.lazy.tsx │ │ │ │ │ │ ├── foo.bar.tsx │ │ │ │ │ │ ├── foo.tsx │ │ │ │ │ │ ├── initiallyEmpty.lazy.tsx │ │ │ │ │ │ ├── initiallyEmpty.tsx │ │ │ │ │ │ └── initiallyLazy.tsx │ │ │ │ │ ├── template-verbose.tsx │ │ │ │ │ ├── template.lazy.tsx │ │ │ │ │ └── template.tsx │ │ │ │ ├── flat/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── blog.$slug.index.tsx │ │ │ │ │ ├── blog.index.tsx │ │ │ │ │ ├── blog.route.tsx │ │ │ │ │ ├── blog_.$blogId.$slug.route.tsx │ │ │ │ │ ├── blog_.$blogId.$slug_.bar.tsx │ │ │ │ │ ├── blog_.$blogId.route.tsx │ │ │ │ │ ├── blog_.$blogId_.edit.tsx │ │ │ │ │ ├── blog_.stats.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts.$postId.deep.tsx │ │ │ │ │ ├── posts.$postId.index.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ ├── flat-route-group/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── app.(compras)/ │ │ │ │ │ │ ├── compras_._mas.divisiones.tsx │ │ │ │ │ │ ├── compras_._mas.tsx │ │ │ │ │ │ └── compras_.ordenes.tsx │ │ │ │ │ └── app.tsx │ │ │ │ ├── invalid-param-names/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── $123.tsx │ │ │ │ │ ├── $user-name.tsx │ │ │ │ │ ├── $validParam.tsx │ │ │ │ │ └── __root.tsx │ │ │ │ ├── lazy/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── posts.$postId.tsx │ │ │ │ │ ├── posts.index.tsx │ │ │ │ │ ├── posts.route.lazy.tsx │ │ │ │ │ └── posts.route.tsx │ │ │ │ ├── lazy-multi-slug-params/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── sub/ │ │ │ │ │ ├── $a.$b.lazy.tsx │ │ │ │ │ ├── route.lazy.tsx │ │ │ │ │ └── test.lazy.tsx │ │ │ │ ├── lazy-only-layout-and-index/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── path.index.lazy.tsx │ │ │ │ │ └── path.lazy.tsx │ │ │ │ ├── lazy-only-layout-and-index-in-pathless/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── _layout/ │ │ │ │ │ ├── path.index.lazy.tsx │ │ │ │ │ ├── path.lazy.tsx │ │ │ │ │ └── route.tsx │ │ │ │ ├── nested-layouts/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── (folder)/ │ │ │ │ │ │ └── in-folder.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── _layout-a1/ │ │ │ │ │ │ └── foo.tsx │ │ │ │ │ ├── _layout-a1.tsx │ │ │ │ │ ├── _layout-a2/ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ ├── _layout-a2.tsx │ │ │ │ │ ├── foo/ │ │ │ │ │ │ ├── _layout-b5/ │ │ │ │ │ │ │ ├── $id.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── jested/ │ │ │ │ │ │ ├── _layout-b3/ │ │ │ │ │ │ │ ├── _layout-c2/ │ │ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ │ │ ├── _layout-c2.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── _layout-b3.tsx │ │ │ │ │ │ ├── _layout-b4/ │ │ │ │ │ │ │ └── foo.tsx │ │ │ │ │ │ ├── _layout-b4.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ └── nested/ │ │ │ │ │ ├── _layout-b1/ │ │ │ │ │ │ ├── _layout-c1/ │ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ │ ├── _layout-c1.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── _layout-b1.tsx │ │ │ │ │ ├── _layout-b2/ │ │ │ │ │ │ └── foo.tsx │ │ │ │ │ └── _layout-b2.tsx │ │ │ │ ├── nested-route-groups-with-layouts-before-physical/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── (group-a)/ │ │ │ │ │ │ ├── _layout-a/ │ │ │ │ │ │ │ ├── login.tsx │ │ │ │ │ │ │ └── signup.tsx │ │ │ │ │ │ └── _layout-a.tsx │ │ │ │ │ ├── (group-b)/ │ │ │ │ │ │ ├── _layout-b/ │ │ │ │ │ │ │ └── dashboard.tsx │ │ │ │ │ │ └── _layout-b.tsx │ │ │ │ │ ├── (group-c)/ │ │ │ │ │ │ ├── _layout-c/ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── _layout-c.tsx │ │ │ │ │ └── __root.tsx │ │ │ │ ├── nested-verboseFileRoutes-false/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ │ └── settings.tsx │ │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ │ ├── blog/ │ │ │ │ │ │ │ ├── $slug.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── blog_/ │ │ │ │ │ │ │ ├── $blogId/ │ │ │ │ │ │ │ │ ├── $slug/ │ │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ │ ├── $slug_/ │ │ │ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ ├── $blogId_/ │ │ │ │ │ │ │ │ └── edit.tsx │ │ │ │ │ │ │ └── stats.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── posts/ │ │ │ │ │ │ ├── $postId/ │ │ │ │ │ │ │ ├── deep.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ └── tests.test-d.ts │ │ │ │ ├── nested-verboseFileRoutes-true/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── _pathlessLayout/ │ │ │ │ │ │ │ └── settings.tsx │ │ │ │ │ │ ├── _pathlessLayout.tsx │ │ │ │ │ │ ├── blog/ │ │ │ │ │ │ │ ├── $slug.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ ├── blog_/ │ │ │ │ │ │ │ ├── $blogId/ │ │ │ │ │ │ │ │ ├── $slug/ │ │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ │ ├── $slug_/ │ │ │ │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ │ ├── $blogId_/ │ │ │ │ │ │ │ │ └── edit.tsx │ │ │ │ │ │ │ └── stats.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── posts/ │ │ │ │ │ │ ├── $postId/ │ │ │ │ │ │ │ ├── deep.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ └── tests.test-d.ts │ │ │ │ ├── no-duplicate-route-segment/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── foo/ │ │ │ │ │ └── _layout/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── route.tsx │ │ │ │ ├── no-formatted-route-tree/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── nested/ │ │ │ │ │ ├── child.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── numbers-in-path/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── 01-example/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── 02.index.tsx │ │ │ │ │ ├── 03.tsx │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── about.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── only-root/ │ │ │ │ │ ├── routeTree.generated.snapshot.ts │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ └── __root.tsx │ │ │ │ ├── path-above-route-in-group/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ └── a/ │ │ │ │ │ └── $b/ │ │ │ │ │ └── (c)/ │ │ │ │ │ ├── d/ │ │ │ │ │ │ └── e/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── route.tsx │ │ │ │ ├── prefix-suffix/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── wildcard/ │ │ │ │ │ ├── $.tsx │ │ │ │ │ ├── prefix{$}.tsx │ │ │ │ │ ├── {$}[.]suffix.tsx │ │ │ │ │ └── {$}suffix.tsx │ │ │ │ ├── regex-tokens-inline/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── dashboard.home-page.tsx │ │ │ │ │ ├── dashboard.main-layout.tsx │ │ │ │ │ ├── dashboard.settings.tsx │ │ │ │ │ └── index-page.tsx │ │ │ │ ├── regex-tokens-json/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── dashboard.home-page.tsx │ │ │ │ │ │ ├── dashboard.main-layout.tsx │ │ │ │ │ │ ├── dashboard.settings.tsx │ │ │ │ │ │ └── index-page.tsx │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── route-groups/ │ │ │ │ │ ├── _layout.tsx │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── (bar)/ │ │ │ │ │ │ ├── _bar.hello.tsx │ │ │ │ │ │ └── _bar.tsx │ │ │ │ │ ├── (foo)/ │ │ │ │ │ │ └── asdf/ │ │ │ │ │ │ ├── (another-group)/ │ │ │ │ │ │ │ ├── _layout.baz.tsx │ │ │ │ │ │ │ └── _layout.tsx │ │ │ │ │ │ ├── (bar)/ │ │ │ │ │ │ │ ├── $id.tsx │ │ │ │ │ │ │ ├── _layout.about.tsx │ │ │ │ │ │ │ └── _layout.xyz.lazy.tsx │ │ │ │ │ │ ├── _layout.foo.tsx │ │ │ │ │ │ └── _layout.tsx │ │ │ │ │ └── __root.tsx │ │ │ │ ├── routeFileIgnore/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── blog.ignoredPattern.tsx │ │ │ │ │ ├── blog.route.tsx │ │ │ │ │ ├── imIgnored.route.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── routeFilePrefix/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── imNotIncluded.route.tsx │ │ │ │ │ ├── r&__root.tsx │ │ │ │ │ ├── r&blog.ignoredPattern.tsx │ │ │ │ │ ├── r&blog.route.tsx │ │ │ │ │ └── r&index.tsx │ │ │ │ ├── single-level/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ ├── types-disabled/ │ │ │ │ │ ├── routeTree.snapshot.js │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── posts/ │ │ │ │ │ │ └── $postId.tsx │ │ │ │ │ ├── posts.tsx │ │ │ │ │ └── users.$userId.tsx │ │ │ │ ├── virtual/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── db/ │ │ │ │ │ │ ├── dashboard-index.tsx │ │ │ │ │ │ ├── dashboard-invoices.tsx │ │ │ │ │ │ ├── dashboard.tsx │ │ │ │ │ │ ├── invoice-detail.tsx │ │ │ │ │ │ └── invoices-index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── layout.tsx │ │ │ │ │ ├── pages.tsx │ │ │ │ │ ├── root.tsx │ │ │ │ │ └── subtree/ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ ├── $id.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── virtual-config-file-default-export/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── db/ │ │ │ │ │ │ │ ├── dashboard-index.tsx │ │ │ │ │ │ │ ├── dashboard-invoices.tsx │ │ │ │ │ │ │ ├── dashboard.tsx │ │ │ │ │ │ │ ├── invoice-detail.tsx │ │ │ │ │ │ │ └── invoices-index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── layout.tsx │ │ │ │ │ │ ├── pages.tsx │ │ │ │ │ │ ├── root.tsx │ │ │ │ │ │ └── subtree/ │ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ │ ├── $id.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-config-file-named-export/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── db/ │ │ │ │ │ │ │ ├── dashboard-index.tsx │ │ │ │ │ │ │ ├── dashboard-invoices.tsx │ │ │ │ │ │ │ ├── dashboard.tsx │ │ │ │ │ │ │ ├── invoice-detail.tsx │ │ │ │ │ │ │ └── invoices-index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── layout.tsx │ │ │ │ │ │ ├── pages.tsx │ │ │ │ │ │ ├── root.tsx │ │ │ │ │ │ └── subtree/ │ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ │ ├── $id.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-inside-nested/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── foo/ │ │ │ │ │ │ ├── bar/ │ │ │ │ │ │ │ ├── __virtual.ts │ │ │ │ │ │ │ ├── details.tsx │ │ │ │ │ │ │ ├── home.tsx │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ └── bar.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── virtual-inside-with-escaped-underscore/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── nested/ │ │ │ │ │ ├── __virtual.ts │ │ │ │ │ ├── auth.tsx │ │ │ │ │ ├── callback.tsx │ │ │ │ │ └── home.tsx │ │ │ │ ├── virtual-nested-layouts-with-virtual-route/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── a.tsx │ │ │ │ │ ├── b.tsx │ │ │ │ │ ├── home.tsx │ │ │ │ │ └── layout/ │ │ │ │ │ ├── first-layout.tsx │ │ │ │ │ └── second-layout.tsx │ │ │ │ ├── virtual-physical-empty-path-conflict-root/ │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ └── merged/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── route.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-physical-empty-path-conflict-virtual/ │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── about.tsx │ │ │ │ │ │ └── merged/ │ │ │ │ │ │ ├── about.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-physical-empty-path-merge/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── about.tsx │ │ │ │ │ │ └── merged/ │ │ │ │ │ │ ├── contact.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-physical-layout-and-index/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── feature/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-physical-no-prefix/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ │ ├── about.tsx │ │ │ │ │ │ └── merged/ │ │ │ │ │ │ ├── contact.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── routes.ts │ │ │ │ │ └── tsr.config.json │ │ │ │ ├── virtual-root-sibling-routes/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── device/ │ │ │ │ │ │ └── route.tsx │ │ │ │ │ └── history/ │ │ │ │ │ └── route.tsx │ │ │ │ ├── virtual-sibling-routes/ │ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ │ └── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── layout.tsx │ │ │ │ │ ├── post-detail.tsx │ │ │ │ │ └── posts.tsx │ │ │ │ └── virtual-with-escaped-underscore/ │ │ │ │ ├── routeTree.snapshot.ts │ │ │ │ └── routes/ │ │ │ │ ├── __root.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── physical-routes/ │ │ │ │ ├── [_]auth.route.tsx │ │ │ │ ├── [_]hello.tsx │ │ │ │ └── index.tsx │ │ │ ├── generator.test.ts │ │ │ ├── utils.test.ts │ │ │ └── validate-route-params.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.legacy.json │ │ └── vite.config.ts │ ├── router-plugin/ │ │ ├── ARCHITECTURE-CODE-SPLITTING.md │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── router-plugin/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── core/ │ │ │ │ ├── code-splitter/ │ │ │ │ │ ├── compilers.ts │ │ │ │ │ ├── framework-options.ts │ │ │ │ │ ├── path-ids.ts │ │ │ │ │ ├── plugins/ │ │ │ │ │ │ ├── framework-plugins.ts │ │ │ │ │ │ └── react-refresh-route-components.ts │ │ │ │ │ └── plugins.ts │ │ │ │ ├── config.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── route-autoimport-plugin.ts │ │ │ │ ├── route-hmr-statement.ts │ │ │ │ ├── router-code-splitter-plugin.ts │ │ │ │ ├── router-composed-plugin.ts │ │ │ │ ├── router-generator-plugin.ts │ │ │ │ ├── router-hmr-plugin.ts │ │ │ │ └── utils.ts │ │ │ ├── esbuild.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── rspack.ts │ │ │ ├── vite.ts │ │ │ └── webpack.ts │ │ ├── tests/ │ │ │ ├── add-hmr/ │ │ │ │ ├── snapshots/ │ │ │ │ │ ├── react/ │ │ │ │ │ │ ├── arrow-function@false.tsx │ │ │ │ │ │ ├── arrow-function@true.tsx │ │ │ │ │ │ ├── createRootRoute-inline-component@false.tsx │ │ │ │ │ │ ├── createRootRoute-inline-component@true.tsx │ │ │ │ │ │ ├── destructuring@false.tsx │ │ │ │ │ │ ├── destructuring@true.tsx │ │ │ │ │ │ ├── explicit-undefined-component@false.tsx │ │ │ │ │ │ ├── explicit-undefined-component@true.tsx │ │ │ │ │ │ ├── function-declaration@false.tsx │ │ │ │ │ │ └── function-declaration@true.tsx │ │ │ │ │ └── solid/ │ │ │ │ │ ├── arrow-function@false.tsx │ │ │ │ │ └── arrow-function@true.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── react/ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ ├── createRootRoute-inline-component.tsx │ │ │ │ │ ├── destructuring.tsx │ │ │ │ │ ├── explicit-undefined-component.tsx │ │ │ │ │ └── function-declaration.tsx │ │ │ │ └── solid/ │ │ │ │ └── arrow-function.tsx │ │ │ ├── add-hmr.test.ts │ │ │ ├── code-splitter/ │ │ │ │ ├── shared/ │ │ │ │ │ └── imported.tsx │ │ │ │ ├── snapshots/ │ │ │ │ │ ├── react/ │ │ │ │ │ │ ├── 1-default/ │ │ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ │ │ ├── arrow-function@component.tsx │ │ │ │ │ │ │ ├── arrow-function@errorComponent.tsx │ │ │ │ │ │ │ ├── arrow-function@notFoundComponent.tsx │ │ │ │ │ │ │ ├── arrow-function@shared.tsx │ │ │ │ │ │ │ ├── boolean-null-literals.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@component.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@errorComponent.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@notFoundComponent.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@shared.tsx │ │ │ │ │ │ │ ├── chinese.tsx │ │ │ │ │ │ │ ├── chinese@component.tsx │ │ │ │ │ │ │ ├── chinese@errorComponent.tsx │ │ │ │ │ │ │ ├── chinese@notFoundComponent.tsx │ │ │ │ │ │ │ ├── chinese@shared.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@component.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@errorComponent.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@notFoundComponent.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@shared.tsx │ │ │ │ │ │ │ ├── circular-reference-function.tsx │ │ │ │ │ │ │ ├── circular-reference-function@component.tsx │ │ │ │ │ │ │ ├── circular-reference-function@errorComponent.tsx │ │ │ │ │ │ │ ├── circular-reference-function@notFoundComponent.tsx │ │ │ │ │ │ │ ├── circular-reference-function@shared.tsx │ │ │ │ │ │ │ ├── conditional-properties.tsx │ │ │ │ │ │ │ ├── conditional-properties@component.tsx │ │ │ │ │ │ │ ├── conditional-properties@errorComponent.tsx │ │ │ │ │ │ │ ├── conditional-properties@notFoundComponent.tsx │ │ │ │ │ │ │ ├── conditional-properties@shared.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@component.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@errorComponent.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@notFoundComponent.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@shared.tsx │ │ │ │ │ │ │ ├── destructured-export-nested.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@component.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@errorComponent.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@notFoundComponent.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@shared.tsx │ │ │ │ │ │ │ ├── destructured-export.tsx │ │ │ │ │ │ │ ├── destructured-export@component.tsx │ │ │ │ │ │ │ ├── destructured-export@errorComponent.tsx │ │ │ │ │ │ │ ├── destructured-export@notFoundComponent.tsx │ │ │ │ │ │ │ ├── destructured-export@shared.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@component.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@errorComponent.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@shared.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@component.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@errorComponent.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@notFoundComponent.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@shared.tsx │ │ │ │ │ │ │ ├── destructuring.tsx │ │ │ │ │ │ │ ├── destructuring@component.tsx │ │ │ │ │ │ │ ├── destructuring@errorComponent.tsx │ │ │ │ │ │ │ ├── destructuring@notFoundComponent.tsx │ │ │ │ │ │ │ ├── destructuring@shared.tsx │ │ │ │ │ │ │ ├── directive-prologue.tsx │ │ │ │ │ │ │ ├── directive-prologue@component.tsx │ │ │ │ │ │ │ ├── directive-prologue@errorComponent.tsx │ │ │ │ │ │ │ ├── directive-prologue@notFoundComponent.tsx │ │ │ │ │ │ │ ├── directive-prologue@shared.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@component.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@errorComponent.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@shared.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@component.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@errorComponent.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@notFoundComponent.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@shared.tsx │ │ │ │ │ │ │ ├── export-default-component.tsx │ │ │ │ │ │ │ ├── export-default-component@component.tsx │ │ │ │ │ │ │ ├── export-default-component@errorComponent.tsx │ │ │ │ │ │ │ ├── export-default-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── export-default-component@shared.tsx │ │ │ │ │ │ │ ├── function-as-parameter.tsx │ │ │ │ │ │ │ ├── function-as-parameter@component.tsx │ │ │ │ │ │ │ ├── function-as-parameter@errorComponent.tsx │ │ │ │ │ │ │ ├── function-as-parameter@notFoundComponent.tsx │ │ │ │ │ │ │ ├── function-as-parameter@shared.tsx │ │ │ │ │ │ │ ├── function-declaration.tsx │ │ │ │ │ │ │ ├── function-declaration@component.tsx │ │ │ │ │ │ │ ├── function-declaration@errorComponent.tsx │ │ │ │ │ │ │ ├── function-declaration@notFoundComponent.tsx │ │ │ │ │ │ │ ├── function-declaration@shared.tsx │ │ │ │ │ │ │ ├── importAttribute.tsx │ │ │ │ │ │ │ ├── importAttribute@component.tsx │ │ │ │ │ │ │ ├── importAttribute@errorComponent.tsx │ │ │ │ │ │ │ ├── importAttribute@notFoundComponent.tsx │ │ │ │ │ │ │ ├── importAttribute@shared.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@component.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@errorComponent.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@shared.tsx │ │ │ │ │ │ │ ├── imported-default-component.tsx │ │ │ │ │ │ │ ├── imported-default-component@component.tsx │ │ │ │ │ │ │ ├── imported-default-component@errorComponent.tsx │ │ │ │ │ │ │ ├── imported-default-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-default-component@shared.tsx │ │ │ │ │ │ │ ├── imported-errorComponent.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@component.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@errorComponent.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@shared.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@component.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@errorComponent.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@shared.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@component.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@errorComponent.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@shared.tsx │ │ │ │ │ │ │ ├── imported.tsx │ │ │ │ │ │ │ ├── imported@component.tsx │ │ │ │ │ │ │ ├── imported@errorComponent.tsx │ │ │ │ │ │ │ ├── imported@notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported@shared.tsx │ │ │ │ │ │ │ ├── inline.tsx │ │ │ │ │ │ │ ├── inline@component.tsx │ │ │ │ │ │ │ ├── inline@errorComponent.tsx │ │ │ │ │ │ │ ├── inline@notFoundComponent.tsx │ │ │ │ │ │ │ ├── inline@shared.tsx │ │ │ │ │ │ │ ├── random-number.tsx │ │ │ │ │ │ │ ├── random-number@component.tsx │ │ │ │ │ │ │ ├── random-number@errorComponent.tsx │ │ │ │ │ │ │ ├── random-number@notFoundComponent.tsx │ │ │ │ │ │ │ ├── random-number@shared.tsx │ │ │ │ │ │ │ ├── react-memo-component.tsx │ │ │ │ │ │ │ ├── react-memo-component@component.tsx │ │ │ │ │ │ │ ├── react-memo-component@errorComponent.tsx │ │ │ │ │ │ │ ├── react-memo-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── react-memo-component@shared.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@component.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@errorComponent.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@shared.tsx │ │ │ │ │ │ │ ├── retain-export-component.tsx │ │ │ │ │ │ │ ├── retain-export-component@component.tsx │ │ │ │ │ │ │ ├── retain-export-component@errorComponent.tsx │ │ │ │ │ │ │ ├── retain-export-component@notFoundComponent.tsx │ │ │ │ │ │ │ ├── retain-export-component@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-const.tsx │ │ │ │ │ │ │ ├── retain-exports-const@component.tsx │ │ │ │ │ │ │ ├── retain-exports-const@errorComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-const@notFoundComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-const@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@component.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@errorComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@notFoundComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-function.tsx │ │ │ │ │ │ │ ├── retain-exports-function@component.tsx │ │ │ │ │ │ │ ├── retain-exports-function@errorComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-function@notFoundComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-function@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-loader.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@component.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@errorComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@notFoundComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@shared.tsx │ │ │ │ │ │ │ ├── shared-class.tsx │ │ │ │ │ │ │ ├── shared-class@component.tsx │ │ │ │ │ │ │ ├── shared-class@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-class@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-class@shared.tsx │ │ │ │ │ │ │ ├── shared-destructured-export.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@component.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@shared.tsx │ │ │ │ │ │ │ ├── shared-destructured.tsx │ │ │ │ │ │ │ ├── shared-destructured@component.tsx │ │ │ │ │ │ │ ├── shared-destructured@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-destructured@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-destructured@shared.tsx │ │ │ │ │ │ │ ├── shared-exported.tsx │ │ │ │ │ │ │ ├── shared-exported@component.tsx │ │ │ │ │ │ │ ├── shared-exported@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-exported@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-exported@shared.tsx │ │ │ │ │ │ │ ├── shared-function.tsx │ │ │ │ │ │ │ ├── shared-function@component.tsx │ │ │ │ │ │ │ ├── shared-function@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-function@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-function@shared.tsx │ │ │ │ │ │ │ ├── shared-imported-binding.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@component.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@shared.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@component.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@shared.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@component.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@shared.tsx │ │ │ │ │ │ │ ├── shared-none.tsx │ │ │ │ │ │ │ ├── shared-none@component.tsx │ │ │ │ │ │ │ ├── shared-none@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-none@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-none@shared.tsx │ │ │ │ │ │ │ ├── shared-referencing-route.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@component.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@shared.tsx │ │ │ │ │ │ │ ├── shared-transitive.tsx │ │ │ │ │ │ │ ├── shared-transitive@component.tsx │ │ │ │ │ │ │ ├── shared-transitive@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-transitive@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-transitive@shared.tsx │ │ │ │ │ │ │ ├── shared-variable.tsx │ │ │ │ │ │ │ ├── shared-variable@component.tsx │ │ │ │ │ │ │ ├── shared-variable@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-variable@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-variable@shared.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@component.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@errorComponent.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@notFoundComponent.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@shared.tsx │ │ │ │ │ │ │ ├── undefined-literals.tsx │ │ │ │ │ │ │ ├── undefined-literals@component.tsx │ │ │ │ │ │ │ ├── undefined-literals@errorComponent.tsx │ │ │ │ │ │ │ ├── undefined-literals@notFoundComponent.tsx │ │ │ │ │ │ │ ├── undefined-literals@shared.tsx │ │ │ │ │ │ │ ├── useStateDestructure.tsx │ │ │ │ │ │ │ ├── useStateDestructure@component.tsx │ │ │ │ │ │ │ ├── useStateDestructure@errorComponent.tsx │ │ │ │ │ │ │ ├── useStateDestructure@notFoundComponent.tsx │ │ │ │ │ │ │ ├── useStateDestructure@shared.tsx │ │ │ │ │ │ │ ├── using.tsx │ │ │ │ │ │ │ ├── using@component.tsx │ │ │ │ │ │ │ ├── using@errorComponent.tsx │ │ │ │ │ │ │ ├── using@notFoundComponent.tsx │ │ │ │ │ │ │ └── using@shared.tsx │ │ │ │ │ │ ├── 2-components-combined-loader-separate/ │ │ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ │ │ ├── arrow-function@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── arrow-function@loader.tsx │ │ │ │ │ │ │ ├── arrow-function@shared.tsx │ │ │ │ │ │ │ ├── boolean-null-literals.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@loader.tsx │ │ │ │ │ │ │ ├── boolean-null-literals@shared.tsx │ │ │ │ │ │ │ ├── chinese.tsx │ │ │ │ │ │ │ ├── chinese@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── chinese@loader.tsx │ │ │ │ │ │ │ ├── chinese@shared.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@loader.tsx │ │ │ │ │ │ │ ├── circular-reference-arrow-function@shared.tsx │ │ │ │ │ │ │ ├── circular-reference-function.tsx │ │ │ │ │ │ │ ├── circular-reference-function@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── circular-reference-function@loader.tsx │ │ │ │ │ │ │ ├── circular-reference-function@shared.tsx │ │ │ │ │ │ │ ├── conditional-properties.tsx │ │ │ │ │ │ │ ├── conditional-properties@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── conditional-properties@loader.tsx │ │ │ │ │ │ │ ├── conditional-properties@shared.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@loader.tsx │ │ │ │ │ │ │ ├── destructured-export-multiple@shared.tsx │ │ │ │ │ │ │ ├── destructured-export-nested.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@loader.tsx │ │ │ │ │ │ │ ├── destructured-export-nested@shared.tsx │ │ │ │ │ │ │ ├── destructured-export.tsx │ │ │ │ │ │ │ ├── destructured-export@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── destructured-export@loader.tsx │ │ │ │ │ │ │ ├── destructured-export@shared.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@loader.tsx │ │ │ │ │ │ │ ├── destructured-react-memo-imported-component@shared.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@loader.tsx │ │ │ │ │ │ │ ├── destructured-route-options-defaults@shared.tsx │ │ │ │ │ │ │ ├── destructuring.tsx │ │ │ │ │ │ │ ├── destructuring@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── destructuring@loader.tsx │ │ │ │ │ │ │ ├── destructuring@shared.tsx │ │ │ │ │ │ │ ├── directive-prologue.tsx │ │ │ │ │ │ │ ├── directive-prologue@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── directive-prologue@loader.tsx │ │ │ │ │ │ │ ├── directive-prologue@shared.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@loader.tsx │ │ │ │ │ │ │ ├── explicit-undefined-component@shared.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@loader.tsx │ │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@shared.tsx │ │ │ │ │ │ │ ├── export-default-component.tsx │ │ │ │ │ │ │ ├── export-default-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── export-default-component@loader.tsx │ │ │ │ │ │ │ ├── export-default-component@shared.tsx │ │ │ │ │ │ │ ├── function-as-parameter.tsx │ │ │ │ │ │ │ ├── function-as-parameter@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── function-as-parameter@loader.tsx │ │ │ │ │ │ │ ├── function-as-parameter@shared.tsx │ │ │ │ │ │ │ ├── function-declaration.tsx │ │ │ │ │ │ │ ├── function-declaration@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── function-declaration@loader.tsx │ │ │ │ │ │ │ ├── function-declaration@shared.tsx │ │ │ │ │ │ │ ├── importAttribute.tsx │ │ │ │ │ │ │ ├── importAttribute@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── importAttribute@loader.tsx │ │ │ │ │ │ │ ├── importAttribute@shared.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@loader.tsx │ │ │ │ │ │ │ ├── imported-default-component-destructured-loader@shared.tsx │ │ │ │ │ │ │ ├── imported-default-component.tsx │ │ │ │ │ │ │ ├── imported-default-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-default-component@loader.tsx │ │ │ │ │ │ │ ├── imported-default-component@shared.tsx │ │ │ │ │ │ │ ├── imported-errorComponent.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@loader.tsx │ │ │ │ │ │ │ ├── imported-errorComponent@shared.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@loader.tsx │ │ │ │ │ │ │ ├── imported-notFoundComponent@shared.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@loader.tsx │ │ │ │ │ │ │ ├── imported-pendingComponent@shared.tsx │ │ │ │ │ │ │ ├── imported.tsx │ │ │ │ │ │ │ ├── imported@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── imported@loader.tsx │ │ │ │ │ │ │ ├── imported@shared.tsx │ │ │ │ │ │ │ ├── inline.tsx │ │ │ │ │ │ │ ├── inline@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── inline@loader.tsx │ │ │ │ │ │ │ ├── inline@shared.tsx │ │ │ │ │ │ │ ├── random-number.tsx │ │ │ │ │ │ │ ├── random-number@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── random-number@loader.tsx │ │ │ │ │ │ │ ├── random-number@shared.tsx │ │ │ │ │ │ │ ├── react-memo-component.tsx │ │ │ │ │ │ │ ├── react-memo-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── react-memo-component@loader.tsx │ │ │ │ │ │ │ ├── react-memo-component@shared.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@loader.tsx │ │ │ │ │ │ │ ├── react-memo-imported-component@shared.tsx │ │ │ │ │ │ │ ├── retain-export-component.tsx │ │ │ │ │ │ │ ├── retain-export-component@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── retain-export-component@loader.tsx │ │ │ │ │ │ │ ├── retain-export-component@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-const.tsx │ │ │ │ │ │ │ ├── retain-exports-const@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-const@loader.tsx │ │ │ │ │ │ │ ├── retain-exports-const@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@loader.tsx │ │ │ │ │ │ │ ├── retain-exports-destructured@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-function.tsx │ │ │ │ │ │ │ ├── retain-exports-function@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-function@loader.tsx │ │ │ │ │ │ │ ├── retain-exports-function@shared.tsx │ │ │ │ │ │ │ ├── retain-exports-loader.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@loader.tsx │ │ │ │ │ │ │ ├── retain-exports-loader@shared.tsx │ │ │ │ │ │ │ ├── shared-class.tsx │ │ │ │ │ │ │ ├── shared-class@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-class@loader.tsx │ │ │ │ │ │ │ ├── shared-class@shared.tsx │ │ │ │ │ │ │ ├── shared-destructured-export.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@loader.tsx │ │ │ │ │ │ │ ├── shared-destructured-export@shared.tsx │ │ │ │ │ │ │ ├── shared-destructured.tsx │ │ │ │ │ │ │ ├── shared-destructured@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-destructured@loader.tsx │ │ │ │ │ │ │ ├── shared-destructured@shared.tsx │ │ │ │ │ │ │ ├── shared-exported.tsx │ │ │ │ │ │ │ ├── shared-exported@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-exported@loader.tsx │ │ │ │ │ │ │ ├── shared-exported@shared.tsx │ │ │ │ │ │ │ ├── shared-function.tsx │ │ │ │ │ │ │ ├── shared-function@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-function@loader.tsx │ │ │ │ │ │ │ ├── shared-function@shared.tsx │ │ │ │ │ │ │ ├── shared-imported-binding.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@loader.tsx │ │ │ │ │ │ │ ├── shared-imported-binding@shared.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@loader.tsx │ │ │ │ │ │ │ ├── shared-indirect-ref@shared.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@loader.tsx │ │ │ │ │ │ │ ├── shared-jsx-component-ref@shared.tsx │ │ │ │ │ │ │ ├── shared-none.tsx │ │ │ │ │ │ │ ├── shared-none@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-none@loader.tsx │ │ │ │ │ │ │ ├── shared-none@shared.tsx │ │ │ │ │ │ │ ├── shared-referencing-route.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@loader.tsx │ │ │ │ │ │ │ ├── shared-referencing-route@shared.tsx │ │ │ │ │ │ │ ├── shared-transitive.tsx │ │ │ │ │ │ │ ├── shared-transitive@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-transitive@loader.tsx │ │ │ │ │ │ │ ├── shared-transitive@shared.tsx │ │ │ │ │ │ │ ├── shared-variable.tsx │ │ │ │ │ │ │ ├── shared-variable@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-variable@loader.tsx │ │ │ │ │ │ │ ├── shared-variable@shared.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@loader.tsx │ │ │ │ │ │ │ ├── shared-with-side-effect@shared.tsx │ │ │ │ │ │ │ ├── undefined-literals.tsx │ │ │ │ │ │ │ ├── undefined-literals@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── undefined-literals@loader.tsx │ │ │ │ │ │ │ ├── undefined-literals@shared.tsx │ │ │ │ │ │ │ ├── useStateDestructure.tsx │ │ │ │ │ │ │ ├── useStateDestructure@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── useStateDestructure@loader.tsx │ │ │ │ │ │ │ ├── useStateDestructure@shared.tsx │ │ │ │ │ │ │ ├── using.tsx │ │ │ │ │ │ │ ├── using@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ │ ├── using@loader.tsx │ │ │ │ │ │ │ └── using@shared.tsx │ │ │ │ │ │ └── 3-all-combined-errorComponent-separate/ │ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ │ ├── arrow-function@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── arrow-function@errorComponent.tsx │ │ │ │ │ │ ├── arrow-function@shared.tsx │ │ │ │ │ │ ├── boolean-null-literals.tsx │ │ │ │ │ │ ├── boolean-null-literals@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── boolean-null-literals@errorComponent.tsx │ │ │ │ │ │ ├── boolean-null-literals@shared.tsx │ │ │ │ │ │ ├── chinese.tsx │ │ │ │ │ │ ├── chinese@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── chinese@errorComponent.tsx │ │ │ │ │ │ ├── chinese@shared.tsx │ │ │ │ │ │ ├── circular-reference-arrow-function.tsx │ │ │ │ │ │ ├── circular-reference-arrow-function@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── circular-reference-arrow-function@errorComponent.tsx │ │ │ │ │ │ ├── circular-reference-arrow-function@shared.tsx │ │ │ │ │ │ ├── circular-reference-function.tsx │ │ │ │ │ │ ├── circular-reference-function@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── circular-reference-function@errorComponent.tsx │ │ │ │ │ │ ├── circular-reference-function@shared.tsx │ │ │ │ │ │ ├── conditional-properties.tsx │ │ │ │ │ │ ├── conditional-properties@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── conditional-properties@errorComponent.tsx │ │ │ │ │ │ ├── conditional-properties@shared.tsx │ │ │ │ │ │ ├── destructured-export-multiple.tsx │ │ │ │ │ │ ├── destructured-export-multiple@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── destructured-export-multiple@errorComponent.tsx │ │ │ │ │ │ ├── destructured-export-multiple@shared.tsx │ │ │ │ │ │ ├── destructured-export-nested.tsx │ │ │ │ │ │ ├── destructured-export-nested@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── destructured-export-nested@errorComponent.tsx │ │ │ │ │ │ ├── destructured-export-nested@shared.tsx │ │ │ │ │ │ ├── destructured-export.tsx │ │ │ │ │ │ ├── destructured-export@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── destructured-export@errorComponent.tsx │ │ │ │ │ │ ├── destructured-export@shared.tsx │ │ │ │ │ │ ├── destructured-react-memo-imported-component.tsx │ │ │ │ │ │ ├── destructured-react-memo-imported-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── destructured-react-memo-imported-component@errorComponent.tsx │ │ │ │ │ │ ├── destructured-react-memo-imported-component@shared.tsx │ │ │ │ │ │ ├── destructured-route-options-defaults.tsx │ │ │ │ │ │ ├── destructured-route-options-defaults@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── destructured-route-options-defaults@errorComponent.tsx │ │ │ │ │ │ ├── destructured-route-options-defaults@shared.tsx │ │ │ │ │ │ ├── destructuring.tsx │ │ │ │ │ │ ├── destructuring@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── destructuring@errorComponent.tsx │ │ │ │ │ │ ├── destructuring@shared.tsx │ │ │ │ │ │ ├── directive-prologue.tsx │ │ │ │ │ │ ├── directive-prologue@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── directive-prologue@errorComponent.tsx │ │ │ │ │ │ ├── directive-prologue@shared.tsx │ │ │ │ │ │ ├── explicit-undefined-component.tsx │ │ │ │ │ │ ├── explicit-undefined-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── explicit-undefined-component@errorComponent.tsx │ │ │ │ │ │ ├── explicit-undefined-component@shared.tsx │ │ │ │ │ │ ├── export-default-component-and-normal-notFound.tsx │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@errorComponent.tsx │ │ │ │ │ │ ├── export-default-component-and-normal-notFound@shared.tsx │ │ │ │ │ │ ├── export-default-component.tsx │ │ │ │ │ │ ├── export-default-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── export-default-component@errorComponent.tsx │ │ │ │ │ │ ├── export-default-component@shared.tsx │ │ │ │ │ │ ├── function-as-parameter.tsx │ │ │ │ │ │ ├── function-as-parameter@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── function-as-parameter@errorComponent.tsx │ │ │ │ │ │ ├── function-as-parameter@shared.tsx │ │ │ │ │ │ ├── function-declaration.tsx │ │ │ │ │ │ ├── function-declaration@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── function-declaration@errorComponent.tsx │ │ │ │ │ │ ├── function-declaration@shared.tsx │ │ │ │ │ │ ├── importAttribute.tsx │ │ │ │ │ │ ├── importAttribute@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── importAttribute@errorComponent.tsx │ │ │ │ │ │ ├── importAttribute@shared.tsx │ │ │ │ │ │ ├── imported-default-component-destructured-loader.tsx │ │ │ │ │ │ ├── imported-default-component-destructured-loader@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── imported-default-component-destructured-loader@errorComponent.tsx │ │ │ │ │ │ ├── imported-default-component-destructured-loader@shared.tsx │ │ │ │ │ │ ├── imported-default-component.tsx │ │ │ │ │ │ ├── imported-default-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── imported-default-component@errorComponent.tsx │ │ │ │ │ │ ├── imported-default-component@shared.tsx │ │ │ │ │ │ ├── imported-errorComponent.tsx │ │ │ │ │ │ ├── imported-errorComponent@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── imported-errorComponent@errorComponent.tsx │ │ │ │ │ │ ├── imported-errorComponent@shared.tsx │ │ │ │ │ │ ├── imported-notFoundComponent.tsx │ │ │ │ │ │ ├── imported-notFoundComponent@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── imported-notFoundComponent@errorComponent.tsx │ │ │ │ │ │ ├── imported-notFoundComponent@shared.tsx │ │ │ │ │ │ ├── imported-pendingComponent.tsx │ │ │ │ │ │ ├── imported-pendingComponent@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── imported-pendingComponent@errorComponent.tsx │ │ │ │ │ │ ├── imported-pendingComponent@shared.tsx │ │ │ │ │ │ ├── imported.tsx │ │ │ │ │ │ ├── imported@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── imported@errorComponent.tsx │ │ │ │ │ │ ├── imported@shared.tsx │ │ │ │ │ │ ├── inline.tsx │ │ │ │ │ │ ├── inline@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── inline@errorComponent.tsx │ │ │ │ │ │ ├── inline@shared.tsx │ │ │ │ │ │ ├── random-number.tsx │ │ │ │ │ │ ├── random-number@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── random-number@errorComponent.tsx │ │ │ │ │ │ ├── random-number@shared.tsx │ │ │ │ │ │ ├── react-memo-component.tsx │ │ │ │ │ │ ├── react-memo-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── react-memo-component@errorComponent.tsx │ │ │ │ │ │ ├── react-memo-component@shared.tsx │ │ │ │ │ │ ├── react-memo-imported-component.tsx │ │ │ │ │ │ ├── react-memo-imported-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── react-memo-imported-component@errorComponent.tsx │ │ │ │ │ │ ├── react-memo-imported-component@shared.tsx │ │ │ │ │ │ ├── retain-export-component.tsx │ │ │ │ │ │ ├── retain-export-component@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── retain-export-component@errorComponent.tsx │ │ │ │ │ │ ├── retain-export-component@shared.tsx │ │ │ │ │ │ ├── retain-exports-const.tsx │ │ │ │ │ │ ├── retain-exports-const@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── retain-exports-const@errorComponent.tsx │ │ │ │ │ │ ├── retain-exports-const@shared.tsx │ │ │ │ │ │ ├── retain-exports-destructured.tsx │ │ │ │ │ │ ├── retain-exports-destructured@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── retain-exports-destructured@errorComponent.tsx │ │ │ │ │ │ ├── retain-exports-destructured@shared.tsx │ │ │ │ │ │ ├── retain-exports-function.tsx │ │ │ │ │ │ ├── retain-exports-function@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── retain-exports-function@errorComponent.tsx │ │ │ │ │ │ ├── retain-exports-function@shared.tsx │ │ │ │ │ │ ├── retain-exports-loader.tsx │ │ │ │ │ │ ├── retain-exports-loader@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── retain-exports-loader@errorComponent.tsx │ │ │ │ │ │ ├── retain-exports-loader@shared.tsx │ │ │ │ │ │ ├── shared-class.tsx │ │ │ │ │ │ ├── shared-class@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-class@errorComponent.tsx │ │ │ │ │ │ ├── shared-class@shared.tsx │ │ │ │ │ │ ├── shared-destructured-export.tsx │ │ │ │ │ │ ├── shared-destructured-export@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-destructured-export@errorComponent.tsx │ │ │ │ │ │ ├── shared-destructured-export@shared.tsx │ │ │ │ │ │ ├── shared-destructured.tsx │ │ │ │ │ │ ├── shared-destructured@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-destructured@errorComponent.tsx │ │ │ │ │ │ ├── shared-destructured@shared.tsx │ │ │ │ │ │ ├── shared-exported.tsx │ │ │ │ │ │ ├── shared-exported@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-exported@errorComponent.tsx │ │ │ │ │ │ ├── shared-exported@shared.tsx │ │ │ │ │ │ ├── shared-function.tsx │ │ │ │ │ │ ├── shared-function@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-function@errorComponent.tsx │ │ │ │ │ │ ├── shared-function@shared.tsx │ │ │ │ │ │ ├── shared-imported-binding.tsx │ │ │ │ │ │ ├── shared-imported-binding@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-imported-binding@errorComponent.tsx │ │ │ │ │ │ ├── shared-imported-binding@shared.tsx │ │ │ │ │ │ ├── shared-indirect-ref.tsx │ │ │ │ │ │ ├── shared-indirect-ref@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-indirect-ref@errorComponent.tsx │ │ │ │ │ │ ├── shared-indirect-ref@shared.tsx │ │ │ │ │ │ ├── shared-jsx-component-ref.tsx │ │ │ │ │ │ ├── shared-jsx-component-ref@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-jsx-component-ref@errorComponent.tsx │ │ │ │ │ │ ├── shared-jsx-component-ref@shared.tsx │ │ │ │ │ │ ├── shared-none.tsx │ │ │ │ │ │ ├── shared-none@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-none@errorComponent.tsx │ │ │ │ │ │ ├── shared-none@shared.tsx │ │ │ │ │ │ ├── shared-referencing-route.tsx │ │ │ │ │ │ ├── shared-referencing-route@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-referencing-route@errorComponent.tsx │ │ │ │ │ │ ├── shared-referencing-route@shared.tsx │ │ │ │ │ │ ├── shared-transitive.tsx │ │ │ │ │ │ ├── shared-transitive@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-transitive@errorComponent.tsx │ │ │ │ │ │ ├── shared-transitive@shared.tsx │ │ │ │ │ │ ├── shared-variable.tsx │ │ │ │ │ │ ├── shared-variable@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-variable@errorComponent.tsx │ │ │ │ │ │ ├── shared-variable@shared.tsx │ │ │ │ │ │ ├── shared-with-side-effect.tsx │ │ │ │ │ │ ├── shared-with-side-effect@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── shared-with-side-effect@errorComponent.tsx │ │ │ │ │ │ ├── shared-with-side-effect@shared.tsx │ │ │ │ │ │ ├── undefined-literals.tsx │ │ │ │ │ │ ├── undefined-literals@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── undefined-literals@errorComponent.tsx │ │ │ │ │ │ ├── undefined-literals@shared.tsx │ │ │ │ │ │ ├── useStateDestructure.tsx │ │ │ │ │ │ ├── useStateDestructure@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── useStateDestructure@errorComponent.tsx │ │ │ │ │ │ ├── useStateDestructure@shared.tsx │ │ │ │ │ │ ├── using.tsx │ │ │ │ │ │ ├── using@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── using@errorComponent.tsx │ │ │ │ │ │ └── using@shared.tsx │ │ │ │ │ └── solid/ │ │ │ │ │ ├── 1-default/ │ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ │ ├── arrow-function@component.tsx │ │ │ │ │ │ ├── arrow-function@errorComponent.tsx │ │ │ │ │ │ ├── arrow-function@notFoundComponent.tsx │ │ │ │ │ │ └── arrow-function@shared.tsx │ │ │ │ │ ├── 2-components-combined-loader-separate/ │ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ │ ├── arrow-function@component---errorComponent---notFoundComponent---pendingComponent.tsx │ │ │ │ │ │ ├── arrow-function@loader.tsx │ │ │ │ │ │ └── arrow-function@shared.tsx │ │ │ │ │ └── 3-all-combined-errorComponent-separate/ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ ├── arrow-function@component---loader---notFoundComponent---pendingComponent.tsx │ │ │ │ │ ├── arrow-function@errorComponent.tsx │ │ │ │ │ └── arrow-function@shared.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── react/ │ │ │ │ │ ├── arrow-function.tsx │ │ │ │ │ ├── boolean-null-literals.tsx │ │ │ │ │ ├── chinese.tsx │ │ │ │ │ ├── circular-reference-arrow-function.tsx │ │ │ │ │ ├── circular-reference-function.tsx │ │ │ │ │ ├── conditional-properties.tsx │ │ │ │ │ ├── destructured-export-multiple.tsx │ │ │ │ │ ├── destructured-export-nested.tsx │ │ │ │ │ ├── destructured-export.tsx │ │ │ │ │ ├── destructured-react-memo-imported-component.tsx │ │ │ │ │ ├── destructured-route-options-defaults.tsx │ │ │ │ │ ├── destructuring.tsx │ │ │ │ │ ├── directive-prologue.tsx │ │ │ │ │ ├── explicit-undefined-component.tsx │ │ │ │ │ ├── export-default-component-and-normal-notFound.tsx │ │ │ │ │ ├── export-default-component.tsx │ │ │ │ │ ├── function-as-parameter.tsx │ │ │ │ │ ├── function-declaration.tsx │ │ │ │ │ ├── importAttribute.tsx │ │ │ │ │ ├── imported-default-component-destructured-loader.tsx │ │ │ │ │ ├── imported-default-component.tsx │ │ │ │ │ ├── imported-errorComponent.tsx │ │ │ │ │ ├── imported-notFoundComponent.tsx │ │ │ │ │ ├── imported-pendingComponent.tsx │ │ │ │ │ ├── imported.tsx │ │ │ │ │ ├── inline.tsx │ │ │ │ │ ├── random-number.tsx │ │ │ │ │ ├── react-memo-component.tsx │ │ │ │ │ ├── react-memo-imported-component.tsx │ │ │ │ │ ├── retain-export-component.tsx │ │ │ │ │ ├── retain-exports-const.tsx │ │ │ │ │ ├── retain-exports-destructured.tsx │ │ │ │ │ ├── retain-exports-function.tsx │ │ │ │ │ ├── retain-exports-loader.tsx │ │ │ │ │ ├── shared-class.tsx │ │ │ │ │ ├── shared-destructured-export.tsx │ │ │ │ │ ├── shared-destructured.tsx │ │ │ │ │ ├── shared-exported.tsx │ │ │ │ │ ├── shared-function.tsx │ │ │ │ │ ├── shared-imported-binding.tsx │ │ │ │ │ ├── shared-indirect-ref.tsx │ │ │ │ │ ├── shared-jsx-component-ref.tsx │ │ │ │ │ ├── shared-none.tsx │ │ │ │ │ ├── shared-referencing-route.tsx │ │ │ │ │ ├── shared-transitive.tsx │ │ │ │ │ ├── shared-variable.tsx │ │ │ │ │ ├── shared-with-side-effect.tsx │ │ │ │ │ ├── undefined-literals.tsx │ │ │ │ │ ├── useStateDestructure.tsx │ │ │ │ │ └── using.tsx │ │ │ │ └── solid/ │ │ │ │ └── arrow-function.tsx │ │ │ ├── code-splitter.test.ts │ │ │ ├── config/ │ │ │ │ └── withJson/ │ │ │ │ └── tsr.config.json │ │ │ ├── config.test.ts │ │ │ ├── constants.ts │ │ │ ├── delete-nodes/ │ │ │ │ ├── snapshots/ │ │ │ │ │ ├── react/ │ │ │ │ │ │ ├── 1-delete-nodes-undefined/ │ │ │ │ │ │ │ ├── createRootRoute-ssr-function.tsx │ │ │ │ │ │ │ ├── createRootRouteWithContext-ssr-function.tsx │ │ │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ │ │ └── ssr-function.tsx │ │ │ │ │ │ ├── 2-delete-nodes-empty/ │ │ │ │ │ │ │ ├── createRootRoute-ssr-function.tsx │ │ │ │ │ │ │ ├── createRootRouteWithContext-ssr-function.tsx │ │ │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ │ │ └── ssr-function.tsx │ │ │ │ │ │ └── 3-delete-nodes-ssr/ │ │ │ │ │ │ ├── createRootRoute-ssr-function.tsx │ │ │ │ │ │ ├── createRootRouteWithContext-ssr-function.tsx │ │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ │ └── ssr-function.tsx │ │ │ │ │ └── solid/ │ │ │ │ │ ├── 1-delete-nodes-undefined/ │ │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ │ └── ssr-function.tsx │ │ │ │ │ ├── 2-delete-nodes-empty/ │ │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ │ └── ssr-function.tsx │ │ │ │ │ └── 3-delete-nodes-ssr/ │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ └── ssr-function.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── react/ │ │ │ │ │ ├── createRootRoute-ssr-function.tsx │ │ │ │ │ ├── createRootRouteWithContext-ssr-function.tsx │ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ │ ├── ssr-false.tsx │ │ │ │ │ └── ssr-function.tsx │ │ │ │ └── solid/ │ │ │ │ ├── ssr-data-only.tsx │ │ │ │ ├── ssr-false.tsx │ │ │ │ └── ssr-function.tsx │ │ │ ├── delete-nodes.test.ts │ │ │ ├── detect-route-codesplit-groupings.test.ts │ │ │ ├── shared-bindings-helpers.test.ts │ │ │ └── utils.test.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-ssr-query-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-utils/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ast.ts │ │ │ ├── copy-files-plugin.ts │ │ │ ├── index.ts │ │ │ └── logger.ts │ │ ├── tests/ │ │ │ └── stripTypeExports.test.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── router-vite-plugin/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid-router/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── solid-router/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── Asset.tsx │ │ │ ├── CatchBoundary.tsx │ │ │ ├── ClientOnly.tsx │ │ │ ├── HeadContent.dev.tsx │ │ │ ├── HeadContent.tsx │ │ │ ├── Match.tsx │ │ │ ├── Matches.tsx │ │ │ ├── RouterProvider.tsx │ │ │ ├── SafeFragment.tsx │ │ │ ├── ScriptOnce.tsx │ │ │ ├── Scripts.tsx │ │ │ ├── ScrollRestoration.tsx │ │ │ ├── Transitioner.tsx │ │ │ ├── awaited.tsx │ │ │ ├── fileRoute.ts │ │ │ ├── headContentUtils.tsx │ │ │ ├── history.ts │ │ │ ├── index.dev.tsx │ │ │ ├── index.tsx │ │ │ ├── lazyRouteComponent.tsx │ │ │ ├── link.tsx │ │ │ ├── matchContext.tsx │ │ │ ├── not-found.tsx │ │ │ ├── renderRouteNotFound.tsx │ │ │ ├── route.tsx │ │ │ ├── router.ts │ │ │ ├── routerContext.tsx │ │ │ ├── routerStores.ts │ │ │ ├── scroll-restoration.tsx │ │ │ ├── ssr/ │ │ │ │ ├── RouterClient.tsx │ │ │ │ ├── RouterServer.tsx │ │ │ │ ├── client.ts │ │ │ │ ├── defaultRenderHandler.tsx │ │ │ │ ├── defaultStreamHandler.tsx │ │ │ │ ├── renderRouterToStream.tsx │ │ │ │ ├── renderRouterToString.tsx │ │ │ │ └── server.ts │ │ │ ├── typePrimitives.ts │ │ │ ├── useBlocker.tsx │ │ │ ├── useCanGoBack.ts │ │ │ ├── useLoaderData.tsx │ │ │ ├── useLoaderDeps.tsx │ │ │ ├── useLocation.tsx │ │ │ ├── useMatch.tsx │ │ │ ├── useNavigate.tsx │ │ │ ├── useParams.tsx │ │ │ ├── useRouteContext.ts │ │ │ ├── useRouter.tsx │ │ │ ├── useRouterState.tsx │ │ │ ├── useSearch.tsx │ │ │ └── utils.ts │ │ ├── tests/ │ │ │ ├── ClientOnly.test.tsx │ │ │ ├── Matches.test-d.tsx │ │ │ ├── Matches.test.tsx │ │ │ ├── RouterProvider.test-d.tsx │ │ │ ├── RouterProvider.test.tsx │ │ │ ├── Scripts.test.tsx │ │ │ ├── Transitioner.test.tsx │ │ │ ├── blocker.test.tsx │ │ │ ├── createLazyRoute.test.tsx │ │ │ ├── disableGlobalCatchBoundary.test.tsx │ │ │ ├── errorComponent.test.tsx │ │ │ ├── fileRoute.test-d.tsx │ │ │ ├── fileRoute.test.ts │ │ │ ├── index.test.tsx │ │ │ ├── lazy/ │ │ │ │ ├── heavy.tsx │ │ │ │ ├── mockHeavyDependenciesRoute.tsx │ │ │ │ └── normal.tsx │ │ │ ├── link.bench.tsx │ │ │ ├── link.test-d.tsx │ │ │ ├── link.test.tsx │ │ │ ├── loaders.test.tsx │ │ │ ├── navigate.test.tsx │ │ │ ├── not-found.test.tsx │ │ │ ├── optional-path-params.test-d.tsx │ │ │ ├── optional-path-params.test.tsx │ │ │ ├── redirect.test.tsx │ │ │ ├── redirects.test-d.tsx │ │ │ ├── route.test-d.tsx │ │ │ ├── route.test.tsx │ │ │ ├── routeApi.test-d.tsx │ │ │ ├── routeContext.test.tsx │ │ │ ├── router.test-d.tsx │ │ │ ├── router.test.tsx │ │ │ ├── searchMiddleware.test.tsx │ │ │ ├── server/ │ │ │ │ ├── ClientOnly.test.tsx │ │ │ │ ├── Transitioner.test.tsx │ │ │ │ └── errorComponent.test.tsx │ │ │ ├── setupTests.tsx │ │ │ ├── store-updates-during-navigation.test.tsx │ │ │ ├── useBlocker.test-d.tsx │ │ │ ├── useBlocker.test.tsx │ │ │ ├── useCanGoBack.test.tsx │ │ │ ├── useLoaderData.test-d.tsx │ │ │ ├── useLocation.test-d.tsx │ │ │ ├── useMatch.test-d.tsx │ │ │ ├── useMatch.test.tsx │ │ │ ├── useNavigate.test-d.tsx │ │ │ ├── useNavigate.test.tsx │ │ │ ├── useParams.test-d.tsx │ │ │ ├── useParams.test.tsx │ │ │ ├── useRouteContext.test-d.tsx │ │ │ ├── useRouterState.test-d.tsx │ │ │ ├── useSearch.test-d.tsx │ │ │ └── utils.ts │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── tsconfig.legacy.json │ │ └── vite.config.ts │ ├── solid-router-devtools/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── TanStackRouterDevtools.tsx │ │ │ ├── TanStackRouterDevtoolsPanel.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid-router-ssr-query/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid-start/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── solid-start/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── client-only.ts │ │ │ ├── client-rpc.ts │ │ │ ├── client.tsx │ │ │ ├── default-entry/ │ │ │ │ ├── client.tsx │ │ │ │ ├── server.ts │ │ │ │ └── start.ts │ │ │ ├── index.ts │ │ │ ├── plugin/ │ │ │ │ └── vite.ts │ │ │ ├── server-only.ts │ │ │ ├── server-rpc.ts │ │ │ ├── server.tsx │ │ │ ├── ssr-rpc.ts │ │ │ └── useServerFn.ts │ │ ├── tsconfig.json │ │ ├── vite.config.server-entry.ts │ │ └── vite.config.ts │ ├── solid-start-client/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StartClient.tsx │ │ │ ├── hydrateStart.ts │ │ │ ├── index.tsx │ │ │ └── tests/ │ │ │ ├── createServerFn.test-d.tsx │ │ │ └── setupTests.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── solid-start-server/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StartServer.tsx │ │ │ ├── defaultRenderHandler.tsx │ │ │ ├── defaultStreamHandler.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── start-client-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── start-core/ │ │ │ ├── SKILL.md │ │ │ ├── deployment/ │ │ │ │ └── SKILL.md │ │ │ ├── execution-model/ │ │ │ │ └── SKILL.md │ │ │ ├── middleware/ │ │ │ │ └── SKILL.md │ │ │ ├── server-functions/ │ │ │ │ └── SKILL.md │ │ │ └── server-routes/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── client/ │ │ │ │ ├── ServerFunctionSerializationAdapter.ts │ │ │ │ ├── hydrateStart.ts │ │ │ │ └── index.ts │ │ │ ├── client-rpc/ │ │ │ │ ├── createClientRpc.ts │ │ │ │ ├── frame-decoder.ts │ │ │ │ ├── index.ts │ │ │ │ └── serverFnFetcher.ts │ │ │ ├── constants.ts │ │ │ ├── createMiddleware.ts │ │ │ ├── createServerFn.ts │ │ │ ├── createStart.ts │ │ │ ├── fake-start-entry.ts │ │ │ ├── getDefaultSerovalPlugins.ts │ │ │ ├── getGlobalStartContext.ts │ │ │ ├── getRouterInstance.ts │ │ │ ├── getStartContextServerOnly.ts │ │ │ ├── getStartOptions.ts │ │ │ ├── global.ts │ │ │ ├── index.tsx │ │ │ ├── safeObjectMerge.ts │ │ │ ├── serverRoute.ts │ │ │ ├── start-entry.d.ts │ │ │ ├── startEntry.ts │ │ │ └── tests/ │ │ │ ├── createServerFn.test-d.ts │ │ │ └── createServerMiddleware.test-d.ts │ │ ├── tests/ │ │ │ └── frame-decoder.test.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── start-fn-stubs/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── createIsomorphicFn.ts │ │ │ ├── envOnly.ts │ │ │ └── index.ts │ │ ├── tests/ │ │ │ ├── createIsomorphicFn.test-d.ts │ │ │ └── envOnly.test-d.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── start-plugin-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── build-sitemap.ts │ │ │ ├── constants.ts │ │ │ ├── debug.ts │ │ │ ├── dev-server-plugin/ │ │ │ │ ├── dev-styles.ts │ │ │ │ ├── extract-html-scripts.ts │ │ │ │ └── plugin.ts │ │ │ ├── global.d.ts │ │ │ ├── import-protection-plugin/ │ │ │ │ ├── INTERNALS.md │ │ │ │ ├── ast.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── defaults.ts │ │ │ │ ├── extensionlessAbsoluteIdResolver.ts │ │ │ │ ├── matchers.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── postCompileUsage.ts │ │ │ │ ├── rewriteDeniedImports.ts │ │ │ │ ├── sourceLocation.ts │ │ │ │ ├── trace.ts │ │ │ │ ├── types.ts │ │ │ │ ├── utils.ts │ │ │ │ └── virtualModules.ts │ │ │ ├── index.ts │ │ │ ├── load-env-plugin/ │ │ │ │ └── plugin.ts │ │ │ ├── output-directory.ts │ │ │ ├── plugin.ts │ │ │ ├── post-server-build.ts │ │ │ ├── prerender.ts │ │ │ ├── preview-server-plugin/ │ │ │ │ └── plugin.ts │ │ │ ├── queue.ts │ │ │ ├── resolve-entries.ts │ │ │ ├── schema.ts │ │ │ ├── start-compiler-plugin/ │ │ │ │ ├── compiler.ts │ │ │ │ ├── handleClientOnlyJSX.ts │ │ │ │ ├── handleCreateIsomorphicFn.ts │ │ │ │ ├── handleCreateMiddleware.ts │ │ │ │ ├── handleCreateServerFn.ts │ │ │ │ ├── handleEnvOnly.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── start-manifest-plugin/ │ │ │ │ ├── manifestBuilder.ts │ │ │ │ └── plugin.ts │ │ │ ├── start-router-plugin/ │ │ │ │ ├── constants.ts │ │ │ │ ├── generator-plugins/ │ │ │ │ │ ├── prerender-routes-plugin.ts │ │ │ │ │ └── routes-manifest-plugin.ts │ │ │ │ ├── plugin.ts │ │ │ │ └── pruneServerOnlySubtrees.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── tests/ │ │ │ ├── clientOnlyJSX/ │ │ │ │ ├── clientOnlyJSX.test.ts │ │ │ │ ├── snapshots/ │ │ │ │ │ └── server/ │ │ │ │ │ ├── clientOnlyBasic.tsx │ │ │ │ │ ├── clientOnlyMultiple.tsx │ │ │ │ │ ├── clientOnlyNested.tsx │ │ │ │ │ ├── clientOnlyNoFallback.tsx │ │ │ │ │ ├── clientOnlyNotFromTanstack.tsx │ │ │ │ │ ├── clientOnlyRenamed.tsx │ │ │ │ │ └── clientOnlyWrongImportName.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── clientOnlyBasic.tsx │ │ │ │ ├── clientOnlyMultiple.tsx │ │ │ │ ├── clientOnlyNested.tsx │ │ │ │ ├── clientOnlyNoFallback.tsx │ │ │ │ ├── clientOnlyNotFromTanstack.tsx │ │ │ │ ├── clientOnlyRenamed.tsx │ │ │ │ └── clientOnlyWrongImportName.tsx │ │ │ ├── compiler.test.ts │ │ │ ├── createIsomorphicFn/ │ │ │ │ ├── createIsomorphicFn.test.ts │ │ │ │ ├── snapshots/ │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── call-at-module-level.ts │ │ │ │ │ │ ├── createIsomorphicFnDestructured.tsx │ │ │ │ │ │ ├── createIsomorphicFnDestructuredRename.tsx │ │ │ │ │ │ ├── createIsomorphicFnFactory.tsx │ │ │ │ │ │ ├── createIsomorphicFnInline.tsx │ │ │ │ │ │ └── createIsomorphicFnStarImport.tsx │ │ │ │ │ └── server/ │ │ │ │ │ ├── call-at-module-level.ts │ │ │ │ │ ├── createIsomorphicFnDestructured.tsx │ │ │ │ │ ├── createIsomorphicFnDestructuredRename.tsx │ │ │ │ │ ├── createIsomorphicFnFactory.tsx │ │ │ │ │ ├── createIsomorphicFnInline.tsx │ │ │ │ │ └── createIsomorphicFnStarImport.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── call-at-module-level.ts │ │ │ │ ├── createIsomorphicFnDestructured.tsx │ │ │ │ ├── createIsomorphicFnDestructuredRename.tsx │ │ │ │ ├── createIsomorphicFnFactory.tsx │ │ │ │ ├── createIsomorphicFnInline.tsx │ │ │ │ └── createIsomorphicFnStarImport.tsx │ │ │ ├── createMiddleware/ │ │ │ │ ├── createMiddleware.test.ts │ │ │ │ ├── snapshots/ │ │ │ │ │ └── client/ │ │ │ │ │ ├── create-function-middleware.ts │ │ │ │ │ ├── createMiddlewareDestructured.tsx │ │ │ │ │ ├── createMiddlewareDestructuredRename.tsx │ │ │ │ │ ├── createMiddlewareStarImport.tsx │ │ │ │ │ ├── createMiddlewareValidator.tsx │ │ │ │ │ ├── createStart.tsx │ │ │ │ │ └── middleware-factory.ts │ │ │ │ └── test-files/ │ │ │ │ ├── create-function-middleware.ts │ │ │ │ ├── createMiddlewareDestructured.tsx │ │ │ │ ├── createMiddlewareDestructuredRename.tsx │ │ │ │ ├── createMiddlewareStarImport.tsx │ │ │ │ ├── createMiddlewareValidator.tsx │ │ │ │ ├── createStart.tsx │ │ │ │ └── middleware-factory.ts │ │ │ ├── createServerFn/ │ │ │ │ ├── createServerFn.test.ts │ │ │ │ ├── snapshots/ │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── createServerFnDestructured.tsx │ │ │ │ │ │ ├── createServerFnDestructuredRename.tsx │ │ │ │ │ │ ├── createServerFnStarImport.tsx │ │ │ │ │ │ ├── createServerFnValidator.tsx │ │ │ │ │ │ ├── factory.tsx │ │ │ │ │ │ └── isomorphic-fns.tsx │ │ │ │ │ ├── server-caller/ │ │ │ │ │ │ ├── createServerFnDestructured.tsx │ │ │ │ │ │ ├── createServerFnDestructuredRename.tsx │ │ │ │ │ │ ├── createServerFnStarImport.tsx │ │ │ │ │ │ ├── createServerFnValidator.tsx │ │ │ │ │ │ ├── factory.tsx │ │ │ │ │ │ └── isomorphic-fns.tsx │ │ │ │ │ └── server-provider/ │ │ │ │ │ ├── createServerFnDestructured.tsx │ │ │ │ │ ├── createServerFnDestructuredRename.tsx │ │ │ │ │ ├── createServerFnStarImport.tsx │ │ │ │ │ ├── createServerFnValidator.tsx │ │ │ │ │ ├── factory.tsx │ │ │ │ │ └── isomorphic-fns.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── createServerFnDestructured.tsx │ │ │ │ ├── createServerFnDestructuredRename.tsx │ │ │ │ ├── createServerFnStarImport.tsx │ │ │ │ ├── createServerFnValidator.tsx │ │ │ │ ├── factory.tsx │ │ │ │ └── isomorphic-fns.tsx │ │ │ ├── envOnly/ │ │ │ │ ├── envOnly.test.ts │ │ │ │ ├── snapshots/ │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── envOnlyDestructured.tsx │ │ │ │ │ │ ├── envOnlyDestructuredRename.tsx │ │ │ │ │ │ ├── envOnlyFactory.tsx │ │ │ │ │ │ └── envOnlyStarImport.tsx │ │ │ │ │ └── server/ │ │ │ │ │ ├── envOnlyDestructured.tsx │ │ │ │ │ ├── envOnlyDestructuredRename.tsx │ │ │ │ │ ├── envOnlyFactory.tsx │ │ │ │ │ └── envOnlyStarImport.tsx │ │ │ │ └── test-files/ │ │ │ │ ├── envOnlyDestructured.tsx │ │ │ │ ├── envOnlyDestructuredRename.tsx │ │ │ │ ├── envOnlyFactory.tsx │ │ │ │ └── envOnlyStarImport.tsx │ │ │ ├── importProtection/ │ │ │ │ ├── defaults.test.ts │ │ │ │ ├── matchers.test.ts │ │ │ │ ├── postCompileUsage.test.ts │ │ │ │ ├── rewriteDeniedImports.test.ts │ │ │ │ ├── sourceLocation.test.ts │ │ │ │ ├── trace.test.ts │ │ │ │ ├── transform.test.ts │ │ │ │ ├── utils.test.ts │ │ │ │ └── virtualModules.test.ts │ │ │ ├── post-server-build.test.ts │ │ │ ├── prerender-ssrf.test.ts │ │ │ └── start-manifest-plugin/ │ │ │ └── manifestBuilder.test.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── start-server-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── start-server-core/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── constants.ts │ │ │ ├── createServerRpc.ts │ │ │ ├── createSsrRpc.ts │ │ │ ├── createStartHandler.ts │ │ │ ├── fake-start-server-fn-resolver.ts │ │ │ ├── frame-protocol.ts │ │ │ ├── getServerFnById.ts │ │ │ ├── global.d.ts │ │ │ ├── index.tsx │ │ │ ├── request-handler.ts │ │ │ ├── request-response.ts │ │ │ ├── router-manifest.ts │ │ │ ├── serializer/ │ │ │ │ └── ServerFunctionSerializationAdapter.ts │ │ │ ├── server-functions-handler.ts │ │ │ ├── session.ts │ │ │ ├── tanstack-start.d.ts │ │ │ ├── transformAssetUrls.ts │ │ │ └── virtual-modules.ts │ │ ├── tests/ │ │ │ ├── frame-protocol.test.ts │ │ │ └── serverRoute.test-d.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── start-static-server-functions/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── staticFunctionMiddleware.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── start-storage-context/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── async-local-storage.ts │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── valibot-adapter/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tests/ │ │ │ ├── index.test-d.ts │ │ │ └── index.test.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── virtual-file-routes/ │ │ ├── CHANGELOG.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── virtual-file-routes/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── api.ts │ │ │ ├── defineConfig.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vue-router/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── vue-router/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── Asset.tsx │ │ │ ├── Body.tsx │ │ │ ├── CatchBoundary.tsx │ │ │ ├── ClientOnly.tsx │ │ │ ├── HeadContent.dev.tsx │ │ │ ├── HeadContent.tsx │ │ │ ├── Html.tsx │ │ │ ├── Match.tsx │ │ │ ├── Matches.tsx │ │ │ ├── RouterProvider.tsx │ │ │ ├── SafeFragment.tsx │ │ │ ├── ScriptOnce.tsx │ │ │ ├── Scripts.tsx │ │ │ ├── ScrollRestoration.tsx │ │ │ ├── Transitioner.tsx │ │ │ ├── awaited.tsx │ │ │ ├── fileRoute.ts │ │ │ ├── headContentUtils.tsx │ │ │ ├── history.ts │ │ │ ├── index.dev.tsx │ │ │ ├── index.tsx │ │ │ ├── lazyRouteComponent.tsx │ │ │ ├── link.tsx │ │ │ ├── matchContext.tsx │ │ │ ├── not-found.tsx │ │ │ ├── renderRouteNotFound.tsx │ │ │ ├── route.ts │ │ │ ├── router.ts │ │ │ ├── routerContext.tsx │ │ │ ├── routerStores.ts │ │ │ ├── scroll-restoration.tsx │ │ │ ├── ssr/ │ │ │ │ ├── RouterClient.tsx │ │ │ │ ├── RouterServer.tsx │ │ │ │ ├── client.ts │ │ │ │ ├── defaultRenderHandler.tsx │ │ │ │ ├── defaultStreamHandler.tsx │ │ │ │ ├── renderRouterToStream.tsx │ │ │ │ ├── renderRouterToString.tsx │ │ │ │ └── server.ts │ │ │ ├── typePrimitives.ts │ │ │ ├── useBlocker.tsx │ │ │ ├── useCanGoBack.ts │ │ │ ├── useLoaderData.tsx │ │ │ ├── useLoaderDeps.tsx │ │ │ ├── useLocation.tsx │ │ │ ├── useMatch.tsx │ │ │ ├── useNavigate.tsx │ │ │ ├── useParams.tsx │ │ │ ├── useRouteContext.ts │ │ │ ├── useRouter.tsx │ │ │ ├── useRouterState.tsx │ │ │ ├── useSearch.tsx │ │ │ └── utils.ts │ │ ├── tests/ │ │ │ ├── ClientOnly.test.tsx │ │ │ ├── Matches.test-d.tsx │ │ │ ├── Matches.test.tsx │ │ │ ├── RouterProvider.test-d.tsx │ │ │ ├── RouterProvider.test.tsx │ │ │ ├── Scripts.test.tsx │ │ │ ├── Transitioner.test.tsx │ │ │ ├── blocker.test.tsx │ │ │ ├── createLazyRoute.test.tsx │ │ │ ├── disableGlobalCatchBoundary.test.tsx │ │ │ ├── errorComponent.test.tsx │ │ │ ├── fileRoute.test-d.tsx │ │ │ ├── fileRoute.test.ts │ │ │ ├── index.test.tsx │ │ │ ├── lazy/ │ │ │ │ ├── heavy.tsx │ │ │ │ ├── mockHeavyDependenciesRoute.tsx │ │ │ │ └── normal.tsx │ │ │ ├── link.bench.tsx │ │ │ ├── link.test-d.tsx │ │ │ ├── link.test.tsx │ │ │ ├── loaders.test.tsx │ │ │ ├── navigate.test.tsx │ │ │ ├── not-found.test.tsx │ │ │ ├── optional-path-params.test.tsx │ │ │ ├── redirect.test.tsx │ │ │ ├── redirects.test-d.tsx │ │ │ ├── route.test-d.tsx │ │ │ ├── route.test.tsx │ │ │ ├── routeApi.test-d.tsx │ │ │ ├── routeContext.test.tsx │ │ │ ├── router.test-d.tsx │ │ │ ├── router.test.tsx │ │ │ ├── searchMiddleware.test.tsx │ │ │ ├── setupTests.tsx │ │ │ ├── shellComponent.test.tsx │ │ │ ├── store-updates-during-navigation.test.tsx │ │ │ ├── useBlocker.test-d.tsx │ │ │ ├── useBlocker.test.tsx │ │ │ ├── useCanGoBack.test.tsx │ │ │ ├── useLoaderData.test-d.tsx │ │ │ ├── useLocation.test-d.tsx │ │ │ ├── useMatch.test-d.tsx │ │ │ ├── useMatch.test.tsx │ │ │ ├── useNavigate.test-d.tsx │ │ │ ├── useNavigate.test.tsx │ │ │ ├── useParams.test-d.tsx │ │ │ ├── useParams.test.tsx │ │ │ ├── useRouteContext.test-d.tsx │ │ │ ├── useRouterState.test-d.tsx │ │ │ ├── useSearch.test-d.tsx │ │ │ └── utils.ts │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── tsconfig.legacy.json │ │ └── vite.config.ts │ ├── vue-router-devtools/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── TanStackRouterDevtools.tsx │ │ │ ├── TanStackRouterDevtoolsPanel.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vue-router-ssr-query/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vue-start/ │ │ ├── CHANGELOG.md │ │ ├── bin/ │ │ │ └── intent.js │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── skills/ │ │ │ ├── _artifacts/ │ │ │ │ ├── domain_map.yaml │ │ │ │ ├── skill_spec.md │ │ │ │ └── skill_tree.yaml │ │ │ └── vue-start/ │ │ │ └── SKILL.md │ │ ├── src/ │ │ │ ├── client-only.ts │ │ │ ├── client-rpc.ts │ │ │ ├── client.tsx │ │ │ ├── default-entry/ │ │ │ │ ├── client.tsx │ │ │ │ ├── server.ts │ │ │ │ └── start.ts │ │ │ ├── index.ts │ │ │ ├── plugin/ │ │ │ │ └── vite.ts │ │ │ ├── server-only.ts │ │ │ ├── server-rpc.ts │ │ │ ├── server.tsx │ │ │ ├── ssr-rpc.ts │ │ │ └── useServerFn.ts │ │ ├── tsconfig.json │ │ ├── vite.config.server-entry.ts │ │ └── vite.config.ts │ ├── vue-start-client/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StartClient.tsx │ │ │ ├── hydrateStart.ts │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vue-start-server/ │ │ ├── CHANGELOG.md │ │ ├── eslint.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StartServer.tsx │ │ │ ├── defaultRenderHandler.tsx │ │ │ ├── defaultStreamHandler.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── zod-adapter/ │ ├── CHANGELOG.md │ ├── eslint.config.js │ ├── package.json │ ├── src/ │ │ └── index.ts │ ├── tests/ │ │ ├── index.test-d.ts │ │ └── index.test.tsx │ ├── tsconfig.json │ └── vite.config.ts ├── pnpm-workspace.yaml ├── prettier.config.js ├── scripts/ │ ├── benchmarks/ │ │ ├── bundle-size/ │ │ │ ├── measure.mjs │ │ │ └── pr-report.mjs │ │ └── common/ │ │ └── upsert-pr-comment.mjs │ ├── cleanup-empty-packages.mjs │ ├── create-github-release.mjs │ ├── generate-labeler-config.ts │ ├── llms-generate.mjs │ ├── set-ts-version.js │ ├── update-example-deps.mjs │ └── verify-links.ts ├── top-10-how-to-guides.md └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .changeset/config.json ================================================ { "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json", "changelog": [ "@svitejs/changesets-changelog-github-compact", { "repo": "TanStack/router" } ], "commit": false, "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", "fixed": [], "linked": [], "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "onlyUpdatePeerDependentsWhenOutOfRange": true } } ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto ================================================ FILE: .github/FUNDING.yml ================================================ github: tannerlinsley ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: 🐛 Bug Report description: Create a report to help us improve body: - type: markdown attributes: value: | Thank you for reporting an issue :pray:. This issue tracker is for reporting bugs found in `router` (https://github.com/tanstack/router). If you have a question about how to achieve something and are struggling, please post a question inside of `router` Discussions tab: https://github.com/tanstack/router/discussions Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already: - `router` Issues tab: https://github.com/tanstack/router/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc - `router` closed issues tab: https://github.com/tanstack/router/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed - `router` Discussions tab: https://github.com/tanstack/router/discussions The more information you fill in, the better the community can help you. - type: dropdown id: project attributes: label: Which project does this relate to? description: If you are unsure, please leave this as "Router". options: - Router - Start validations: required: true - type: textarea id: description attributes: label: Describe the bug description: Provide a clear and concise description of the challenge you are running into. validations: required: true - type: input id: link attributes: label: Your Example Website or App description: | Which website or app were you using when the bug happened? Note: - Please provide a link via our pre-configured Stackblitz project ([file-based routes](https://stackblitz.com/github/tanstack/router/tree/main/examples/react/quickstart-file-based?file=src%2Fmain.tsx)|[code-based routes](https://stackblitz.com/github/tanstack/router/tree/main/examples/react/quickstart?file=src%2Fmain.tsx)) or a link to a repo that can reproduce the issue. - Your bug will may get fixed much faster if we can run your code and it doesn't have dependencies other than the `router` npm package / dependency. - To create a shareable code example you can use Stackblitz. Please no localhost URLs. - Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve. placeholder: reproduction URL validations: required: true - type: textarea id: steps attributes: label: Steps to Reproduce the Bug or Issue description: Describe the steps we have to take to reproduce the behavior. placeholder: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea id: expected attributes: label: Expected behavior description: Provide a clear and concise description of what you expected to happen. placeholder: | As a user, I expected ___ behavior but i am seeing ___ validations: required: true - type: textarea id: screenshots_or_videos attributes: label: Screenshots or Videos description: | If applicable, add screenshots or a video to help explain your problem. For more information on the supported file image/file types and the file size limits, please refer to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files placeholder: | You can drag your video or image files inside of this editor ↓ - type: textarea id: platform attributes: label: Platform value: | - Router / Start Version: [e.g. 1.121.0] - OS: [e.g. macOS, Windows, Linux] - Browser: [e.g. Chrome, Safari, Firefox] - Browser Version: [e.g. 91.1] - Bundler: [e.g. vite] - Bundler Version: [e.g. 7.0.0] validations: required: true - type: textarea id: additional attributes: label: Additional context description: Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: 🤔 Feature Requests & Questions url: https://github.com/TanStack/router/discussions about: Please ask and answer questions here. - name: 💬 Community Chat url: https://discord.gg/mQd7egN about: A dedicated discord server hosted by TanStack - name: 🦋 TanStack Bluesky url: https://bsky.app/profile/tanstack.com about: Stay up to date with new releases of our libraries ================================================ FILE: .github/renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "configMigration": true, "extends": [ "config:recommended", "group:allNonMajor", "schedule:weekly", ":approveMajorUpdates", ":automergeMinor", ":disablePeerDependencies", ":maintainLockFilesMonthly", ":semanticCommits", ":semanticCommitTypeAll(chore)" ], "ignorePresets": [":ignoreModulesAndTests"], "labels": ["dependencies"], "rangeStrategy": "bump", "postUpdateOptions": ["pnpmDedupe"], "ignoreDeps": [ "@types/node", "@types/react", "@types/react-dom", "h3", "node", "react", "react-dom", "react-server-dom-webpack", "temp-react", "temp-react-dom", "typescript", "typescript52", "typescript53", "typescript54", "typescript55", "typescript56", "unplugin", "use-sync-external-store", "waku" ] } ================================================ FILE: .github/workflows/autofix.yml ================================================ name: autofix.ci # needed to securely identify the workflow on: pull_request: push: branches: [main, alpha, beta] concurrency: group: ${{ github.workflow }}-${{ github.event.number || github.ref }} cancel-in-progress: true permissions: contents: read jobs: autofix: name: autofix runs-on: ubuntu-latest if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }} steps: - name: Checkout uses: actions/checkout@v6.0.2 - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Fix formatting run: pnpm format - name: Generate labeler config run: pnpm labeler-generate - name: Apply fixes uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 with: commit-message: 'ci: apply automated fixes' ================================================ FILE: .github/workflows/bundle-size.yml ================================================ name: Bundle Size on: # We use `pull_request_target` to split trust boundaries across jobs: # - `benchmark-pr` checks out PR merge code and runs it as untrusted with read-only permissions. # - `comment-pr` runs trusted base-repo code with limited write access to upsert the PR comment. pull_request_target: paths: - 'packages/**' - 'benchmarks/**' push: branches: [main] paths: - 'packages/**' - 'benchmarks/**' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.number || github.ref }} cancel-in-progress: true permissions: contents: read env: NX_NO_CLOUD: true jobs: benchmark-pr: name: Benchmark PR if: github.event_name == 'pull_request_target' runs-on: ubuntu-latest outputs: current_json_b64: ${{ steps.capture.outputs.current_json_b64 }} steps: - name: Checkout uses: actions/checkout@v6.0.2 with: ref: refs/pull/${{ github.event.pull_request.number }}/merge fetch-depth: 0 persist-credentials: false - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Measure Bundle Size run: pnpm nx run @benchmarks/bundle-size:build --outputStyle=stream --skipRemoteCache - name: Capture Benchmark Outputs id: capture run: | { echo "current_json_b64=$(base64 -w 0 < benchmarks/bundle-size/results/current.json)" } >> "$GITHUB_OUTPUT" comment-pr: name: Upsert PR Comment if: github.event_name == 'pull_request_target' runs-on: ubuntu-latest needs: benchmark-pr permissions: contents: read pull-requests: write steps: - name: Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: Restore Benchmark Outputs env: CURRENT_JSON_B64: ${{ needs.benchmark-pr.outputs.current_json_b64 }} run: | mkdir -p benchmarks/bundle-size/results node -e "const fs=require('node:fs'); fs.writeFileSync('benchmarks/bundle-size/results/current.json', Buffer.from(process.env.CURRENT_JSON_B64 || '', 'base64'))" - name: Read Historical Data (if available) run: | mkdir -p benchmarks/bundle-size/results if git fetch --depth=1 origin gh-pages; then if git show origin/gh-pages:benchmarks/bundle-size/data.js > benchmarks/bundle-size/results/history-data.js 2>/dev/null; then echo "Loaded bundle-size history from gh-pages." else rm -f benchmarks/bundle-size/results/history-data.js echo "No bundle-size history found on gh-pages yet." fi fi - name: Build PR Report run: | node scripts/benchmarks/bundle-size/pr-report.mjs \ --current benchmarks/bundle-size/results/current.json \ --history benchmarks/bundle-size/results/history-data.js \ --output benchmarks/bundle-size/results/pr-comment.md \ --base-sha "${{ github.event.pull_request.base.sha }}" \ --dashboard-url "https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/benchmarks/bundle-size/" - name: Upsert Sticky PR Comment env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | node scripts/benchmarks/common/upsert-pr-comment.mjs \ --pr "${{ github.event.pull_request.number }}" \ --body-file benchmarks/bundle-size/results/pr-comment.md benchmark-main: name: Publish Bundle Size History if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'TanStack' runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Measure Bundle Size run: pnpm nx run @benchmarks/bundle-size:build --outputStyle=stream --skipRemoteCache - name: Publish Benchmark Dashboard uses: benchmark-action/github-action-benchmark@4bdcce38c94cec68da58d012ac24b7b1155efe8b # v1.20.7 with: tool: customSmallerIsBetter name: Bundle Size (gzip) output-file-path: benchmarks/bundle-size/results/benchmark-action.json github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true gh-pages-branch: gh-pages benchmark-data-dir-path: benchmarks/bundle-size max-items-in-chart: 200 summary-always: true comment-on-alert: false fail-on-alert: false ================================================ FILE: .github/workflows/check-skills.yml ================================================ # check-skills.yml — Drop this into your library repo's .github/workflows/ # # Checks for stale intent skills after a release and opens a review PR # if any skills need attention. The PR body includes a prompt you can # paste into Claude Code, Cursor, or any coding agent to update them. # # Triggers: new release published, or manual workflow_dispatch. # # Template variables (replaced by `intent setup`): # @tanstack/react-router — e.g. @tanstack/query # # Adapted for TanStack Router monorepo: loops over all packages with skills. name: Check Skills on: release: types: [published] workflow_dispatch: {} permissions: contents: write pull-requests: write jobs: check: name: Check for stale skills runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - name: Install intent CLI run: npm install -g @tanstack/intent - name: Install dependencies run: npm install --ignore-scripts env: npm_config_legacy_peer_deps: 'true' - name: Check staleness id: stale run: | # Monorepo: collect stale reports from all packages with skills ALL_OUTPUT="[" FIRST=true for dir in packages/*/; do if [ -d "$dir/skills" ]; then PKG_OUTPUT=$(cd "$dir" && npx @tanstack/intent stale --json 2>/dev/null) || true if [ -n "$PKG_OUTPUT" ] && [ "$PKG_OUTPUT" != "[]" ] && [ "$PKG_OUTPUT" != "No intent-enabled packages found." ]; then if [ "$FIRST" = true ]; then FIRST=false else ALL_OUTPUT="${ALL_OUTPUT}," fi # Strip outer brackets and append entries ENTRIES=$(echo "$PKG_OUTPUT" | node -e " const input = require('fs').readFileSync('/dev/stdin','utf8').trim(); try { const arr = JSON.parse(input); process.stdout.write(JSON.stringify(arr).slice(1, -1)); } catch {} ") ALL_OUTPUT="${ALL_OUTPUT}${ENTRIES}" fi fi done ALL_OUTPUT="${ALL_OUTPUT}]" echo "$ALL_OUTPUT" # Check if any skills need review NEEDS_REVIEW=$(echo "$ALL_OUTPUT" | node -e " const input = require('fs').readFileSync('/dev/stdin','utf8'); try { const reports = JSON.parse(input); const stale = reports.flatMap(r => r.skills.filter(s => s.needsReview).map(s => ({ library: r.library, skill: s.name, reasons: s.reasons })) ); if (stale.length > 0) { console.log(JSON.stringify(stale)); } } catch {} ") if [ -z "$NEEDS_REVIEW" ]; then echo "has_stale=false" >> "$GITHUB_OUTPUT" else echo "has_stale=true" >> "$GITHUB_OUTPUT" # Escape for multiline GH output EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) echo "stale_json<<$EOF" >> "$GITHUB_OUTPUT" echo "$NEEDS_REVIEW" >> "$GITHUB_OUTPUT" echo "$EOF" >> "$GITHUB_OUTPUT" fi - name: Build summary if: steps.stale.outputs.has_stale == 'true' id: summary run: | node -e " const stale = JSON.parse(process.env.STALE_JSON); const lines = stale.map(s => '- **' + s.skill + '** (' + s.library + '): ' + s.reasons.join(', ') ); const summary = lines.join('\n'); const prompt = [ 'Review and update the following stale intent skills for TanStack Router:', '', ...stale.map(s => '- ' + s.skill + ': ' + s.reasons.join(', ')), '', 'For each stale skill:', '1. Read the current SKILL.md file', '2. Check what changed in the library since the skill was last updated', '3. Update the skill content to reflect current APIs and behavior', '4. Run \`npx @tanstack/intent validate\` to verify the updated skill', ].join('\n'); // Write outputs const fs = require('fs'); const env = fs.readFileSync(process.env.GITHUB_OUTPUT, 'utf8'); const eof = require('crypto').randomBytes(15).toString('base64'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'summary<<' + eof + '\n' + summary + '\n' + eof + '\n' + 'prompt<<' + eof + '\n' + prompt + '\n' + eof + '\n' ); " env: STALE_JSON: ${{ steps.stale.outputs.stale_json }} - name: Open review PR if: steps.stale.outputs.has_stale == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | VERSION="${{ github.event.release.tag_name || 'manual' }}" BRANCH="skills/review-${VERSION}" git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git checkout -b "$BRANCH" git commit --allow-empty -m "chore: review stale skills for ${VERSION}" git push origin "$BRANCH" gh pr create \ --title "Review stale skills (${VERSION})" \ --body "$(cat <<'PREOF' ## Stale Skills Detected The following skills may need updates after the latest release: ${{ steps.summary.outputs.summary }} --- ### Update Prompt Paste this into your coding agent (Claude Code, Cursor, etc.): ~~~ ${{ steps.summary.outputs.prompt }} ~~~ PREOF )" \ --head "$BRANCH" \ --base main ================================================ FILE: .github/workflows/client-nav-benchmarks.yml ================================================ # Setup taken from https://codspeed.io/docs/benchmarks/nodejs/vitest name: Benchmarks on: push: branches: - 'main' paths: - 'packages/**' - 'benchmarks/**' pull_request: paths: - 'packages/**' - 'benchmarks/**' workflow_dispatch: permissions: contents: read # required for actions/checkout id-token: write # required for OIDC authentication with CodSpeed env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} SERVER_PRESET: 'node-server' NX_NO_CLOUD: true jobs: benchmarks: name: Run ${{ matrix.benchmark }}:${{ matrix.framework }} CodSpeed benchmark strategy: fail-fast: false matrix: framework: - react - solid - vue benchmark: - client-nav - ssr runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6.0.2 - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Run ${{ matrix.benchmark }}:${{ matrix.framework }} CodSpeed benchmark continue-on-error: true uses: CodSpeedHQ/action@v4 with: mode: simulation run: WITH_INSTRUMENTATION=1 pnpm nx run @benchmarks/${{ matrix.benchmark }}:test:perf:${{ matrix.framework }} ================================================ FILE: .github/workflows/labeler.yml ================================================ name: Labeler on: pull_request_target: permissions: contents: read pull-requests: write jobs: labeler: runs-on: ubuntu-latest steps: - name: Labeller uses: actions/labeler@v6.0.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} configuration-path: labeler-config.yml ================================================ FILE: .github/workflows/notify-playbooks.yml ================================================ # notify-intent.yml — Drop this into your library repo's .github/workflows/ # # Fires a repository_dispatch event to TanStack/intent whenever docs or # source files change on merge to main. This triggers the skill staleness # check workflow in the intent repo. # # Requirements: # - A fine-grained PAT with contents:write on TanStack/intent stored # as the INTENT_NOTIFY_TOKEN repository secret. # # Template variables (replaced by `intent setup`): # @tanstack/react-router — e.g. @tanstack/query # docs/** — e.g. docs/** # packages/*/src/** — e.g. packages/query-core/src/** # # Adapted for TanStack Router monorepo: watches all packages. name: Notify Intent on: push: branches: [main] paths: - 'docs/**' - 'packages/*/src/**' jobs: notify: name: Notify TanStack Intent runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 2 - name: Collect changed files id: changes run: | FILES=$(git diff --name-only HEAD~1 HEAD | jq -R -s -c 'split("\n") | map(select(length > 0))') echo "files=$FILES" >> "$GITHUB_OUTPUT" - name: Dispatch to intent repo uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.INTENT_NOTIFY_TOKEN }} repository: TanStack/intent event-type: skill-check client-payload: | { "package": "@tanstack/router", "sha": "${{ github.sha }}", "changed_files": ${{ steps.changes.outputs.files }} } ================================================ FILE: .github/workflows/pr.yml ================================================ name: PR on: pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.number || github.ref }} cancel-in-progress: true env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} SERVER_PRESET: 'node-server' permissions: contents: read pull-requests: write jobs: test: name: Test runs-on: ubuntu-latest env: TSR_TMP_DIR: ./tmp steps: - name: Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: Start Nx Agents run: npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml" - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Get base and head commits for `nx affected` uses: nrwl/nx-set-shas@v4.4.0 with: main-branch-name: main - name: Run Checks run: pnpm run test:pr --parallel=3 - name: Stop Nx Agents if: ${{ always() }} run: npx nx-cloud stop-all-agents preview: name: Preview runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Build Packages run: pnpm run build:all - name: Publish Previews run: pnpx pkg-pr-new publish --pnpm './packages/*' --template './examples/*/*' version-preview: name: Version Preview runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6.0.2 - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Changeset Preview uses: TanStack/config/.github/changeset-preview@main ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: [main, '*-pre', '*-maint'] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} SERVER_PRESET: 'node-server' permissions: contents: write id-token: write pull-requests: write jobs: release: name: Release if: "!contains(github.event.head_commit.message, 'ci: changeset release')" runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: Check for changesets id: changesets run: | CHANGESET_FILES=$(ls .changeset/*.md 2>/dev/null | grep -v README.md || true) if [ -z "$CHANGESET_FILES" ]; then echo "has_changesets=false" >> "$GITHUB_OUTPUT" else echo "has_changesets=true" >> "$GITHUB_OUTPUT" fi - name: Start Nx Agents if: steps.changesets.outputs.has_changesets == 'true' run: npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml" - name: Setup Tools uses: TanStack/config/.github/setup@main - name: Run Tests if: steps.changesets.outputs.has_changesets == 'true' run: pnpm run test:ci --parallel=3 - name: Stop Nx Agents if: ${{ always() && steps.changesets.outputs.has_changesets == 'true' }} run: npx nx-cloud stop-all-agents - name: Enter Pre-Release Mode if: "contains(github.ref_name, '-pre') && !hashFiles('.changeset/pre.json')" run: pnpm changeset pre enter pre - name: Version Packages run: pnpm run changeset:version env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Commit and Push Version Changes id: commit run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add . if git commit -m "ci: changeset release"; then git push echo "committed=true" >> "$GITHUB_OUTPUT" fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Determine dist-tag if: steps.commit.outputs.committed == 'true' id: dist-tag run: | BRANCH="${GITHUB_REF_NAME}" if [[ "$BRANCH" == *-pre ]]; then echo "prerelease=true" >> "$GITHUB_OUTPUT" elif [[ "$BRANCH" == *-maint ]]; then echo "tag=maint" >> "$GITHUB_OUTPUT" else echo "latest=true" >> "$GITHUB_OUTPUT" fi - name: Publish Packages if: steps.commit.outputs.committed == 'true' run: pnpm run changeset:publish ${{ steps.dist-tag.outputs.tag && format('--tag {0}', steps.dist-tag.outputs.tag) }} - name: Create GitHub Release if: steps.commit.outputs.committed == 'true' run: node scripts/create-github-release.mjs ${{ steps.dist-tag.outputs.prerelease == 'true' && '--prerelease' }} ${{ steps.dist-tag.outputs.latest == 'true' && '--latest' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/validate-skills.yml ================================================ # validate-skills.yml — Drop this into your library repo's .github/workflows/ # # Validates skill files on PRs that touch the skills/ directory. # Ensures frontmatter is correct, names match paths, and files stay under # the 500-line limit. name: Validate Skills on: pull_request: paths: - 'skills/**' - '**/skills/**' jobs: validate: name: Validate skill files runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - name: Install intent CLI run: npm install -g @tanstack/intent - name: Find and validate skills run: | # Find all directories containing SKILL.md files SKILLS_DIR="" if [ -d "skills" ]; then SKILLS_DIR="skills" elif [ -d "packages" ]; then # Monorepo — find skills/ under packages for dir in packages/*/skills; do if [ -d "$dir" ]; then echo "Validating $dir..." intent validate "$dir" fi done exit 0 fi if [ -n "$SKILLS_DIR" ]; then intent validate "$SKILLS_DIR" else echo "No skills/ directory found — skipping validation." fi ================================================ FILE: .gitignore ================================================ .vite # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies node_modules package-lock.json yarn.lock # builds types build */build dist .output lib es artifacts .rpt2_cache coverage *.tgz .wrangler # tests packages/router-generator/tests/**/*.gen.ts packages/router-generator/tests/**/*.gen.js **/port*.txt **/test-results/ # Playwright artifacts **/playwright-report/ **/blob-report/ # misc .DS_Store .env .env.local .env.development.local .env.test.local .env.production.local .next npm-debug.log* yarn-debug.log* yarn-error.log* .history size-plugin.json stats-hydration.json stats-react.json stats.html .vscode/settings.json *.log .DS_Store .cache .pnpm-store ts-perf /examples/*/*/yarn.lock /examples/*/*/package-lock.json .netlify nx-cloud.env .nx/cache .nx/workspace-data gpt/db.json vite.config.timestamp-* vite.config.js.timestamp-* vite.config.ts.timestamp-* vite.config.timestamp_* vite.config.js.timestamp_* vite.config.ts.timestamp_* .idea *.vitest-temp.json # Handling VSCode settings /.vscode/ !/examples/react/**/.vscode/settings.json **/llms **/.tanstack ================================================ FILE: .npmrc ================================================ provenance=true ================================================ FILE: .nvmrc ================================================ 24.8.0 ================================================ FILE: .nx/workflows/dynamic-changesets.yaml ================================================ distribute-on: small-changeset: 3 linux-medium-js medium-changeset: 6 linux-medium-js large-changeset: 10 linux-medium-js ================================================ FILE: .prettierignore ================================================ **/.next **/.nx/cache **/.svelte-kit **/build **/coverage **/dist pnpm-lock.yaml **/snapshots **/.vercel **/.output **/node_modules node_modules **/test-results **/tests/generator/file-modification/routes/(test)/* **/tests/generator/**/routeTree*.snapshot.ts /.nx/workspace-data **/src/routeTree.gen.ts ================================================ FILE: AGENTS.md ================================================ # AGENTS.md ## Project overview TanStack Router is a type-safe router with built-in caching and URL state management for React and Solid applications. This monorepo contains two main products: - **TanStack Router** - Core routing library with type-safe navigation, search params, and path params - **TanStack Start** - Full-stack framework built on top of TanStack Router ## Setup commands - Install deps: `pnpm install` - Setup e2e testing: `pnpm exec playwright install` - Build packages: `pnpm build` (affected) or `pnpm build:all` (force all) - Start dev server: `pnpm dev` - Run tests: `pnpm test` ## Code style - TypeScript strict mode with extensive type safety - Framework-agnostic core logic separated from React/Solid bindings - Type-safe routing with search params and path params - Use workspace protocol for internal dependencies (`workspace:*`) ## Dev environment tips - This is a pnpm workspace monorepo with packages organized by functionality - Nx provides caching, affected testing, targeting, and parallel execution for efficiency - Use `pnpm nx show projects` to list all available packages - Target specific packages: `pnpm nx run @tanstack/react-router:test:unit` - Target multiple packages: `pnpm nx run-many --target=test:eslint --projects=@tanstack/history,@tanstack/router-core` - Run affected tests only: `pnpm nx affected --target=test:unit` - Exclude patterns: `pnpm nx run-many --target=test:unit --exclude="examples/**,e2e/**"` - Navigate to examples and run `pnpm dev` to test changes: `cd examples/react/basic && pnpm dev` - **Granular unit testing through Nx (recommended):** - Specific files: `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx tests/Scripts.test.tsx` - Test patterns: `pnpm nx run @tanstack/react-router:test:unit -- tests/ClientOnly.test.tsx -t "should render fallback"` - Name patterns: `pnpm nx run @tanstack/react-router:test:unit -- -t "navigation"` (all tests with "navigation" in name) - Exclude patterns: `pnpm nx run @tanstack/react-router:test:unit -- --exclude="**/*link*" tests/` - List tests: `pnpm nx run @tanstack/react-router:test:unit -- list tests/link.test.tsx` (or `-- list` for all) - **Available test targets per package:** `test:unit`, `test:types`, `test:eslint`, `test:build`, `test:perf`, `build` - **Testing strategy:** Package level (nx) → File-level args via nx → Test-level args (`-t`) via nx → Pattern-level args (`--exclude`) via nx - **Agent execution guardrails (important):** - Always prefer `pnpm nx ...` over `npx nx ...`. - Prefer Nx targets over direct test runners so task dependencies (including required builds) remain in the graph. - In sandbox, run Nx with `CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipRemoteCache` - Run only one Nx command at a time. - If an Nx command shows no output for ~20 seconds, stop, run `pnpm nx reset` once, and retry once. - Do not loop retries indefinitely. If it still hangs or sandbox blocks graph/daemon behavior, request escalation immediately. ## Testing instructions - **Critical**: Always run unit and type tests during development - do not proceed if they fail - **Test types:** `pnpm test:unit`, `pnpm test:types`, `pnpm test:eslint`, `pnpm test:e2e`, `pnpm test:build` - **Full CI suite:** `pnpm test:ci` - **Fix formatting:** `pnpm format` - **Efficient targeted testing workflow:** 1. **Affected only:** `pnpm nx affected --target=test:unit` (compares to main branch) 2. **Specific packages:** `pnpm nx run @tanstack/react-router:test:unit` 3. **Specific files:** `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx` 4. **Specific patterns:** `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx -t "preloading"` - **Pro tips:** - Use `pnpm nx run @tanstack/react-router:test:unit -- list` to explore available tests before running - Use `-t "pattern"` to focus on specific functionality during development - Use `--exclude` patterns to skip unrelated tests - Keep all test filtering arguments behind `pnpm nx run ... -- ...` for maximum precision while preserving task dependencies - **Example workflow:** `pnpm nx run @tanstack/react-router:test:unit` → `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx` → `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx -t "preloading"` ## PR instructions - Always run `pnpm test:eslint`, `pnpm test:types`, and `pnpm test:unit` before committing - Test changes in relevant example apps: `cd examples/react/basic && pnpm dev` - Update corresponding documentation in `docs/` directory when adding features - Add or update tests for any code changes - Use internal docs links relative to `docs/` folder (e.g., `./guide/data-loading`) ## Package structure **Core packages:** - `packages/router-core/` - Framework-agnostic core router logic - `packages/react-router/`, `packages/solid-router/` - React/Solid bindings and components - `packages/history/` - Browser history management **Tooling:** - `packages/router-cli/` - CLI tools for code generation - `packages/router-generator/` - Route generation utilities - `packages/router-plugin/` - Universal bundler plugins (Vite, Webpack, ESBuild, Rspack) - `packages/virtual-file-routes/` - Virtual file routing system **Developer experience:** - `packages/router-devtools/`, `packages/*-router-devtools/` - Development tools - `packages/eslint-plugin-router/` - ESLint rules for router **Validation adapters:** - `packages/zod-adapter/`, `packages/valibot-adapter/`, `packages/arktype-adapter/` **Start framework:** - `packages/*-start/`, `packages/start-*/` - Full-stack framework packages **Examples & testing:** - `examples/react/`, `examples/solid/` - Example applications (test changes here) - `e2e/` - End-to-end tests (requires Playwright) - `docs/router/`, `docs/start/` - Documentation with React/Solid subdirectories **Dependencies:** Uses workspace protocol (`workspace:*`) - core → framework → start packages ## Common development tasks **Adding new routes:** - Use file-based routing in `src/routes/` directories - Or use code-based routing with route definitions - Run route generation with CLI tools **Testing changes:** - Build packages: `pnpm build` or `pnpm dev` (watch mode) - Run example apps to test functionality - Use devtools for debugging router state **Documentation updates:** - Update relevant docs in `docs/` directory - Ensure examples reflect documentation changes - Test documentation links and references - Use relative links to `docs/` folder format ## Framework-specific notes **React:** - Uses React Router components and hooks - Supports React Server Components (RSC) - Examples include React Query integration - Package: `@tanstack/react-router` **Solid:** - Uses Solid Router components and primitives - Supports Solid Start for full-stack applications - Examples include Solid Query integration - Package: `@tanstack/solid-router` ## Environment requirements - **Node.js** - Required for development - **pnpm** - Package manager (required for workspace features) - **Playwright** - Required for e2e tests (`pnpm exec playwright install`) ## Key architecture patterns - **Type Safety**: Extensive TypeScript for type-safe routing - **Framework Agnostic**: Core logic separated from framework bindings - **Plugin Architecture**: Universal bundler plugins using unplugin - **File-based Routing**: Support for both code-based and file-based routing - **Search Params**: First-class support for type-safe search parameters ## Development workflow 1. **Setup**: `pnpm install` and `pnpm exec playwright install` 2. **Build**: `pnpm build:all` or `pnpm dev` for watch mode 3. **Test**: Make changes and run relevant tests (use nx for targeted testing) 4. **Examples**: Navigate to examples and run `pnpm dev` to test changes 5. **Quality**: Run `pnpm test:eslint`, `pnpm test:types`, `pnpm test:unit` before committing ## References - **Documentation**: https://tanstack.com/router - **GitHub**: https://github.com/TanStack/router - **Discord Community**: https://discord.com/invite/WrRKjPJ ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing - Clone the repo - `gh repo clone TanStack/router` - Ensure `node` is installed - https://nodejs.org/en/ - Ensure `pnpm` is installed - https://pnpm.io/installation - Why? We use `pnpm` to manage workspace dependencies. It's easily the best monorepo/workspace experience available as of when this was written. - Install dependencies - `pnpm install` - This installs dependencies for all of the packages in the monorepo, even examples! - Dependencies inside of the packages and examples are automatically linked together as local/dynamic dependencies. - Install test dependencies - `pnpm exec playwright install` (required for e2e tests) - Run the build or dev watcher - `pnpm build:all` (build all packages) or - `pnpm build` (cached build with [nx affected](https://nx.dev/nx-api/nx/documents/affected)) or - `pnpm dev` - Navigate to an example - `cd examples/react/basic` - Run the example - `pnpm dev` - Make changes to the code - If you ran `pnpm dev` the dev watcher will automatically rebuild the code that has changed - Editing the docs locally and previewing the changes - The documentations for all the TanStack projects are hosted on [tanstack.com](https://tanstack.com), which is a TanStack Start application (https://github.com/TanStack/tanstack.com). You need to run this app locally to preview your changes in the `TanStack/router` docs. > [!NOTE] > The website fetches the doc pages from GitHub in production, and searches for them at `../router/docs` in development. Your local clone of `TanStack/router` needs to be in the same directory as the local clone of `TanStack/tanstack.com`. You can follow these steps to set up the docs for local development: 1. Make a new directory called `tanstack`. ```sh mkdir tanstack ``` 2. Enter that directory and clone the [`TanStack/router`](https://github.com/TanStack/router) and [`TanStack/tanstack.com`](https://github.com/TanStack/tanstack.com) repos. ```sh cd tanstack git clone git@github.com:TanStack/router.git # We probably don't need all the branches and commit history # from the `tanstack.com` repo, so let's just create a shallow # clone of the latest version of the `main` branch. # Read more about shallow clones here: # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/#user-content-shallow-clones git clone git@github.com:TanStack/tanstack.com.git --depth=1 --single-branch --branch=main ``` > [!NOTE] > Your `tanstack` directory should look like this: > > ``` > tanstack/ > | > +-- router/ (<-- this directory cannot be called anything else!) > | > +-- tanstack.com/ > ``` 3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode: ```sh cd tanstack.com pnpm i # The app will run on https://localhost:3000 by default pnpm dev ``` 4. Now you can visit http://localhost:3000/router/latest/docs/framework/react/overview in the browser and see the changes you make in `tanstack/router/docs` there. > [!WARNING] > You will need to update the `docs/(router or start)config.json` file (in `TanStack/router`) if you add a new documentation page! You can see the whole process in the screen capture below: https://github.com/fulopkovacs/form/assets/43729152/9d35a3c3-8153-4e74-9cb2-af275f7a269b ================================================ FILE: DEBUGGING.md ================================================ # Debugging & Testing Guide _A practical guide for debugging complex issues and running tests effectively, learned from investigating production regressions in large codebases._ ## Quick Start Debugging Checklist When you encounter a bug report or failing test: 1. **Reproduce first** - Create a minimal test case that demonstrates the exact issue 2. **Establish baseline** - Run existing tests to see what currently works/breaks 3. **Add targeted logging** - Insert debug output at key decision points 4. **Trace the data flow** - Follow the path from input to unexpected output 5. **Check recent changes** - Look for version changes mentioned in bug reports 6. **Test your hypothesis** - Make small, targeted changes and validate each step ## Essential Testing Commands ### Monorepo with Nx ```bash # Run all tests for a package npx nx test:unit @package-name # Run specific test file npx nx test:unit @package-name -- --run path/to/test.test.tsx # Run tests matching a pattern npx nx test:unit @package-name -- --run "pattern-in-test-name" # Run with verbose output npx nx test:unit @package-name -- --run --verbose ``` ### Standard npm/yarn projects ```bash # Run specific test file npm test -- --run path/to/test.test.tsx yarn test path/to/test.test.tsx # Run tests matching pattern npm test -- --grep "test pattern" ``` ### Useful test flags ```bash # Run only (don't watch for changes) --run # Show full output including console.logs --verbose # Run in specific environment --environment=jsdom ``` ## Effective Debugging Strategies ### 1. Strategic Logging ```javascript // Use distinctive prefixes for easy filtering console.log('[DEBUG useNavigate] from:', from, 'to:', to) console.log('[DEBUG router] current location:', state.location.pathname) // Log both input and output of functions console.log('[DEBUG buildLocation] input:', dest) // ... function logic ... console.log('[DEBUG buildLocation] output:', result) ``` **Pro tip:** Use `[DEBUG componentName]` prefixes so you can easily filter logs in browser dev tools. ### 2. Reproduction Test Pattern ```javascript test('should reproduce the exact issue from bug report', async () => { // Set up the exact scenario described const router = createRouter({ /* exact config from bug report */ }) // Perform the exact user actions await navigate({ to: '/initial-route' }) await navigate({ to: '.', search: { param: 'value' } }) // Assert the expected vs actual behavior expect(router.state.location.pathname).toBe('/expected') // This should fail initially, proving reproduction }) ``` ### 3. Data Flow Tracing ``` User Action → Hook Call → Router Logic → State Update → UI Update ↓ ↓ ↓ ↓ ↓ onClick() → useNavigate() → buildLocation() → setState() → re-render ``` Add logging at each step to see where the flow diverges from expectations. ## Common Pitfalls & Solutions ### React Testing Issues **Problem:** State updates not reflected in tests ```javascript // ❌ Bad - missing act() wrapper fireEvent.click(button) expect(component.state).toBe(newValue) // ✅ Good - wrapped in act() act(() => { fireEvent.click(button) }) expect(component.state).toBe(newValue) ``` **Problem:** Async operations not completing ```javascript // ❌ Bad - not waiting for async const result = await someAsyncOperation() expect(result).toBe(expected) // ✅ Good - ensuring completion await act(async () => { await someAsyncOperation() }) expect(result).toBe(expected) ``` ### React Router Specific Issues **Context vs Location confusion:** - `useMatch({ strict: false })` returns the **component's route context** - `router.state.location.pathname` returns the **current URL** - These can be different when components are rendered by parent routes ```javascript // Component rendered by parent route "/" but URL is "/child" const match = useMatch({ strict: false }) // Returns "/" context const location = router.state.location.pathname // Returns "/child" ``` ## Search & Investigation Commands ### Finding relevant code ```bash # Search for specific patterns in TypeScript/JavaScript files grep -r "navigate.*to.*\." --include="*.ts" --include="*.tsx" . # Find files related to a feature find . -name "*navigate*" -type f # Search with ripgrep (faster) rg "useNavigate" --type typescript ``` ### Git investigation ```bash # Find when a specific line was changed git blame path/to/file.ts # See recent changes to a file git log --oneline -10 path/to/file.ts # Search commit messages git log --grep="navigation" --oneline ``` ## Testing Best Practices ### Test Structure ```javascript describe('Feature', () => { beforeEach(() => { // Reset state for each test cleanup() history = createBrowserHistory() }) test('should handle specific scenario', async () => { // Arrange - set up the test conditions const router = createRouter(config) // Act - perform the action being tested await act(async () => { navigate({ to: '/target' }) }) // Assert - verify the results expect(router.state.location.pathname).toBe('/target') }) }) ``` ### Multiple Assertions ```javascript test('navigation should update both path and search', async () => { await navigate({ to: '/page', search: { q: 'test' } }) // Test multiple aspects expect(router.state.location.pathname).toBe('/page') expect(router.state.location.search).toEqual({ q: 'test' }) expect(router.state.matches).toHaveLength(2) }) ``` ## Architecture Investigation Process ### 1. Map the System ``` User Input → Component → Hook → Core Logic → State → UI ``` Identify each layer and what it's responsible for. ### 2. Find the Divergence Point Use logging to identify exactly where expected behavior diverges: ```javascript console.log('Input received:', input) // ... processing ... console.log('After step 1:', intermediate) // ... more processing ... console.log('Final output:', output) // Is this what we expected? ``` ### 3. Check Assumptions Common false assumptions: - "This hook returns the current route" (might return component context) - "State updates are synchronous" (often async in React) - "This worked before" (check if tests actually covered this case) ## Regression Investigation ### Version Comparison ```bash # Check what changed between versions git diff v1.120.13..v1.121.34 -- packages/react-router/ # Look for specific changes git log v1.120.13..v1.121.34 --oneline --grep="navigate" ``` ### Bisecting Issues ```bash # Start bisect to find breaking commit git bisect start git bisect bad HEAD git bisect good v1.120.13 # Test each commit until you find the breaking change ``` ## When to Stop & Reconsider **Stop changing code when:** - Your fix breaks multiple existing tests - You're changing fundamental assumptions - The solution feels hacky or overly complex **Consider instead:** - Adding a new API rather than changing existing behavior - Documenting the current behavior if it's actually correct - Creating a more targeted fix for the specific use case ## Advanced Debugging Techniques ### React DevTools - Inspect component tree to understand render context - Check props and state at each level - Use Profiler to identify performance issues ### Browser DevTools ```javascript // Add global debugging helpers window.debugRouter = router window.debugState = () => console.log(router.state) // Use conditional breakpoints if (router.state.location.pathname === '/problematic-route') { debugger } ``` ### Test Isolation ```javascript // Run only one test to isolate issues test.only('this specific failing test', () => { // ... }) // Skip problematic tests temporarily test.skip('temporarily disabled', () => { // ... }) ``` ## Key Takeaways 1. **Reproduction beats theory** - A failing test that demonstrates the issue is worth more than understanding the problem in theory 2. **Existing tests are protection** - If your fix breaks many existing tests, you're probably changing the wrong thing 3. **Context matters** - Especially in React, understanding where components are rendered and what context they have access to is crucial 4. **Small changes, frequent validation** - Make small, targeted changes and test each one rather than large refactors 5. **Sometimes the answer is "don't change it"** - Not every reported issue needs a code change; sometimes documentation or a new API is the right solution --- _This guide was developed while investigating a navigation regression in TanStack Router, where `navigate({ to: "." })` unexpectedly redirected to the root instead of staying on the current route._ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021-present Tanner Linsley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================
TanStack Router ## TanStack Router A modern router designed for type safety, data‑driven navigation, and seamless developer experience. - End‑to-end type safety (routes, params, loaders) - Schema‑driven search params with validation - Built‑in caching, prefetching & invalidation - Nested layouts, transitions & error boundaries ### [Read the Router Docs →](https://tanstack.com/router) TanStack Start ## TanStack Start A full‑stack framework built on Router, designed for server rendering, streaming, and production‑ready deployments. - Full‑document SSR & streaming - Server functions & end‑to‑end type safety - Deployment‑ready bundling & builds - All the power of TanStack Router, plus full‑stack features ### [Read the Start Docs →](https://tanstack.com/start)

npm downloads GitHub stars Bundle size

semantic-release Best of JS Follow @TanStack

### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
## Get Involved - We welcome issues and pull requests! - Participate in [GitHub discussions](https://github.com/TanStack/router/discussions) - Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ) - See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions ## Partners
CodeRabbit Cloudflare Netlify
Neon Clerk Convex
Sentry Prisma Strapi
Router & you?

We're looking for TanStack Router & Start Partners to join our mission! Partner with us to push the boundaries of TanStack Router & Start and build amazing things together.

LET'S CHAT
## Explore the TanStack Ecosystem - TanStack Config – Tooling for JS/TS packages - TanStack DB – Reactive sync client store - TanStack DevTools – Unified devtools panel - TanStack Form – Type‑safe form state - TanStack Pacer – Debouncing, throttling, batching
- TanStack Query – Async state & caching - TanStack Ranger – Range & slider primitives - TanStack Store – Reactive data store - TanStack Table – Headless datagrids - TanStack Virtual – Virtualized rendering … and more at TanStack.com » ================================================ FILE: _artifacts/domain_map.yaml ================================================ # domain_map.yaml # Generated by skill-domain-discovery # Library: TanStack Router # Version: 1.166.2 # Date: 2026-03-07 # Status: reviewed library: name: '@tanstack/react-router' version: '1.166.2' repository: 'https://github.com/TanStack/router' description: >- Type-safe router for React and Solid with built-in SWR caching, JSON-first search params, file-based route generation, and end-to-end type inference. primary_framework: 'React' domains: - name: 'Defining Routes' slug: 'defining-routes' description: >- Setting up route trees via file-based, code-based, or virtual file routes, configuring the router instance, and registering types for end-to-end inference. - name: 'Navigating' slug: 'navigating' description: >- Moving between routes using Links, imperative navigation, redirects, preloading, active states, and navigation blocking. - name: 'Managing URL State' slug: 'managing-url-state' description: >- Reading and writing search params and path params with validation, serialization, middlewares, and type-safe adapters. - name: 'Loading Data' slug: 'loading-data' description: >- Fetching data via route loaders, coordinating external caches, managing SWR caching, deferred loading, and error/pending states. - name: 'Protecting Routes' slug: 'protecting-routes' description: >- Authentication guards, authorization checks, RBAC patterns, and redirect-based access control via beforeLoad. - name: 'Rendering and Layout' slug: 'rendering-and-layout' description: >- Outlets, nested layouts, pathless layouts, code splitting, error boundaries, not-found handling, route masking, scroll restoration, and document head management. - name: 'Type Safety' slug: 'type-safety' description: >- Router type registration, inference patterns, narrowing with from, TypeScript performance, and avoiding unnecessary type annotations. - name: 'Server-Side Rendering' slug: 'server-side-rendering' description: >- Non-streaming and streaming SSR setup, hydration, data serialization, and isomorphic router creation. skills: # ── Defining Routes ────────────────────────────────────────────── - name: 'Route Setup' slug: 'route-setup' domain: 'defining-routes' description: >- Set up a TanStack Router project with file-based, code-based, or virtual file routes, configure the router instance, and register types. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' - '@tanstack/router-plugin' - '@tanstack/router-generator' - '@tanstack/router-cli' - '@tanstack/virtual-file-routes' - '@tanstack/eslint-plugin-router' covers: - createRouter - createRootRoute - createRootRouteWithContext - createRoute - createFileRoute - createLazyFileRoute - addChildren - routeTree.gen.ts - declare module Register - TanStackRouter bundler plugin - tsr.config.json - virtual file routes API - file naming conventions - route matching and sorting tasks: - 'Scaffold a new TanStack Router project' - 'Configure file-based routing with Vite plugin' - 'Set up code-based route tree manually' - 'Configure virtual file routes for custom conventions' - 'Register router types via module declaration' - 'Set up ESLint plugin for route property ordering' reference_candidates: - topic: 'file naming conventions' reason: '>10 distinct conventions (dot separator, $ token, _ prefix/suffix, () groups, [] escaping, index, route.tsx, __root)' failure_modes: - mistake: 'Missing router type registration' mechanism: >- Without declare module Register, top-level exports like Link, useNavigate, useParams have no type safety. Code compiles but all route paths are untyped strings. wrong_pattern: | const router = createRouter({ routeTree }) // no declare module — Link to="" has no autocomplete correct_pattern: | const router = createRouter({ routeTree }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } source: 'docs/router/guide/creating-a-router.md' priority: CRITICAL status: active skills: ['route-setup', 'type-safety'] - mistake: 'Not committing routeTree.gen.ts' mechanism: >- routeTree.gen.ts is runtime code, not a build artifact. Without it in version control, other developers cannot build or run the project. source: 'docs/router/faq.md' priority: HIGH status: active - mistake: 'Wrong route property order breaks type inference' mechanism: >- Properties like beforeLoad must come before loader in createRoute/createFileRoute calls. Incorrect order causes context types to not flow into loader. wrong_pattern: | createFileRoute('/posts')({ loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery), beforeLoad: ({ context }) => ({ queryClient: context.queryClient }), }) correct_pattern: | createFileRoute('/posts')({ beforeLoad: ({ context }) => ({ queryClient: context.queryClient }), loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery), }) source: 'docs/router/eslint/create-route-property-order.md' priority: HIGH status: active - mistake: 'Placing TanStackRouter plugin after framework plugin' mechanism: >- The TanStackRouter Vite plugin must be listed before the React/Solid plugin in the Vite config. Wrong order causes route generation to fail silently. wrong_pattern: | plugins: [react(), TanStackRouterVite()] correct_pattern: | plugins: [TanStackRouterVite(), react()] source: 'docs/router/installation/with-vite.md' priority: HIGH status: active - mistake: 'Using getParentRoute incorrectly in code-based routing' mechanism: >- Every non-root route must reference its correct parent via getParentRoute. Wrong parent breaks type inference for context, search params, and path params from ancestors. source: 'docs/router/decisions-on-dx.md' priority: HIGH status: active compositions: - library: '@tanstack/eslint-plugin-router' skill: null # ── Navigating ─────────────────────────────────────────────────── - name: 'Navigation' slug: 'navigation' domain: 'navigating' description: >- Navigate between routes using Link, useNavigate, Navigate component, and router.navigate. Configure preloading, active link states, and navigation blocking. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - Link component - useNavigate hook - Navigate component - router.navigate - ToOptions / NavigateOptions / LinkOptions - from / to relative navigation - activeOptions / activeProps / inactiveProps - preload (intent / viewport / render) - preloadDelay / defaultPreloadDelay - useBlocker hook - Block component - createLink (custom link components) - linkOptions helper - MatchRoute component - scroll restoration tasks: - 'Create type-safe links between routes' - 'Navigate programmatically after an action' - 'Set up link preloading on hover intent' - 'Style active and inactive links' - 'Block navigation for unsaved changes' - 'Create custom link components with createLink' - 'Build reusable navigation options with linkOptions' - 'Configure scroll restoration' failure_modes: - mistake: 'Interpolating params into the to string' mechanism: >- Agents build paths like to={`/posts/${id}`} instead of using the params option. Breaks type safety and param encoding. wrong_pattern: | Post correct_pattern: | Post source: 'docs/router/guide/navigation.md' priority: CRITICAL status: active skills: ['navigation', 'type-safety'] - mistake: 'Using useNavigate instead of Link for user-clickable elements' mechanism: >- Link renders a real with href, supports cmd/ctrl+click and preloading. useNavigate produces no accessible element. source: 'docs/router/guide/navigation.md' priority: MEDIUM status: active - mistake: 'Not providing from for relative navigation' mechanism: >- Without from, the router assumes navigation from root. Only absolute paths are autocompleted. Relative paths like ".." resolve from root instead of current route. wrong_pattern: | Back correct_pattern: | Back source: 'docs/router/guide/navigation.md' priority: HIGH status: active - mistake: 'Using search as object instead of function loses existing params' mechanism: >- search={{ page: 2 }} replaces all search params. Must use function form to preserve existing params. wrong_pattern: | Page 2 correct_pattern: | ({ ...prev, page: 2 })}>Page 2 source: 'docs/router/how-to/navigate-with-search-params.md' priority: HIGH status: active skills: ['navigation', 'search-params'] # ── Managing URL State ─────────────────────────────────────────── - name: 'Search Params' slug: 'search-params' domain: 'managing-url-state' description: >- Validate, read, write, and transform search params with schema validation, adapters, middlewares, and custom serialization. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' - '@tanstack/zod-adapter' - '@tanstack/valibot-adapter' - '@tanstack/arktype-adapter' covers: - validateSearch - useSearch hook - Route.useSearch - getRouteApi().useSearch - search middlewares - retainSearchParams - stripSearchParams - custom serialization (parseSearch / stringifySearch) - zod adapter / valibot adapter / arktype adapter - search param inheritance from parent routes - structural sharing for search params tasks: - 'Add validated search params to a route' - 'Read search params in components' - 'Update search params while preserving others' - 'Share search params across parent and child routes' - 'Set up search param middlewares for retention/stripping' - 'Configure custom search param serialization' - 'Handle arrays, objects, and dates in search params' subsystems: - name: 'Zod adapter' package: '@tanstack/zod-adapter' config_surface: 'zodValidator, fallback() wrapper for .catch/.default type correctness' - name: 'Valibot adapter' package: '@tanstack/valibot-adapter' config_surface: 'Standard Schema compliant, requires valibot 1.0' - name: 'ArkType adapter' package: '@tanstack/arktype-adapter' config_surface: 'Standard Schema compliant, requires arktype 2.0-rc, pass directly to validateSearch' reference_candidates: - topic: 'search param validation patterns' reason: '>10 distinct patterns (basic, optional, arrays, objects, dates, discriminated unions, transforms, conditional, cross-route inheritance, middleware chaining)' failure_modes: - mistake: 'Using zod .catch() instead of adapter fallback()' mechanism: >- Zod .catch() makes the output type unknown, losing type safety. The @tanstack/zod-adapter fallback() preserves the correct inferred type. wrong_pattern: | z.object({ page: z.number().catch(1) }) correct_pattern: | import { fallback } from '@tanstack/zod-adapter' z.object({ page: fallback(z.number(), 1) }) source: 'docs/router/guide/search-params.md' priority: HIGH status: active - mistake: 'Returning entire search object from loaderDeps' mechanism: >- loaderDeps: ({ search }) => search causes loader to re-run on ANY search param change, not just relevant ones. Deep equality comparison fires on every unrelated change. wrong_pattern: | loaderDeps: ({ search }) => search correct_pattern: | loaderDeps: ({ search }) => ({ page: search.page }) source: 'docs/router/guide/data-loading.md' priority: HIGH status: active skills: ['search-params', 'data-loading'] - mistake: 'Passing Date objects in search params' mechanism: >- Date objects serialize to [object Object] in URLs. Must convert to ISO strings for correct serialization. wrong_pattern: | correct_pattern: | source: 'docs/router/how-to/arrays-objects-dates-search-params.md' priority: HIGH status: active - mistake: 'Parent route missing validateSearch blocks inheritance' mechanism: >- Child routes cannot access shared search params unless the parent route defines validateSearch. Without it, the types and runtime values are not available. source: 'docs/router/how-to/share-search-params-across-routes.md' priority: MEDIUM status: active - name: 'Path Params' slug: 'path-params' domain: 'managing-url-state' description: >- Define and consume dynamic path segments, splat routes, optional params, and prefix/suffix patterns. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - '$paramName dynamic segments' - '$ splat / _splat' - '{-$paramName} optional params' - '{$paramName} prefix/suffix patterns' - useParams hook - params.parse / params.stringify - pathParamsAllowedCharacters tasks: - 'Define routes with dynamic path parameters' - 'Use splat routes for catch-all matching' - 'Set up optional path parameters for i18n' - 'Navigate with type-safe path params' failure_modes: - mistake: 'Interpolating path params into to string' mechanism: >- Same as navigation failure mode — agents build template literal paths instead of using params option. source: 'docs/router/guide/navigation.md' priority: CRITICAL status: active skills: ['path-params', 'navigation'] - mistake: 'Using * for splat routes instead of $' mechanism: >- TanStack Router uses $ for splat routes, not * like some other routers. The captured value is under _splat key, not * (though * works in v1 for backwards compat, removed in v2). source: 'docs/router/routing/routing-concepts.md' priority: MEDIUM status: 'fixed-but-legacy-risk' version_context: '* works in v1 for compat but will be removed in v2' - mistake: 'Using curly braces for basic dynamic segments' mechanism: >- Basic dynamic segments use $postId (no braces). Curly braces are only for prefix/suffix patterns like {$postId}.txt or optional params {-$locale}. source: 'docs/router/guide/path-params.md' priority: MEDIUM status: active # ── Loading Data ───────────────────────────────────────────────── - name: 'Data Loading' slug: 'data-loading' domain: 'loading-data' description: >- Load data via route loaders with built-in SWR caching, configure staleTime/gcTime, use loaderDeps for cache keys, handle pending and error states, and inject dependencies via router context. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - loader option - loaderDeps - staleTime / gcTime / defaultPreloadStaleTime - pendingComponent / pendingMs / pendingMinMs - errorComponent / onError / onCatch - beforeLoad - router context / createRootRouteWithContext - router.invalidate - Await component - deferred data loading tasks: - 'Add a data loader to a route' - 'Configure cache timing (staleTime, gcTime)' - 'Set up loader dependencies for search-param-driven data' - 'Show pending UI during slow loads' - 'Handle loader errors with error boundaries' - 'Inject dependencies via router context' - 'Defer non-critical data with unawaited promises' - 'Invalidate cached data after mutations' failure_modes: - mistake: 'Assuming loaders only run on the server' mechanism: >- TanStack Router is client-first. Loaders run on the client by default (and also on the server if using TanStack Start). Agents trained on Remix/Next assume loaders are server-only and put server-only code (DB queries, fs access) in them. source: 'maintainer interview' priority: CRITICAL status: active skills: ['data-loading', 'ssr'] - mistake: 'Not understanding staleTime default is 0' mechanism: >- Default staleTime is 0, meaning data is always considered stale and reloads in background on every route re-match. Agents may not set staleTime, causing excessive refetching. source: 'docs/router/guide/data-loading.md' priority: MEDIUM status: active - mistake: 'Using reset() instead of router.invalidate() in error components' mechanism: >- For loader errors, reset() only resets the error boundary but does not re-run the loader. Must use router.invalidate() to coordinate both reload and boundary reset. wrong_pattern: | function PostErrorComponent({ error, reset }) { return } correct_pattern: | function PostErrorComponent({ error, reset }) { const router = useRouter() return } source: 'docs/router/guide/data-loading.md' priority: HIGH status: active - mistake: 'Double parentheses missing on createRootRouteWithContext' mechanism: >- createRootRouteWithContext() is a factory — it returns a function. Must call it twice: createRootRouteWithContext()(routeOptions). Missing second call is a common error. wrong_pattern: | const rootRoute = createRootRouteWithContext<{ auth: AuthState }>({ component: RootComponent, }) correct_pattern: | const rootRoute = createRootRouteWithContext<{ auth: AuthState }>()({ component: RootComponent, }) source: 'docs/router/guide/data-loading.md' priority: HIGH status: active - mistake: 'Using React hooks in beforeLoad or loader' mechanism: >- beforeLoad and loader are not React components — hooks cannot be called in them. Must extract hook state in a wrapper component and pass via router context. source: 'docs/router/guide/router-context.md' priority: HIGH status: active compositions: - library: '@tanstack/react-query' skill: 'external-data-loading' - name: 'External Data Loading' slug: 'external-data-loading' domain: 'loading-data' description: >- Integrate TanStack Query (or other external caches) with Router for coordinated data loading, SSR dehydration/hydration, and streaming. type: composition packages: - '@tanstack/react-router' - '@tanstack/react-router-ssr-query' covers: - External cache coordination pattern - queryClient.ensureQueryData in loader - useSuspenseQuery in components - setupRouterSsrQueryIntegration - defaultPreloadStaleTime - dehydrate / hydrate router options - QueryClient per-request isolation - Wrap router option for providers tasks: - 'Set up TanStack Query with Router' - 'Preload query data in route loaders' - 'Configure SSR dehydration/hydration with Query' - 'Stream query data during SSR' failure_modes: - mistake: 'Not setting defaultPreloadStaleTime to 0 with external caches' mechanism: >- Router's built-in preload cache (30s default) prevents external library from controlling freshness. Set defaultPreloadStaleTime: 0 to let Query manage caching. wrong_pattern: | createRouter({ routeTree }) correct_pattern: | createRouter({ routeTree, defaultPreloadStaleTime: 0 }) source: 'docs/router/guide/external-data-loading.md' priority: HIGH status: active - mistake: 'Creating QueryClient outside createRouter for SSR' mechanism: >- For SSR, QueryClient must be created inside createRouter to ensure per-request isolation. A shared singleton leaks data between requests. wrong_pattern: | const queryClient = new QueryClient() const router = createRouter({ routeTree, context: { queryClient } }) correct_pattern: | function createAppRouter() { const queryClient = new QueryClient() return createRouter({ routeTree, context: { queryClient } }) } source: 'docs/router/guide/external-data-loading.md' priority: HIGH status: active - mistake: 'Awaiting prefetchQuery in loader blocks rendering' mechanism: >- For streaming/deferred patterns, prefetchQuery should NOT be awaited in the loader — it starts fetching on server and streams without blocking. Awaiting defeats the purpose. source: 'docs/router/integrations/query.md' priority: MEDIUM status: active # ── Protecting Routes ──────────────────────────────────────────── - name: 'Auth and Guards' slug: 'auth-and-guards' domain: 'protecting-routes' description: >- Protect routes with authentication checks, authorization guards, and RBAC patterns using beforeLoad redirects and layout routes. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - beforeLoad for auth checks - redirect() / throw redirect() - isRedirect helper - Authenticated layout routes (_authenticated) - Non-redirect auth (inline login) - RBAC with roles and permissions - Auth provider integration (Auth0, Clerk, Supabase) - Router context for auth state tasks: - 'Protect routes with authentication checks' - 'Redirect unauthenticated users to login' - 'Implement role-based access control' - 'Integrate third-party auth providers' - 'Pass auth state through router context' failure_modes: - mistake: 'Auth check in component instead of beforeLoad' mechanism: >- Component-level auth checks cause protected content to flash before redirect. beforeLoad runs before any component rendering and prevents the flash entirely. wrong_pattern: | component: () => { const auth = useAuth() if (!auth.isAuthenticated) return return } correct_pattern: | beforeLoad: ({ context }) => { if (!context.auth.isAuthenticated) { throw redirect({ to: '/login' }) } } source: 'docs/router/how-to/setup-authentication.md' priority: HIGH status: active - mistake: 'Not re-throwing redirects in try/catch' mechanism: >- redirect() works by throwing. If beforeLoad has a try/catch for error handling, the redirect gets swallowed unless re-thrown via isRedirect() check. wrong_pattern: | beforeLoad: async ({ context }) => { try { await validateSession(context.auth) } catch (e) { // swallows redirect! console.error(e) } } correct_pattern: | beforeLoad: async ({ context }) => { try { await validateSession(context.auth) } catch (e) { if (isRedirect(e)) throw e console.error(e) } } source: 'docs/router/guide/authenticated-routes.md' priority: HIGH status: active - mistake: 'Trying to conditionally render root route component' mechanism: >- Root route is always rendered regardless of auth state. Use a pathless layout route (_authenticated) with beforeLoad for conditional rendering. source: 'docs/router/faq.md' priority: MEDIUM status: active # ── Rendering and Layout ───────────────────────────────────────── - name: 'Code Splitting' slug: 'code-splitting' domain: 'rendering-and-layout' description: >- Split route code into lazy-loaded chunks using automatic code splitting, .lazy.tsx files, or code-based lazy loading. type: core packages: - '@tanstack/react-router' - '@tanstack/router-plugin' covers: - autoCodeSplitting plugin option - .lazy.tsx file convention - createLazyFileRoute - createLazyRoute - lazyRouteComponent - getRouteApi for code-split components - codeSplitGroupings per-route override - splitBehavior programmatic config - Critical vs non-critical route properties tasks: - 'Enable automatic code splitting' - 'Manually split a route with .lazy.tsx' - 'Access typed hooks from code-split components' - 'Customize which properties are split per route' failure_modes: - mistake: 'Exporting route property functions prevents code splitting' mechanism: >- Exported functions from route files are included in the main bundle even with auto code splitting. Component functions must NOT be exported. wrong_pattern: | export function PostsComponent() { return
Posts
} correct_pattern: | function PostsComponent() { return
Posts
} source: 'docs/router/guide/automatic-code-splitting.md' priority: HIGH status: active - mistake: 'Trying to code-split the root route' mechanism: >- __root.tsx does not support code splitting — it is always rendered regardless of current route. source: 'docs/router/guide/code-splitting.md' priority: MEDIUM status: active - mistake: 'Splitting the loader adds double async cost' mechanism: >- Loader is already async. Splitting it requires fetching the chunk AND then executing the loader, adding latency. Loaders should stay in the main bundle unless there is a specific reason to split them. source: 'docs/router/guide/code-splitting.md' priority: MEDIUM status: active - mistake: 'Importing Route in code-split files for typed hooks' mechanism: >- Importing the Route object in a lazy-loaded file pulls the route config into the lazy chunk, defeating code splitting. Use getRouteApi('/path') instead. wrong_pattern: | import { Route } from './posts.tsx' const data = Route.useLoaderData() correct_pattern: | import { getRouteApi } from '@tanstack/react-router' const route = getRouteApi('/posts') const data = route.useLoaderData() source: 'docs/router/guide/code-splitting.md' priority: HIGH status: active - name: 'Not Found and Errors' slug: 'not-found-and-errors' domain: 'rendering-and-layout' description: >- Handle not-found routes, missing resources, and route-level errors with notFound(), notFoundComponent, errorComponent, and CatchBoundary. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - notFound() function - notFoundComponent - defaultNotFoundComponent - notFoundMode (fuzzy / root) - errorComponent - onError / onCatch - CatchBoundary component - NotFoundRoute (deprecated) tasks: - 'Add a 404 page for unmatched routes' - 'Throw not-found for missing resources in loaders' - 'Configure error boundaries per route' - 'Target specific routes with notFound()' failure_modes: - mistake: 'Using deprecated NotFoundRoute' mechanism: >- NotFoundRoute is deprecated. When present, notFound() and notFoundComponent will NOT work. Must remove NotFoundRoute and use notFoundComponent instead. source: 'docs/router/guide/not-found-errors.md' priority: HIGH status: 'fixed-but-legacy-risk' version_context: 'NotFoundRoute deprecated, will be removed in next major' - mistake: 'Expecting useLoaderData to work in notFoundComponent' mechanism: >- When notFoundComponent renders, the route's loader may not have completed. useLoaderData() may return undefined. Use useParams, useSearch, or useRouteContext instead. source: 'docs/router/guide/not-found-errors.md' priority: MEDIUM status: active - mistake: 'Leaf route cannot handle not-found errors' mechanism: >- Routes without children never render Outlet and therefore cannot catch not-found errors. Only routes with children (or the root route) can have notFoundComponent. source: 'docs/router/guide/not-found-errors.md' priority: MEDIUM status: active - name: 'Route Masking' slug: 'route-masking' domain: 'rendering-and-layout' description: >- Mask the browser URL while navigating to a different internal route, with configurable unmask behavior. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - mask option on Link / navigate - createRouteMask - routeMasks router option - unmaskOnReload - location.maskedLocation - location.state.__tempLocation tasks: - 'Show a modal route with a clean URL' - 'Configure declarative route masks' - 'Control unmask behavior on page reload' failure_modes: - mistake: 'Expecting masked URLs to survive sharing' mechanism: >- Masking data is stored in browser history state. When a URL is shared or opened in a new tab, masking data is lost and the visible URL is used directly. source: 'docs/router/guide/route-masking.md' priority: MEDIUM status: active # ── Type Safety ────────────────────────────────────────────────── - name: 'Type Safety' slug: 'type-safety' domain: 'type-safety' description: >- Leverage TanStack Router's fully inferred type system. Avoid unnecessary type annotations, narrow routes with from, and optimize TypeScript performance. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - Full type inference philosophy - Register module declaration - from narrowing on hooks and Link - strict false for shared components - getRouteApi for code-split typed access - addChildren with object syntax for TS perf - LinkProps / ValidateLinkOptions type utilities - as const satisfies pattern tasks: - 'Register router types for global type safety' - 'Narrow route types with from parameter' - 'Use getRouteApi in code-split components' - 'Optimize TypeScript performance in large route trees' - 'Build type-safe link option utilities' failure_modes: - mistake: 'Adding type annotations or casts to inferred values' mechanism: >- TanStack Router is fully type-safe with full inference. Adding as Type, generic type parameters, satisfies, or explicit type annotations is unnecessary and can mask real type errors or break the inference chain. wrong_pattern: | const search = useSearch({ from: '/posts' }) as { page: number } correct_pattern: | const search = useSearch({ from: '/posts' }) // search.page is already typed as number source: 'maintainer interview' priority: CRITICAL status: active - mistake: 'Using un-narrowed LinkProps type' mechanism: >- LinkProps without generics is an extremely large union type. Using it as a variable type causes severe TS performance degradation. Use as const satisfies or narrow with generics. wrong_pattern: | const props: LinkProps = { to: '/posts' } correct_pattern: | const props = { to: '/posts' } as const satisfies LinkProps source: 'docs/router/guide/type-safety.md' priority: HIGH status: active - mistake: 'Not narrowing Link/useNavigate with from' mechanism: >- Without from, search resolves to a union of ALL routes search params. TypeScript check time grows linearly with route count. Always provide from to narrow. wrong_pattern: | correct_pattern: | source: 'docs/router/guide/type-safety.md' priority: HIGH status: active # ── Server-Side Rendering ──────────────────────────────────────── - name: 'SSR' slug: 'ssr' domain: 'server-side-rendering' description: >- Set up server-side rendering with non-streaming or streaming approaches, manage hydration, and handle document head. type: core packages: - '@tanstack/react-router' - '@tanstack/router-core' covers: - RouterClient / RouterServer - renderRouterToString / renderRouterToStream - createRequestHandler - defaultRenderHandler / defaultStreamHandler - HeadContent / Scripts components - head route option (meta, links, styles, scripts) - ScriptOnce component - Automatic loader dehydration/hydration - Memory history on server - Data serialization (Date, Error, FormData, undefined) tasks: - 'Set up non-streaming SSR with Express' - 'Set up streaming SSR' - 'Configure document head management' - 'Handle hydration mismatches' failure_modes: - mistake: 'Using browser APIs in loaders without environment check' mechanism: >- Loaders run on both client and server when using SSR. Browser-only APIs (window, document, localStorage) will throw on the server. source: 'docs/router/guide/ssr.md' priority: HIGH status: active skills: ['ssr', 'data-loading'] - mistake: 'Using hash fragments for server-rendered content' mechanism: >- Hash fragments are client-only — browsers never send them to the server. Using hash for conditional rendering causes hydration mismatches during SSR. source: 'docs/router/guide/navigation.md' priority: MEDIUM status: active - mistake: 'Generating Next.js or Remix SSR patterns' mechanism: >- Agents trained on Next.js generate getServerSideProps, App Router patterns, or server components. Agents trained on Remix generate server-only loader exports. TanStack Router has its own SSR API (RouterClient/RouterServer). source: 'maintainer interview' priority: CRITICAL status: active skills: ['ssr', 'route-setup', 'data-loading'] # ── Migration ──────────────────────────────────────────────────── - name: 'Migrate from React Router' slug: 'migrate-from-react-router' domain: 'defining-routes' description: >- Step-by-step migration from React Router v7 to TanStack Router, covering route definitions, navigation, search params, and data loading. type: lifecycle packages: - '@tanstack/react-router' - '@tanstack/router-plugin' covers: - Migration checklist - Route definition conversion - Link / useNavigate API differences - useSearchParams to validateSearch + useSearch - useParams with from - Outlet replacement - Loader conversion - Code splitting differences tasks: - 'Migrate routes from React Router to TanStack Router' - 'Convert React Router Links and navigation' - 'Replace useSearchParams with validated search params' - 'Convert React Router loaders' failure_modes: - mistake: 'Leaving React Router imports alongside TanStack Router' mechanism: >- React Router and TanStack Router share similar API names (Link, useNavigate, Outlet). Leftover React Router imports cause context errors ("cannot use useNavigate outside of context"). Uninstall react-router-dom to surface these. source: 'docs/router/how-to/migrate-from-react-router.md' priority: HIGH status: active - mistake: 'Using React Router useSearchParams pattern' mechanism: >- React Router uses useSearchParams() returning URLSearchParams. TanStack Router uses validateSearch schema + useSearch() returning typed objects. Agents may generate the RR pattern. wrong_pattern: | const [searchParams, setSearchParams] = useSearchParams() const page = Number(searchParams.get('page')) correct_pattern: | // In route definition: validateSearch: z.object({ page: fallback(z.number(), 1) }) // In component: const { page } = Route.useSearch() source: 'docs/router/installation/migrate-from-react-router.md' priority: HIGH status: active tensions: - name: 'Type safety strictness vs rapid prototyping' skills: ['type-safety', 'route-setup'] description: >- Full type inference requires proper setup (Register, correct property order, getParentRoute). This upfront cost conflicts with quick prototyping where developers want to skip setup. implication: >- Agents skip Register declaration or use type casts to silence errors, producing code that compiles but has no type safety. - name: 'Client-first loaders vs SSR expectations' skills: ['data-loading', 'ssr'] description: >- Loaders are client-first by design but agents assume they are server-only (Remix/Next mental model). This tension means browser APIs work in loaders by default but break under SSR. implication: >- Agents either put server-only code in client loaders or avoid browser APIs unnecessarily, depending on which framework mental model they default to. - name: 'Built-in SWR cache vs external cache coordination' skills: ['data-loading', 'external-data-loading'] description: >- Router has built-in caching with staleTime/gcTime defaults. When using an external cache like React Query, the built-in cache must be bypassed (defaultPreloadStaleTime: 0) or it conflicts with the external library's freshness management. implication: >- Agents use both caches simultaneously without disabling the built-in one, causing stale data or double-fetching. - name: 'Code splitting granularity vs loader performance' skills: ['code-splitting', 'data-loading'] description: >- Splitting more aggressively reduces initial bundle but splitting the loader adds a network round-trip before data can be fetched. The optimal split point differs per route. implication: >- Agents split everything including loaders, adding latency to data loading without understanding the tradeoff. cross_references: - from: 'route-setup' to: 'type-safety' reason: 'Register declaration and property order are prerequisites for type inference' - from: 'navigation' to: 'search-params' reason: 'Link search prop and navigate search option directly interact with search param validation' - from: 'data-loading' to: 'search-params' reason: 'loaderDeps consumes validated search params as cache keys' - from: 'data-loading' to: 'external-data-loading' reason: 'Understanding built-in caching is prerequisite for coordinating with external caches' - from: 'auth-and-guards' to: 'data-loading' reason: 'beforeLoad runs before loader — auth context flows into loader via route context' - from: 'code-splitting' to: 'data-loading' reason: 'Loader splitting decisions affect data loading performance' - from: 'ssr' to: 'data-loading' reason: 'SSR changes where loaders execute — must handle both environments' - from: 'ssr' to: 'external-data-loading' reason: 'SSR dehydration/hydration requires special setup for Query integration' - from: 'not-found-and-errors' to: 'data-loading' reason: 'notFound() thrown in loaders interacts with error boundaries and loader data availability' - from: 'migrate-from-react-router' to: 'route-setup' reason: 'Migration requires understanding TanStack Router route setup to replace RR patterns' gaps: [] ================================================ FILE: _artifacts/skill_spec.md ================================================ # TanStack Router — Skill Spec TanStack Router is a type-safe router for React and Solid applications with built-in SWR caching, JSON-first search params, file-based route generation, and end-to-end type inference. It is client-first/isomorphic — loaders run on the client by default and additionally on the server when used with TanStack Start. ## Domains | Domain | Description | Skills | | --------------------- | ------------------------------------------------------------------- | --------------------------------------------------- | | Defining Routes | Setting up route trees, configuring the router, registering types | route-setup, migrate-from-react-router | | Navigating | Links, imperative navigation, preloading, blocking | navigation | | Managing URL State | Search params and path params with validation and serialization | search-params, path-params | | Loading Data | Route loaders, SWR caching, external cache coordination, context/DI | data-loading, external-data-loading | | Protecting Routes | Auth guards, RBAC, beforeLoad redirects | auth-and-guards | | Rendering and Layout | Code splitting, error/not-found handling, route masking | code-splitting, not-found-and-errors, route-masking | | Type Safety | Inference patterns, narrowing, TS performance | type-safety | | Server-Side Rendering | SSR setup, hydration, head management | ssr | ## Skill Inventory | Skill | Type | Domain | What it covers | Failure modes | | ------------------------- | ----------- | --------------------- | ------------------------------------------------------------------------------------ | ------------- | | route-setup | core | defining-routes | createRouter, file/code/virtual routing, Register, plugin config, ESLint | 5 | | navigation | core | navigating | Link, useNavigate, Navigate, preloading, blocking, active states, scroll restoration | 4 | | search-params | core | managing-url-state | validateSearch, useSearch, middlewares, serialization, adapters | 4 | | path-params | core | managing-url-state | $params, splats, optional params, prefix/suffix patterns | 3 | | data-loading | core | loading-data | loader, loaderDeps, staleTime/gcTime, pending/error, context/DI, deferred | 5 | | external-data-loading | composition | loading-data | TanStack Query integration, SSR dehydration/hydration, streaming | 3 | | auth-and-guards | core | protecting-routes | beforeLoad redirects, layout auth, RBAC, provider integration | 3 | | code-splitting | core | rendering-and-layout | autoCodeSplitting, .lazy.tsx, getRouteApi, split groupings | 4 | | not-found-and-errors | core | rendering-and-layout | notFound(), notFoundComponent, errorComponent, CatchBoundary | 3 | | route-masking | core | rendering-and-layout | mask option, createRouteMask, unmaskOnReload | 1 | | type-safety | core | type-safety | Register, from narrowing, TS perf, type utilities | 3 | | ssr | core | server-side-rendering | RouterClient/Server, streaming, hydration, head management | 3 | | migrate-from-react-router | lifecycle | defining-routes | Migration checklist, API mapping, search params conversion | 2 | ## Failure Mode Inventory ### route-setup (5 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ------------------------------------------------------ | -------- | --------------------------------------- | ------------ | | 1 | Missing router type registration | CRITICAL | docs/guide/creating-a-router | type-safety | | 2 | Not committing routeTree.gen.ts | HIGH | docs/faq | — | | 3 | Wrong route property order breaks type inference | HIGH | docs/eslint/create-route-property-order | — | | 4 | Placing TanStackRouter plugin after framework plugin | HIGH | docs/installation/with-vite | — | | 5 | Using getParentRoute incorrectly in code-based routing | HIGH | docs/decisions-on-dx | — | ### navigation (4 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | -------------------------------------------------------- | -------- | --------------------------------------- | ------------- | | 1 | Interpolating params into the to string | CRITICAL | docs/guide/navigation | type-safety | | 2 | Using useNavigate instead of Link for clickable elements | MEDIUM | docs/guide/navigation | — | | 3 | Not providing from for relative navigation | HIGH | docs/guide/navigation | — | | 4 | Using search as object instead of function loses params | HIGH | docs/how-to/navigate-with-search-params | search-params | ### search-params (4 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ------------------------------------------------------ | -------- | -------------------------------- | ------------ | | 1 | Using zod .catch() instead of adapter fallback() | HIGH | docs/guide/search-params | — | | 2 | Returning entire search object from loaderDeps | HIGH | docs/guide/data-loading | data-loading | | 3 | Passing Date objects in search params | HIGH | docs/how-to/arrays-objects-dates | — | | 4 | Parent route missing validateSearch blocks inheritance | MEDIUM | docs/how-to/share-search-params | — | ### path-params (3 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | --------------------------------------------- | -------- | ----------------------------- | ------------ | | 1 | Interpolating path params into to string | CRITICAL | docs/guide/navigation | navigation | | 2 | Using \* for splat routes instead of $ | MEDIUM | docs/routing/routing-concepts | — | | 3 | Using curly braces for basic dynamic segments | MEDIUM | docs/guide/path-params | — | ### data-loading (5 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ---------------------------------------------------------------- | -------- | ------------------------- | ------------ | | 1 | Assuming loaders only run on the server | CRITICAL | maintainer interview | ssr | | 2 | Not understanding staleTime default is 0 | MEDIUM | docs/guide/data-loading | — | | 3 | Using reset() instead of router.invalidate() in error components | HIGH | docs/guide/data-loading | — | | 4 | Double parentheses missing on createRootRouteWithContext | HIGH | docs/guide/data-loading | — | | 5 | Using React hooks in beforeLoad or loader | HIGH | docs/guide/router-context | — | ### external-data-loading (3 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ------------------------------------------------- | -------- | -------------------------------- | ------------ | | 1 | Not setting defaultPreloadStaleTime to 0 | HIGH | docs/guide/external-data-loading | — | | 2 | Creating QueryClient outside createRouter for SSR | HIGH | docs/guide/external-data-loading | — | | 3 | Awaiting prefetchQuery in loader blocks rendering | MEDIUM | docs/integrations/query | — | ### auth-and-guards (3 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | --------------------------------------------------- | -------- | -------------------------------- | ------------ | | 1 | Auth check in component instead of beforeLoad | HIGH | docs/how-to/setup-authentication | — | | 2 | Not re-throwing redirects in try/catch | HIGH | docs/guide/authenticated-routes | — | | 3 | Trying to conditionally render root route component | MEDIUM | docs/faq | — | ### code-splitting (4 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ---------------------------------------------------------- | -------- | ----------------------------------- | ------------ | | 1 | Exporting route property functions prevents code splitting | HIGH | docs/guide/automatic-code-splitting | — | | 2 | Trying to code-split the root route | MEDIUM | docs/guide/code-splitting | — | | 3 | Splitting the loader adds double async cost | MEDIUM | docs/guide/code-splitting | — | | 4 | Importing Route in code-split files for typed hooks | HIGH | docs/guide/code-splitting | — | ### not-found-and-errors (3 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ---------------------------------------------------- | -------- | --------------------------- | ------------ | | 1 | Using deprecated NotFoundRoute | HIGH | docs/guide/not-found-errors | — | | 2 | Expecting useLoaderData to work in notFoundComponent | MEDIUM | docs/guide/not-found-errors | — | | 3 | Leaf route cannot handle not-found errors | MEDIUM | docs/guide/not-found-errors | — | ### route-masking (1 failure mode) | # | Mistake | Priority | Source | Cross-skill? | | --- | ---------------------------------------- | -------- | ------------------------ | ------------ | | 1 | Expecting masked URLs to survive sharing | MEDIUM | docs/guide/route-masking | — | ### type-safety (3 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | --------------------------------------------------- | -------- | ---------------------- | ------------ | | 1 | Adding type annotations or casts to inferred values | CRITICAL | maintainer interview | — | | 2 | Using un-narrowed LinkProps type | HIGH | docs/guide/type-safety | — | | 3 | Not narrowing Link/useNavigate with from | HIGH | docs/guide/type-safety | — | ### ssr (3 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ------------------------------------------------------- | -------- | --------------------- | ------------------------- | | 1 | Using browser APIs in loaders without environment check | HIGH | docs/guide/ssr | data-loading | | 2 | Using hash fragments for server-rendered content | MEDIUM | docs/guide/navigation | — | | 3 | Generating Next.js or Remix SSR patterns | CRITICAL | maintainer interview | route-setup, data-loading | ### migrate-from-react-router (2 failure modes) | # | Mistake | Priority | Source | Cross-skill? | | --- | ------------------------------------------------------ | -------- | ------------------------------------------- | ------------ | | 1 | Leaving React Router imports alongside TanStack Router | HIGH | docs/how-to/migrate-from-react-router | — | | 2 | Using React Router useSearchParams pattern | HIGH | docs/installation/migrate-from-react-router | — | ## Tensions | Tension | Skills | Agent implication | | ------------------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------- | | Type safety strictness vs rapid prototyping | type-safety ↔ route-setup | Agents skip Register declaration or use type casts to silence errors | | Client-first loaders vs SSR expectations | data-loading ↔ ssr | Agents put server-only code in client loaders or avoid browser APIs unnecessarily | | Built-in SWR cache vs external cache coordination | data-loading ↔ external-data-loading | Agents use both caches simultaneously causing stale data or double-fetching | | Code splitting granularity vs loader performance | code-splitting ↔ data-loading | Agents split everything including loaders, adding latency | ## Cross-References | From | To | Reason | | ------------------------- | --------------------- | ---------------------------------------------------------------------------------- | | route-setup | type-safety | Register declaration and property order are prerequisites for type inference | | navigation | search-params | Link search prop directly interacts with search param validation | | data-loading | search-params | loaderDeps consumes validated search params as cache keys | | data-loading | external-data-loading | Understanding built-in caching is prerequisite for external cache coordination | | auth-and-guards | data-loading | beforeLoad runs before loader — auth context flows into loader via route context | | code-splitting | data-loading | Loader splitting decisions affect data loading performance | | ssr | data-loading | SSR changes where loaders execute — must handle both environments | | ssr | external-data-loading | SSR dehydration/hydration requires special Query setup | | not-found-and-errors | data-loading | notFound() in loaders interacts with error boundaries and loader data availability | | migrate-from-react-router | route-setup | Migration requires understanding TanStack Router route setup | ## Subsystems & Reference Candidates | Skill | Subsystems | Reference candidates | | ------------- | --------------------------------------------- | -------------------------------------------------------- | | route-setup | — | file naming conventions (>10 distinct conventions) | | search-params | Zod adapter, Valibot adapter, ArkType adapter | search param validation patterns (>10 distinct patterns) | | All others | — | — | ## Recommended Skill File Structure - **Core skills:** route-setup, navigation, search-params, path-params, data-loading, code-splitting, not-found-and-errors, route-masking, type-safety, ssr, auth-and-guards - **Framework skills:** None needed separately — React and Solid share the same core API surface. Framework-specific notes belong inline. - **Lifecycle skills:** migrate-from-react-router - **Composition skills:** external-data-loading (TanStack Query) - **Reference files:** search-params (validation patterns), route-setup (file naming conventions) ## Composition Opportunities | Library | Integration points | Composition skill needed? | | --------------------- | --------------------------------------------------------- | ------------------------------------------ | | @tanstack/react-query | Loader coordination, SSR dehydration/hydration, streaming | Yes — external-data-loading | | Zod | Search param validation | No — covered as subsystem in search-params | | Valibot | Search param validation | No — covered as subsystem in search-params | | ArkType | Search param validation | No — covered as subsystem in search-params | ================================================ FILE: _artifacts/skill_tree.yaml ================================================ # skills/_artifacts/skill_tree.yaml library: name: '@tanstack/router' version: '1.166.2' repository: 'https://github.com/TanStack/router' description: >- Type-safe router for React and Solid with built-in SWR caching, JSON-first search params, and end-to-end type inference. generated_from: domain_map: '_artifacts/domain_map.yaml' skill_spec: '_artifacts/skill_spec.md' generated_at: '2026-03-07' skills: # ── Router Core Skills ─────────────────────────────────────────── - name: 'Router Core' slug: 'router-core' type: 'core' domain: 'defining-routes' path: 'skills/router-core/SKILL.md' package: 'packages/router-core' description: >- Framework-agnostic core concepts for TanStack Router: route trees, createRouter, createRoute, createRootRoute, createRootRouteWithContext, addChildren, Register type declaration, route matching, route sorting, file naming conventions. Entry point for all router skills. sources: - 'TanStack/router:docs/router/overview.md' - 'TanStack/router:docs/router/routing/routing-concepts.md' - 'TanStack/router:docs/router/routing/route-trees.md' - 'TanStack/router:docs/router/routing/route-matching.md' - 'TanStack/router:docs/router/guide/creating-a-router.md' - 'TanStack/router:docs/router/decisions-on-dx.md' - 'TanStack/router:packages/router-core/src' - name: 'Search Params' slug: 'router-core/search-params' type: 'sub-skill' domain: 'managing-url-state' path: 'skills/router-core/search-params/SKILL.md' package: 'packages/router-core' description: >- validateSearch, search param validation with Zod/Valibot/ArkType adapters, fallback(), search middlewares (retainSearchParams, stripSearchParams), custom serialization (parseSearch, stringifySearch), search param inheritance, loaderDeps for cache keys, reading and writing search params. requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/search-params.md' - 'TanStack/router:docs/router/how-to/setup-basic-search-params.md' - 'TanStack/router:docs/router/how-to/validate-search-params.md' - 'TanStack/router:docs/router/how-to/navigate-with-search-params.md' - 'TanStack/router:docs/router/how-to/share-search-params-across-routes.md' - 'TanStack/router:docs/router/how-to/arrays-objects-dates-search-params.md' - 'TanStack/router:docs/router/guide/custom-search-param-serialization.md' subsystems: - 'Zod adapter' - 'Valibot adapter' - 'ArkType adapter' references: - 'references/validation-patterns.md' - name: 'Path Params' slug: 'router-core/path-params' type: 'sub-skill' domain: 'managing-url-state' path: 'skills/router-core/path-params/SKILL.md' package: 'packages/router-core' description: >- Dynamic path segments ($paramName), splat routes ($ / _splat), optional params ({-$paramName}), prefix/suffix patterns ({$param}.ext), useParams, params.parse/stringify, pathParamsAllowedCharacters, i18n locale patterns. requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/path-params.md' - 'TanStack/router:docs/router/guide/internationalization-i18n.md' - 'TanStack/router:docs/router/routing/routing-concepts.md' - name: 'Navigation' slug: 'router-core/navigation' type: 'sub-skill' domain: 'navigating' path: 'skills/router-core/navigation/SKILL.md' package: 'packages/router-core' description: >- Link component, useNavigate, Navigate component, router.navigate, ToOptions/NavigateOptions/LinkOptions, from/to relative navigation, activeOptions/activeProps, preloading (intent/viewport/render), preloadDelay, navigation blocking (useBlocker, Block), createLink, linkOptions helper, scroll restoration, MatchRoute. requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/navigation.md' - 'TanStack/router:docs/router/guide/preloading.md' - 'TanStack/router:docs/router/guide/navigation-blocking.md' - 'TanStack/router:docs/router/guide/link-options.md' - 'TanStack/router:docs/router/guide/custom-link.md' - 'TanStack/router:docs/router/guide/scroll-restoration.md' - name: 'Data Loading' slug: 'router-core/data-loading' type: 'sub-skill' domain: 'loading-data' path: 'skills/router-core/data-loading/SKILL.md' package: 'packages/router-core' description: >- Route loader option, loaderDeps for cache keys, staleTime/gcTime/ defaultPreloadStaleTime SWR caching, pendingComponent/pendingMs/ pendingMinMs, errorComponent/onError/onCatch, beforeLoad, router context and createRootRouteWithContext DI pattern, router.invalidate, Await component, deferred data loading with unawaited promises. requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/data-loading.md' - 'TanStack/router:docs/router/guide/deferred-data-loading.md' - 'TanStack/router:docs/router/guide/router-context.md' - 'TanStack/router:docs/router/guide/data-mutations.md' - name: 'Auth and Guards' slug: 'router-core/auth-and-guards' type: 'sub-skill' domain: 'protecting-routes' path: 'skills/router-core/auth-and-guards/SKILL.md' package: 'packages/router-core' description: >- Route protection with beforeLoad, redirect()/throw redirect(), isRedirect helper, authenticated layout routes (_authenticated), non-redirect auth (inline login), RBAC with roles and permissions, auth provider integration (Auth0, Clerk, Supabase), router context for auth state. requires: - 'router-core' - 'router-core/data-loading' sources: - 'TanStack/router:docs/router/guide/authenticated-routes.md' - 'TanStack/router:docs/router/how-to/setup-authentication.md' - 'TanStack/router:docs/router/how-to/setup-auth-providers.md' - 'TanStack/router:docs/router/how-to/setup-rbac.md' - name: 'Code Splitting' slug: 'router-core/code-splitting' type: 'sub-skill' domain: 'rendering-and-layout' path: 'skills/router-core/code-splitting/SKILL.md' package: 'packages/router-core' description: >- Automatic code splitting (autoCodeSplitting), .lazy.tsx convention, createLazyFileRoute, createLazyRoute, lazyRouteComponent, getRouteApi for typed hooks in split files, codeSplitGroupings per-route override, splitBehavior programmatic config, critical vs non-critical properties. requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/code-splitting.md' - 'TanStack/router:docs/router/guide/automatic-code-splitting.md' - name: 'Not Found and Errors' slug: 'router-core/not-found-and-errors' type: 'sub-skill' domain: 'rendering-and-layout' path: 'skills/router-core/not-found-and-errors/SKILL.md' package: 'packages/router-core' description: >- notFound() function, notFoundComponent, defaultNotFoundComponent, notFoundMode (fuzzy/root), errorComponent, onError/onCatch, CatchBoundary, NotFoundRoute (deprecated), route masking (mask option, createRouteMask, unmaskOnReload). requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/not-found-errors.md' - 'TanStack/router:docs/router/guide/route-masking.md' - name: 'Type Safety' slug: 'router-core/type-safety' type: 'sub-skill' domain: 'type-safety' path: 'skills/router-core/type-safety/SKILL.md' package: 'packages/router-core' description: >- Full type inference philosophy (never cast, never annotate inferred values), Register module declaration, from narrowing on hooks and Link, strict:false for shared components, getRouteApi for code-split typed access, addChildren with object syntax for TS perf, LinkProps and ValidateLinkOptions type utilities, as const satisfies pattern. requires: - 'router-core' sources: - 'TanStack/router:docs/router/guide/type-safety.md' - 'TanStack/router:docs/router/guide/type-utilities.md' - 'TanStack/router:docs/router/guide/render-optimizations.md' - name: 'SSR' slug: 'router-core/ssr' type: 'sub-skill' domain: 'server-side-rendering' path: 'skills/router-core/ssr/SKILL.md' package: 'packages/router-core' description: >- Non-streaming and streaming SSR, RouterClient/RouterServer, renderRouterToString/renderRouterToStream, createRequestHandler, defaultRenderHandler/defaultStreamHandler, HeadContent/Scripts components, head route option (meta/links/styles/scripts), ScriptOnce, automatic loader dehydration/hydration, memory history on server, data serialization, document head management. requires: - 'router-core' - 'router-core/data-loading' sources: - 'TanStack/router:docs/router/guide/ssr.md' - 'TanStack/router:docs/router/guide/document-head-management.md' - 'TanStack/router:docs/router/how-to/setup-ssr.md' # ── React Router Skills ────────────────────────────────────────── - name: 'React Router' slug: 'react-router' type: 'framework' domain: 'defining-routes' path: 'skills/react-router/SKILL.md' package: 'packages/react-router' description: >- React bindings for TanStack Router: RouterProvider, useRouter, useRouterState, useMatch, useMatches, useLocation, useSearch, useParams, useNavigate, useLoaderData, useLoaderDeps, useRouteContext, useBlocker, useCanGoBack, Link, Navigate, Outlet, CatchBoundary, ErrorComponent. React-specific patterns for hooks, providers, SSR hydration, and createLink with forwardRef. requires: - 'router-core' sources: - 'TanStack/router:packages/react-router/src' - 'TanStack/router:docs/router/guide/creating-a-router.md' - 'TanStack/router:docs/router/installation/manual.md' # ── Composition Skills ─────────────────────────────────────────── - name: 'External Data Loading (TanStack Query)' slug: 'compositions/router-query' type: 'composition' domain: 'loading-data' path: 'skills/compositions/router-query/SKILL.md' package: 'packages/react-router' description: >- Integrating TanStack Router with TanStack Query: queryClient in router context, ensureQueryData/prefetchQuery in loaders, useSuspenseQuery in components, defaultPreloadStaleTime: 0, setupRouterSsrQueryIntegration for SSR dehydration/hydration and streaming, per-request QueryClient isolation. requires: - 'router-core' - 'router-core/data-loading' - 'react-router' sources: - 'TanStack/router:docs/router/guide/external-data-loading.md' - 'TanStack/router:docs/router/integrations/query.md' # ── Lifecycle Skills ───────────────────────────────────────────── - name: 'Migrate from React Router' slug: 'lifecycle/migrate-from-react-router' type: 'lifecycle' domain: 'defining-routes' path: 'skills/lifecycle/migrate-from-react-router/SKILL.md' package: 'packages/react-router' description: >- Step-by-step migration from React Router v7 to TanStack Router: route definition conversion, Link/useNavigate API differences, useSearchParams to validateSearch + useSearch, useParams with from, Outlet replacement, loader conversion, code splitting differences. requires: - 'router-core' - 'react-router' sources: - 'TanStack/router:docs/router/how-to/migrate-from-react-router.md' - 'TanStack/router:docs/router/installation/migrate-from-react-router.md' ================================================ FILE: _artifacts/start_domain_map.yaml ================================================ # domain_map.yaml # Generated by skill-domain-discovery # Library: TanStack Start # Version: 1.166.2 # Date: 2026-03-07 # Status: reviewed library: name: '@tanstack/react-start' version: '1.166.2' repository: 'https://github.com/TanStack/router' description: >- Full-stack React framework built on TanStack Router and Vite. Adds SSR, streaming, server functions (type-safe RPCs), middleware, server routes, and universal deployment. Isomorphic by default — all code runs in both environments unless explicitly constrained. primary_framework: 'React' domains: - name: 'Project Setup' slug: 'project-setup' description: >- Scaffolding a Start project, configuring Vite plugin, router setup with getRouter(), root route with document shell, client and server entry points. - name: 'Server Functions' slug: 'server-functions' description: >- Creating type-safe RPCs with createServerFn, input validation, calling from loaders/components/other server functions, error handling, streaming responses. - name: 'Middleware and Context' slug: 'middleware-and-context' description: >- Request middleware, server function middleware, context passing with sendContext, global middleware via createStart, middleware factories, fetch override precedence. - name: 'Execution Model' slug: 'execution-model' description: >- Isomorphic code execution, environment functions (createServerFn, createServerOnlyFn, createClientOnlyFn, createIsomorphicFn), import protection, dead code elimination, environment variable safety. - name: 'Server Routes' slug: 'server-routes' description: >- Server-side API endpoints defined in routes, HTTP method handlers, handler middleware, request/response patterns. - name: 'Deployment and Rendering' slug: 'deployment-and-rendering' description: >- Hosting providers (Cloudflare, Netlify, Vercel, Node/Docker), selective SSR, SPA mode, static prerendering, ISR with Cache-Control headers, SEO and head management. skills: # ── Project Setup ──────────────────────────────────────────────── - name: 'Start Setup' slug: 'start-setup' domain: 'project-setup' description: >- Scaffold a TanStack Start project, configure Vite plugin with tanstackStart(), set up router with getRouter(), create root route with document shell (HeadContent, Scripts, Outlet), configure client and server entry points. type: core packages: - '@tanstack/react-start' - '@tanstack/start-plugin-core' covers: - tanstackStart Vite plugin - getRouter() factory pattern - Root route document shell - HeadContent / Scripts / Outlet - Client entry point (optional) - Server entry point (optional) - routeTree.gen.ts - tsconfig configuration tasks: - 'Scaffold a new TanStack Start project' - 'Configure the Vite plugin' - 'Set up the router factory' - 'Customize client/server entry points' failure_modes: - mistake: 'React plugin before Start plugin in Vite config' mechanism: >- Start's Vite plugin must come before React's plugin. Wrong order causes route generation and server function compilation to fail. wrong_pattern: | plugins: [react(), tanstackStart()] correct_pattern: | plugins: [tanstackStart(), react()] source: 'docs/start/framework/react/build-from-scratch.md' priority: CRITICAL status: active - mistake: 'Enabling verbatimModuleSyntax in tsconfig' mechanism: >- verbatimModuleSyntax causes server bundles to leak into client bundles. Must be disabled. source: 'docs/start/framework/react/build-from-scratch.md' priority: HIGH status: active - mistake: 'Missing Scripts component in root route' mechanism: >- The Scripts component must be rendered in the body of the root route for proper hydration and functionality. Without it, client-side JavaScript does not load. source: 'docs/start/framework/react/guide/routing.md' priority: HIGH status: active # ── Server Functions ───────────────────────────────────────────── - name: 'Server Functions' slug: 'server-functions' domain: 'server-functions' description: >- Create type-safe RPCs with createServerFn, validate inputs with Zod or plain functions, call from loaders/components/ event handlers, handle errors/redirects/notFound, stream responses with ReadableStream or async generators. type: core packages: - '@tanstack/react-start' - '@tanstack/start-client-core' - '@tanstack/start-server-core' covers: - createServerFn (GET/POST) - inputValidator (Zod or function) - useServerFn hook - Server context utilities (getRequest, getRequestHeader, setResponseHeader, setResponseStatus) - Error handling (throw errors, redirect, notFound) - Streaming (ReadableStream, async generators) - File organization (.functions.ts, .server.ts) - FormData handling tasks: - 'Create a server function for data fetching' - 'Validate server function inputs' - 'Call server functions from components' - 'Stream data from server functions' - 'Handle errors in server functions' failure_modes: - mistake: 'Putting server-only code in loaders instead of server functions' mechanism: >- Loaders are ISOMORPHIC — they run on both client and server. Database queries, file system access, and secret API keys in loaders will either fail on the client or leak to the client bundle. Use createServerFn for server-only logic. wrong_pattern: | export const Route = createFileRoute('/posts')({ loader: async () => { const posts = await db.query('SELECT * FROM posts') return { posts } }, }) correct_pattern: | const getPosts = createServerFn({ method: 'GET' }) .handler(async () => { const posts = await db.query('SELECT * FROM posts') return { posts } }) export const Route = createFileRoute('/posts')({ loader: () => getPosts(), }) source: 'maintainer interview' priority: CRITICAL status: active skills: ['server-functions', 'execution-model'] - mistake: 'Using dynamic imports for server functions' mechanism: >- Dynamic imports of server functions can cause bundler issues. Static imports are safe — the build process replaces server implementations with RPC stubs. wrong_pattern: | const { getUser } = await import('~/utils/users.functions') correct_pattern: | import { getUser } from '~/utils/users.functions' source: 'docs/start/framework/react/guide/server-functions.md' priority: HIGH status: active - mistake: 'Generating Next.js or Remix server patterns' mechanism: >- Agents generate getServerSideProps, "use server" directives, or Remix-style loader exports. TanStack Start uses createServerFn for server-only code. wrong_pattern: | 'use server' export async function getUser() { ... } correct_pattern: | const getUser = createServerFn({ method: 'GET' }) .handler(async () => { ... }) source: 'maintainer interview' priority: CRITICAL status: active # ── Middleware and Context ─────────────────────────────────────── - name: 'Middleware' slug: 'middleware' domain: 'middleware-and-context' description: >- Request middleware and server function middleware with createMiddleware, context passing via next(), sendContext for client-server transfer, global middleware via createStart in src/start.ts, middleware factories, fetch override precedence, header merging. type: core packages: - '@tanstack/react-start' - '@tanstack/start-client-core' - '@tanstack/start-server-core' covers: - createMiddleware - Request middleware (.server only) - Server function middleware (.client + .server) - Context passing via next({ context }) - sendContext for client-server transfer - Global middleware (createStart in src/start.ts) - Middleware factories - Fetch override precedence - Header merging - Method order enforcement (middleware → inputValidator → client → server) tasks: - 'Add authentication middleware' - 'Pass context through middleware chain' - 'Configure global request middleware' - 'Create reusable middleware factories' failure_modes: - mistake: 'Trusting client context without server validation' mechanism: >- Client context via sendContext is NOT validated by default. Dynamic user-generated data must be validated in server-side middleware before use. source: 'docs/start/framework/react/guide/middleware.md' priority: HIGH status: active - mistake: 'Wrong middleware method order' mechanism: >- TypeScript enforces method order: middleware → inputValidator → client → server. Wrong order causes type errors and runtime failures. source: 'docs/start/framework/react/guide/middleware.md' priority: MEDIUM status: active - mistake: 'Confusing request vs server function middleware' mechanism: >- Request middleware runs on ALL server requests (SSR, server routes, server functions). Server function middleware runs only for server functions and has .client() method. Using the wrong type causes unexpected scope. source: 'docs/start/framework/react/guide/middleware.md' priority: MEDIUM status: active # ── Execution Model ────────────────────────────────────────────── - name: 'Execution Model' slug: 'execution-model' domain: 'execution-model' description: >- Isomorphic code execution model, environment boundary functions (createServerFn, createServerOnlyFn, createClientOnlyFn, createIsomorphicFn), import protection, dead code elimination, environment variable safety (VITE_ prefix), useHydrated hook. type: core packages: - '@tanstack/react-start' - '@tanstack/start-client-core' - '@tanstack/start-server-core' covers: - Isomorphic-by-default principle - createServerFn (RPC boundary) - createServerOnlyFn (throws on client) - createClientOnlyFn (throws on server) - createIsomorphicFn (different impl per env) - ClientOnly component - useHydrated hook - Import protection (experimental) - Environment variables (VITE_ prefix, process.env) - Dead code elimination / tree shaking tasks: - 'Protect server-only code from client bundles' - 'Use environment-specific implementations' - 'Handle environment variables safely' - 'Debug import protection violations' failure_modes: - mistake: 'Assuming loaders are server-only' mechanism: >- ALL code in TanStack Start is isomorphic by default. Loaders run on BOTH server and client. Server-only operations must use createServerFn, createServerOnlyFn, or server routes. wrong_pattern: | export const Route = createFileRoute('/dashboard')({ loader: async () => { const secret = process.env.API_SECRET return fetch(`https://api.example.com/data`, { headers: { Authorization: secret } }) }, }) correct_pattern: | const getData = createServerFn({ method: 'GET' }) .handler(async () => { const secret = process.env.API_SECRET return fetch(`https://api.example.com/data`, { headers: { Authorization: secret } }) }) export const Route = createFileRoute('/dashboard')({ loader: () => getData(), }) source: 'docs/start/framework/react/guide/execution-model.md' priority: CRITICAL status: active skills: ['execution-model', 'server-functions'] - mistake: 'Exposing secrets via module-level process.env' mechanism: >- Module-level process.env access runs in both environments. The variable value leaks into the client bundle. Access secrets only inside createServerFn or createServerOnlyFn. wrong_pattern: | const apiKey = process.env.SECRET_KEY export function fetchData() { ... } correct_pattern: | const fetchData = createServerFn({ method: 'GET' }) .handler(async () => { const apiKey = process.env.SECRET_KEY return fetch(url, { headers: { Authorization: apiKey } }) }) source: 'docs/start/framework/react/guide/execution-model.md' priority: CRITICAL status: active - mistake: 'Using VITE_ prefix for server secrets' mechanism: >- VITE_ prefixed variables are exposed to the client bundle. Server secrets must NOT have the VITE_ prefix. Access them via process.env inside server functions only. source: 'docs/start/framework/react/guide/environment-variables.md' priority: CRITICAL status: active # ── Server Routes ──────────────────────────────────────────────── - name: 'Server Routes' slug: 'server-routes' domain: 'server-routes' description: >- Define server-side API endpoints alongside app routes using the server property with HTTP method handlers, per-handler middleware via createHandlers, request/response patterns. type: core packages: - '@tanstack/react-start' - '@tanstack/start-server-core' covers: - server property on createFileRoute - handlers object (GET, POST, PUT, DELETE) - createHandlers for per-handler middleware - Handler context (request, params, context) - Request body parsing (json, text, formData) - Response helpers (Response.json) - File naming for API routes tasks: - 'Create a REST API endpoint' - 'Add middleware to server route handlers' - 'Handle different HTTP methods' failure_modes: - mistake: 'Duplicate path resolution for server routes' mechanism: >- Each route can only have a single handler file. Having both users.ts and users/index.ts causes errors. source: 'docs/start/framework/react/guide/server-routes.md' priority: MEDIUM status: active # ── Deployment and Rendering ───────────────────────────────────── - name: 'Deployment' slug: 'deployment' domain: 'deployment-and-rendering' description: >- Deploy TanStack Start to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway. Configure selective SSR, SPA mode, static prerendering, ISR with Cache-Control headers. type: core packages: - '@tanstack/react-start' - '@tanstack/start-plugin-core' covers: - Cloudflare Workers deployment - Netlify deployment - Vercel / Railway deployment - Node.js / Docker deployment - Bun deployment - Selective SSR (ssr option per route) - SPA mode configuration - Static prerendering (prerender option) - ISR with Cache-Control headers - SEO (head property, structured data) tasks: - 'Deploy to Cloudflare Workers' - 'Deploy to Netlify' - 'Configure selective SSR per route' - 'Enable SPA mode' - 'Set up static prerendering' - 'Configure ISR with cache headers' failure_modes: - mistake: 'Bun deployment with React 18' mechanism: >- Bun-specific deployment only works with React 19. For React 18, use Node.js deployment guidelines. source: 'docs/start/framework/react/guide/hosting.md' priority: MEDIUM status: active - mistake: 'Missing nodejs_compat flag for Cloudflare Workers' mechanism: >- Cloudflare Workers requires compatibility_flags: ["nodejs_compat"] in wrangler config. Without it, Node.js APIs used by Start fail at runtime. source: 'docs/start/framework/react/guide/hosting.md' priority: HIGH status: active - mistake: 'Child route loosening parent SSR config' mechanism: >- SSR config inherits from parent and can only become MORE restrictive (true → data-only → false). A child cannot set ssr: true if parent has ssr: false. source: 'docs/start/framework/react/guide/selective-ssr.md' priority: MEDIUM status: active tensions: - name: 'Isomorphic defaults vs server-only expectations' skills: ['execution-model', 'server-functions'] description: >- All code runs everywhere by default. Agents trained on Next.js/Remix assume loaders and route code are server-only. implication: >- Agents put secrets, DB queries, and file system access in loaders instead of server functions, causing client-side failures or security leaks. - name: 'Simplicity of isomorphic code vs security boundaries' skills: ['execution-model', 'middleware'] description: >- The isomorphic model makes code easy to write but requires explicit boundaries for security. Agents don't realize they need to actively constrain execution environment. implication: >- Agents expose secrets via module-level process.env or forget to validate sendContext data in middleware. cross_references: - from: 'server-functions' to: 'execution-model' reason: 'Server functions ARE the isomorphic boundary — understanding the execution model is prerequisite' - from: 'server-functions' to: 'middleware' reason: 'Server function middleware chains compose with server functions' - from: 'middleware' to: 'server-routes' reason: 'Server routes use the same middleware system' - from: 'deployment' to: 'execution-model' reason: 'Deployment target affects which environment code runs in' gaps: [] ================================================ FILE: _artifacts/start_skill_tree.yaml ================================================ # skills/_artifacts/start_skill_tree.yaml library: name: '@tanstack/react-start' version: '1.166.2' repository: 'https://github.com/TanStack/router' description: >- Full-stack React framework built on TanStack Router and Vite. Adds SSR, streaming, server functions (type-safe RPCs), middleware, server routes, and universal deployment. Isomorphic by default — all code runs in both environments unless explicitly constrained. generated_from: domain_map: '_artifacts/start_domain_map.yaml' generated_at: '2026-03-07' skills: # ── Start Core Skills ─────────────────────────────────────────── - name: 'Start Core' slug: 'start-core' type: 'core' domain: 'project-setup' path: 'skills/start-core/SKILL.md' package: 'packages/start-client-core' description: >- Core overview for TanStack Start: tanstackStart() Vite plugin, getRouter() factory, root route document shell (HeadContent, Scripts, Outlet), client/server entry points, routeTree.gen.ts, tsconfig configuration. Entry point for all Start skills. sources: - 'TanStack/router:docs/start/framework/react/build-from-scratch.md' - 'TanStack/router:docs/start/framework/react/quick-start.md' - 'TanStack/router:docs/start/framework/react/guide/routing.md' - name: 'Server Functions' slug: 'start-core/server-functions' type: 'sub-skill' domain: 'server-functions' path: 'skills/start-core/server-functions/SKILL.md' package: 'packages/start-client-core' description: >- createServerFn (GET/POST), inputValidator (Zod or function), useServerFn hook, server context utilities (getRequest, getRequestHeader, setResponseHeader, setResponseStatus), error handling (throw errors, redirect, notFound), streaming (ReadableStream, async generators), FormData handling, file organization (.functions.ts, .server.ts). requires: - 'start-core' sources: - 'TanStack/router:docs/start/framework/react/guide/server-functions.md' - name: 'Middleware' slug: 'start-core/middleware' type: 'sub-skill' domain: 'middleware-and-context' path: 'skills/start-core/middleware/SKILL.md' package: 'packages/start-client-core' description: >- createMiddleware, request middleware (.server only), server function middleware (.client + .server), context passing via next({ context }), sendContext for client-server transfer, global middleware via createStart in src/start.ts, middleware factories, method order enforcement. requires: - 'start-core' - 'start-core/server-functions' sources: - 'TanStack/router:docs/start/framework/react/guide/middleware.md' - name: 'Execution Model' slug: 'start-core/execution-model' type: 'sub-skill' domain: 'execution-model' path: 'skills/start-core/execution-model/SKILL.md' package: 'packages/start-client-core' description: >- Isomorphic-by-default principle, environment boundary functions (createServerFn, createServerOnlyFn, createClientOnlyFn, createIsomorphicFn), ClientOnly component, useHydrated hook, import protection, dead code elimination, environment variable safety (VITE_ prefix, process.env). requires: - 'start-core' sources: - 'TanStack/router:docs/start/framework/react/guide/execution-model.md' - 'TanStack/router:docs/start/framework/react/guide/environment-variables.md' - name: 'Server Routes' slug: 'start-core/server-routes' type: 'sub-skill' domain: 'server-routes' path: 'skills/start-core/server-routes/SKILL.md' package: 'packages/start-client-core' description: >- Server-side API endpoints using the server property on createFileRoute, HTTP method handlers (GET, POST, PUT, DELETE), createHandlers for per-handler middleware, handler context (request, params, context), request body parsing, response helpers, file naming for API routes. requires: - 'start-core' sources: - 'TanStack/router:docs/start/framework/react/guide/server-routes.md' - name: 'Deployment' slug: 'start-core/deployment' type: 'sub-skill' domain: 'deployment-and-rendering' path: 'skills/start-core/deployment/SKILL.md' package: 'packages/start-client-core' description: >- Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway. Selective SSR (ssr option per route), SPA mode, static prerendering, ISR with Cache-Control headers, SEO and head management. requires: - 'start-core' sources: - 'TanStack/router:docs/start/framework/react/guide/hosting.md' - 'TanStack/router:docs/start/framework/react/guide/selective-ssr.md' - 'TanStack/router:docs/start/framework/react/guide/static-prerendering.md' - 'TanStack/router:docs/start/framework/react/guide/full-stack-seo.md' # ── React Start Skills ────────────────────────────────────────── - name: 'React Start' slug: 'react-start' type: 'framework' domain: 'project-setup' path: 'skills/react-start/SKILL.md' package: 'packages/react-start' description: >- React bindings for TanStack Start: createStart, StartClient, StartServer, React-specific imports, re-exports from @tanstack/react-router, full project setup with React. requires: - 'start-core' sources: - 'TanStack/router:packages/react-start/src' - 'TanStack/router:docs/start/framework/react/build-from-scratch.md' # ── Lifecycle Skills ──────────────────────────────────────────── - name: 'Migrate from Next.js' slug: 'lifecycle/migrate-from-nextjs' type: 'lifecycle' domain: 'project-setup' path: 'skills/lifecycle/migrate-from-nextjs/SKILL.md' package: 'packages/react-start' description: >- Step-by-step migration from Next.js App Router to TanStack Start: route definition conversion, API mapping, server function conversion from Server Actions, middleware conversion, data fetching pattern changes. requires: - 'start-core' - 'react-start' sources: - 'TanStack/router:docs/start/framework/react/guide/server-functions.md' - 'TanStack/router:docs/start/framework/react/guide/middleware.md' - 'TanStack/router:docs/start/framework/react/guide/execution-model.md' ================================================ FILE: benchmarks/bundle-size/.gitignore ================================================ node_modules dist scenarios/*/src/routeTree.gen.ts results/*.json results/*.md results/*.js !results/.gitkeep ================================================ FILE: benchmarks/bundle-size/README.md ================================================ # Bundle Size Benchmarks This workspace contains deterministic bundle-size fixtures for: - `@tanstack/react-router` - `@tanstack/solid-router` - `@tanstack/vue-router` - `@tanstack/react-start` - `@tanstack/solid-start` Each package has two scenarios: - `minimal`: Small route app with `__root` + index route that renders `hello world` - `full`: Same route shape plus a broad root-level harness that imports/uses the full hooks/components surface - Start `full` scenarios also exercise `createServerFn`, `createMiddleware`, and `useServerFn` ## Design Notes - Scenarios use file-based routing as the default app style. - Router scenarios use `@tanstack/router-plugin/vite` with `autoCodeSplitting: true`. - Start scenarios use `@tanstack/-start/plugin/vite` with router code-splitting enabled. - Full-surface coverage is manually maintained (no strict export-coverage gate). - Metrics are measured from initial-load JS graph only and reported as raw/gzip/brotli bytes. - Gzip is the primary tracking signal for PR deltas and historical charting. ## Local Run ```bash pnpm nx run @benchmarks/bundle-size:build ``` This writes: - `benchmarks/bundle-size/results/current.json` - `benchmarks/bundle-size/results/benchmark-action.json` ## CI Reporting - PR workflow generates a sticky comment with: - current gzip values - baseline delta - inline sparkline trend - Pushes to `main` publish historical chart data to GitHub Pages via `benchmark-action/github-action-benchmark`. ## Manual Update Policy When router/start public hooks/components evolve, update the corresponding `*-full/src/routes/__root.tsx` harness to keep full scenarios representative. ## Backfill Readiness The measurement script supports optional interfaces for historical backfilling: - `--sha` - `--measured-at` - `--append-history` These are intended for one-off scripts that replay historical commits and append results to the same history dataset shape used for chart generation. If `--append-history` points at a `data.js` file, output is written as `window.BENCHMARK_DATA = ...` for direct GitHub Pages compatibility. ================================================ FILE: benchmarks/bundle-size/package.json ================================================ { "name": "@benchmarks/bundle-size", "private": true, "type": "module", "scripts": { "build": "node ../../scripts/benchmarks/bundle-size/measure.mjs" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/solid-router": "workspace:^", "@tanstack/vue-router": "workspace:^", "@tanstack/react-start": "workspace:^", "@tanstack/solid-start": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "solid-js": "^1.9.10", "vue": "^3.5.16" }, "devDependencies": { "@tanstack/router-plugin": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "^5.7.2", "vite": "^8.0.0", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: benchmarks/bundle-size/results/.gitkeep ================================================ ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-full/index.html ================================================ react-router-full
================================================ FILE: benchmarks/bundle-size/scenarios/react-router-full/src/main.tsx ================================================ import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree, scrollRestoration: true, }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement) { throw new Error('Root element `#app` not found') } if (!rootElement.innerHTML) { ReactDOM.createRoot(rootElement).render() } ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-full/src/routes/__root.tsx ================================================ import { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, HeadContent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScriptOnce, Scripts, ScrollRestoration, createLink, createRootRoute, linkOptions, useAwaited, useBlocker, useCanGoBack, useElementScrollRestoration, useHydrated, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useChildMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags, } from '@tanstack/react-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { const router = useRouter() const hydrated = useHydrated() const awaited = useAwaited({ promise: Promise.resolve('ready') }) const linkProps = useLinkProps({ to: '/' } as any) const matchRoute = useMatchRoute() const matches = useMatches() const parentMatches = useParentMatches() const childMatches = useChildMatches() const match = useMatch({ strict: false, shouldThrow: false } as any) const loaderDeps = useLoaderDeps({ strict: false } as any) const loaderData = useLoaderData({ strict: false } as any) const params = useParams({ strict: false } as any) const search = useSearch({ strict: false } as any) const routeContext = useRouteContext({ strict: false } as any) const routerState = useRouterState({ select: (state) => state.status } as any) const location = useLocation() const canGoBack = useCanGoBack() const navigate = useNavigate() const scrollEntry = useElementScrollRestoration({ id: 'root-scroll' }) const tags = useTags() useBlocker({ shouldBlockFn: () => false, disabled: true, withResolver: false, }) const linkFactoryResult = linkOptions({ to: '/' } as any) const routeMatchResult = matchRoute({ to: '/' } as any) const SvgLink = createLink('svg') const hooksAndComponents = [ useAwaited, useHydrated, useLinkProps, useMatchRoute, useMatches, useParentMatches, useChildMatches, useMatch, useLoaderDeps, useLoaderData, useBlocker, useNavigate, useParams, useSearch, useRouteContext, useRouter, useRouterState, useLocation, useCanGoBack, useElementScrollRestoration, useTags, Await, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScrollRestoration, Block, ScriptOnce, Asset, HeadContent, Scripts, ] ;(globalThis as any).__TANSTACK_BUNDLE_SIZE_KEEP__ = { hooksAndComponents, } void awaited void linkFactoryResult void matches void parentMatches void childMatches void match void loaderDeps void loaderData void params void search void routeContext void routerState void location void canGoBack void navigate void scrollEntry void tags void routeMatchResult return ( <> {'window.__tsr_bundle_size = true'} home {() => } }> {() => } false} disabled withResolver={false}> {() => } }>
hello world
) } ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-full/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-full/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true, }), react(), ], }) ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-minimal/index.html ================================================ react-router-minimal
================================================ FILE: benchmarks/bundle-size/scenarios/react-router-minimal/src/main.tsx ================================================ import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement) { throw new Error('Root element `#app` not found') } if (!rootElement.innerHTML) { ReactDOM.createRoot(rootElement).render() } ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-minimal/src/routes/__root.tsx ================================================ import { Outlet, createRootRoute } from '@tanstack/react-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return } ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-minimal/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/react-router-minimal/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true, }), react(), ], }) ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-full/src/router.tsx ================================================ import { createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, scrollRestoration: true, }) } ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-full/src/routes/__root.tsx ================================================ import { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, HeadContent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScriptOnce, Scripts, ScrollRestoration, createLink, createRootRoute, linkOptions, useAwaited, useBlocker, useCanGoBack, useElementScrollRestoration, useHydrated, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useChildMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags, } from '@tanstack/react-router' import { createMiddleware, createServerFn, useServerFn, } from '@tanstack/react-start' const requestMiddleware = createMiddleware().server(async ({ next }) => { return next() }) const functionMiddleware = createMiddleware({ type: 'function' }) .client(async ({ next }) => { return next() }) .server(async ({ next }) => { return next() }) const helloServerFn = createServerFn({ method: 'GET' }) .middleware([requestMiddleware, functionMiddleware]) .handler(async () => { return 'hello from server fn' }) export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { const router = useRouter() const hydrated = useHydrated() const awaited = useAwaited({ promise: Promise.resolve('ready') }) const linkProps = useLinkProps({ to: '/' } as any) const matchRoute = useMatchRoute() const matches = useMatches() const parentMatches = useParentMatches() const childMatches = useChildMatches() const match = useMatch({ strict: false, shouldThrow: false } as any) const loaderDeps = useLoaderDeps({ strict: false } as any) const loaderData = useLoaderData({ strict: false } as any) const params = useParams({ strict: false } as any) const search = useSearch({ strict: false } as any) const routeContext = useRouteContext({ strict: false } as any) const routerState = useRouterState({ select: (state) => state.status } as any) const location = useLocation() const canGoBack = useCanGoBack() const navigate = useNavigate() const scrollEntry = useElementScrollRestoration({ id: 'root-scroll' }) const tags = useTags() const invokeServerFn = useServerFn(helloServerFn) useBlocker({ shouldBlockFn: () => false, disabled: true, withResolver: false, }) const linkFactoryResult = linkOptions({ to: '/' } as any) const routeMatchResult = matchRoute({ to: '/' } as any) const SvgLink = createLink('svg') const startSurface = [createMiddleware, createServerFn, useServerFn] const hooksAndComponents = [ useAwaited, useHydrated, useLinkProps, useMatchRoute, useMatches, useParentMatches, useChildMatches, useMatch, useLoaderDeps, useLoaderData, useBlocker, useNavigate, useParams, useSearch, useRouteContext, useRouter, useRouterState, useLocation, useCanGoBack, useElementScrollRestoration, useTags, Await, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScrollRestoration, Block, ScriptOnce, Asset, HeadContent, Scripts, ] ;(globalThis as any).__TANSTACK_BUNDLE_SIZE_KEEP__ = { hooksAndComponents, startSurface, } void awaited void linkFactoryResult void matches void parentMatches void childMatches void match void loaderDeps void loaderData void params void search void routeContext void routerState void location void canGoBack void navigate void scrollEntry void tags void routeMatchResult void invokeServerFn return ( {'window.__tsr_bundle_size = true'} home {() => } }> {() => } false} disabled withResolver={false}> {() => } }>
hello world
) } ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-full/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-full/vite.config.ts ================================================ import { defineConfig } from 'vite' import viteReact from '@vitejs/plugin-react' import { tanstackStart } from '@tanstack/react-start/plugin/vite' export default defineConfig({ plugins: [tanstackStart(), viteReact()], }) ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-minimal/src/router.tsx ================================================ import { createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, scrollRestoration: true, }) } ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-minimal/src/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/react-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-minimal/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/react-start-minimal/vite.config.ts ================================================ import { defineConfig } from 'vite' import viteReact from '@vitejs/plugin-react' import { tanstackStart } from '@tanstack/react-start/plugin/vite' export default defineConfig({ plugins: [tanstackStart(), viteReact()], }) ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-full/index.html ================================================ solid-router-full
================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-full/src/main.tsx ================================================ import { render } from 'solid-js/web' import { RouterProvider, createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree, scrollRestoration: true, }) declare module '@tanstack/solid-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement) { throw new Error('Root element `#app` not found') } if (!rootElement.innerHTML) { render(() => , rootElement) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-full/src/routes/__root.tsx ================================================ import { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, HeadContent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScriptOnce, Scripts, ScrollRestoration, createLink, createRootRoute, linkOptions, useAwaited, useBlocker, useCanGoBack, useChildMatches, useElementScrollRestoration, useHydrated, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags, } from '@tanstack/solid-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { const router = useRouter() const hydrated = useHydrated() const [awaited] = useAwaited({ promise: Promise.resolve('ready') }) const linkProps = useLinkProps({ to: '/' } as any) const matchRoute = useMatchRoute() const matches = useMatches() const parentMatches = useParentMatches() const childMatches = useChildMatches() const match = useMatch({ strict: false, shouldThrow: false } as any) const loaderDeps = useLoaderDeps({ strict: false } as any) const loaderData = useLoaderData({ strict: false } as any) const params = useParams({ strict: false } as any) const search = useSearch({ strict: false } as any) const routeContext = useRouteContext({ strict: false } as any) const routerState = useRouterState({ select: (state) => state.status } as any) const location = useLocation() const canGoBack = useCanGoBack() const navigate = useNavigate() const scrollEntry = useElementScrollRestoration({ id: 'root-scroll' }) const tags = useTags() useBlocker({ shouldBlockFn: () => false, disabled: true, withResolver: false, }) const linkFactoryResult = linkOptions({ to: '/' } as any) const routeMatchResult = matchRoute({ to: '/' } as any) const SvgLink = createLink('svg') const hooksAndComponents = [ useAwaited, useHydrated, useLinkProps, useMatchRoute, useMatches, useParentMatches, useChildMatches, useMatch, useLoaderDeps, useLoaderData, useBlocker, useNavigate, useParams, useSearch, useRouteContext, useRouter, useRouterState, useLocation, useCanGoBack, useElementScrollRestoration, useTags, Await, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScrollRestoration, Block, ScriptOnce, Asset, HeadContent, Scripts, ] ;(globalThis as any).__TANSTACK_BUNDLE_SIZE_KEEP__ = { hooksAndComponents, } void awaited void linkFactoryResult void matches() void parentMatches() void childMatches() void match() void loaderDeps() void loaderData() void params() void search() void routeContext() void routerState() void location() void canGoBack() void navigate void scrollEntry void tags() void routeMatchResult() return ( <> {'window.__tsr_bundle_size = true'} home {() => } }> {() => } false} disabled withResolver={false}> {() => } }> {() => }
hello world
) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-full/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-full/vite.config.ts ================================================ import { defineConfig } from 'vite' import solid from 'vite-plugin-solid' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'solid', autoCodeSplitting: true, }), solid(), ], }) ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-minimal/index.html ================================================ solid-router-minimal
================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-minimal/src/main.tsx ================================================ import { render } from 'solid-js/web' import { RouterProvider, createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree }) declare module '@tanstack/solid-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement) { throw new Error('Root element `#app` not found') } if (!rootElement.innerHTML) { render(() => , rootElement) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-minimal/src/routes/__root.tsx ================================================ import { Outlet, createRootRoute } from '@tanstack/solid-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-minimal/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/solid-router-minimal/vite.config.ts ================================================ import { defineConfig } from 'vite' import solid from 'vite-plugin-solid' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'solid', autoCodeSplitting: true, }), solid(), ], }) ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-full/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, scrollRestoration: true, }) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-full/src/routes/__root.tsx ================================================ import { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, HeadContent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScriptOnce, Scripts, ScrollRestoration, createLink, createRootRoute, linkOptions, useAwaited, useBlocker, useCanGoBack, useChildMatches, useElementScrollRestoration, useHydrated, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags, } from '@tanstack/solid-router' import { createMiddleware, createServerFn, useServerFn, } from '@tanstack/solid-start' type BundleSizeKeep = { hooksAndComponents: ReadonlyArray startSurface: ReadonlyArray } declare global { var __TANSTACK_BUNDLE_SIZE_KEEP__: BundleSizeKeep | undefined } const requestMiddleware = createMiddleware().server(async ({ next }) => { return next() }) const functionMiddleware = createMiddleware({ type: 'function' }) .client(async ({ next }) => { return next() }) .server(async ({ next }) => { return next() }) const helloServerFn = createServerFn({ method: 'GET' }) .middleware([requestMiddleware, functionMiddleware]) .handler(async () => { return 'hello from server fn' }) export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { const router = useRouter() const hydrated = useHydrated() const [awaited] = useAwaited({ promise: Promise.resolve('ready') }) const linkProps = useLinkProps({ to: '/' }) const matchRoute = useMatchRoute() const matches = useMatches() const parentMatches = useParentMatches() const childMatches = useChildMatches() const match = useMatch({ strict: false, shouldThrow: false }) const loaderDeps = useLoaderDeps({ strict: false }) const loaderData = useLoaderData({ strict: false }) const params = useParams({ strict: false }) const search = useSearch({ strict: false }) const routeContext = useRouteContext({ strict: false }) const routerState = useRouterState({ select: (state) => state.status }) const location = useLocation() const canGoBack = useCanGoBack() const navigate = useNavigate() const scrollEntry = useElementScrollRestoration({ id: 'root-scroll' }) const tags = useTags() const invokeServerFn = useServerFn(helloServerFn) useBlocker({ shouldBlockFn: () => false, disabled: true, withResolver: false, }) const linkFactoryResult = linkOptions({ to: '/' }) const routeMatchResult = matchRoute({ to: '/' }) const SvgLink = createLink('svg') const startSurface = [createMiddleware, createServerFn, useServerFn] const hooksAndComponents = [ useAwaited, useHydrated, useLinkProps, useMatchRoute, useMatches, useParentMatches, useChildMatches, useMatch, useLoaderDeps, useLoaderData, useBlocker, useNavigate, useParams, useSearch, useRouteContext, useRouter, useRouterState, useLocation, useCanGoBack, useElementScrollRestoration, useTags, Await, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScrollRestoration, Block, ScriptOnce, Asset, HeadContent, Scripts, ] globalThis.__TANSTACK_BUNDLE_SIZE_KEEP__ = { hooksAndComponents, startSurface, } void awaited void linkFactoryResult void matches() void parentMatches() void childMatches() void match() void loaderDeps() void loaderData() void params() void search() void routeContext() void routerState() void location() void canGoBack() void navigate void scrollEntry void tags() void routeMatchResult() void invokeServerFn return ( {'window.__tsr_bundle_size = true'} home {() => } }> {() => } false} disabled withResolver={false}> {() => } }> {() => }
hello world
) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-full/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-full/vite.config.ts ================================================ import { defineConfig } from 'vite' import solid from 'vite-plugin-solid' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' export default defineConfig({ plugins: [tanstackStart(), solid({ ssr: true })], }) ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-minimal/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, scrollRestoration: true, }) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-minimal/src/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-minimal/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/solid-start-minimal/vite.config.ts ================================================ import { defineConfig } from 'vite' import solid from 'vite-plugin-solid' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' export default defineConfig({ plugins: [tanstackStart(), solid({ ssr: true })], }) ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-full/index.html ================================================ vue-router-full
================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-full/src/main.tsx ================================================ import { createApp } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree, scrollRestoration: true, }) declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement) { throw new Error('Root element `#app` not found') } if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-full/src/routes/__root.tsx ================================================ import { Asset, Await, Block, Body, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, HeadContent, Html, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScriptOnce, Scripts, ScrollRestoration, createLink, createRootRoute, linkOptions, useAwaited, useBlocker, useCanGoBack, useChildMatches, useElementScrollRestoration, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags, } from '@tanstack/vue-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { const router = useRouter() const [awaited] = useAwaited({ promise: Promise.resolve('ready') }) const linkProps = useLinkProps({ to: '/' } as any) const matchRoute = useMatchRoute() const matches = useMatches() const parentMatches = useParentMatches() const childMatches = useChildMatches() const match = useMatch({ strict: false, shouldThrow: false } as any) const loaderDeps = useLoaderDeps({ strict: false } as any) const loaderData = useLoaderData({ strict: false } as any) const params = useParams({ strict: false } as any) const search = useSearch({ strict: false } as any) const routeContext = useRouteContext({ strict: false } as any) const routerState = useRouterState({ select: (state) => state.status } as any) const location = useLocation() const canGoBack = useCanGoBack() const navigate = useNavigate() const scrollEntry = useElementScrollRestoration({ id: 'root-scroll' }) const tags = useTags() useBlocker({ shouldBlockFn: () => false, disabled: true, withResolver: false, }) const linkFactoryResult = linkOptions({ to: '/' } as any) const routeMatchResult = matchRoute({ to: '/' } as any) const SvgLink = createLink('svg') const hooksAndComponents = [ useAwaited, useLinkProps, useMatchRoute, useMatches, useParentMatches, useChildMatches, useMatch, useLoaderDeps, useLoaderData, useBlocker, useNavigate, useParams, useSearch, useRouteContext, useRouter, useRouterState, useLocation, useCanGoBack, useElementScrollRestoration, useTags, Await, CatchBoundary, CatchNotFound, ClientOnly, DefaultGlobalNotFound, ErrorComponent, Link, Match, MatchRoute, Matches, Navigate, Outlet, RouterContextProvider, ScrollRestoration, Block, ScriptOnce, Asset, HeadContent, Scripts, Body, Html, ] ;(globalThis as any).__TANSTACK_BUNDLE_SIZE_KEEP__ = { hooksAndComponents, } void awaited void linkFactoryResult void matches.value void parentMatches.value void childMatches.value void match.value void loaderDeps.value void loaderData.value void params.value void search.value void routeContext.value void routerState.value void location.value void canGoBack.value void navigate void scrollEntry void tags() void routeMatchResult.value return ( <> {'window.__tsr_bundle_size = true'} home {() => } }> } /> false} disabled withResolver={false}> {() => } }>
hello world
) } ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-full/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-full/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'vue', autoCodeSplitting: true, }), vue(), vueJsx(), ], }) ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-minimal/index.html ================================================ vue-router-minimal
================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-minimal/src/main.tsx ================================================ import { createApp } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' const router = createRouter({ routeTree }) declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement) { throw new Error('Root element `#app` not found') } if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-minimal/src/routes/__root.tsx ================================================ import { Outlet, createRootRoute } from '@tanstack/vue-router' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return } ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-minimal/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return
hello world
} ================================================ FILE: benchmarks/bundle-size/scenarios/vue-router-minimal/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'vue', autoCodeSplitting: true, }), vue(), vueJsx(), ], }) ================================================ FILE: benchmarks/bundle-size/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "target": "ESNext", "module": "ESNext", "moduleResolution": "Bundler", "jsx": "preserve", "allowJs": true, "skipLibCheck": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: benchmarks/client-nav/README.md ================================================ # Client Navigation Benchmarks Cross-framework client-side navigation benchmarks for: - `@tanstack/react-router` - `@tanstack/solid-router` - `@tanstack/vue-router` ## Layout - `react/` - React benchmark + Vitest config - `solid/` - Solid benchmark + Vitest config - `vue/` - Vue benchmark + Vitest config ## Run Run all benchmarks through Nx so dependency builds are part of the graph: ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:perf --outputStyle=stream --skipRemoteCache ``` Run framework-specific benchmarks: ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:perf:react --outputStyle=stream --skipRemoteCache CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:perf:solid --outputStyle=stream --skipRemoteCache CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:perf:vue --outputStyle=stream --skipRemoteCache ``` Run framework-specific flame benchmarks (10 second loop, profiled with `@platformatic/flame`, forced to `NODE_ENV=production`): ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:flame:react --outputStyle=stream --skipRemoteCache CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:flame:solid --outputStyle=stream --skipRemoteCache CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:flame:vue --outputStyle=stream --skipRemoteCache ``` Typecheck benchmark sources: ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:types --outputStyle=stream --skipRemoteCache ``` ================================================ FILE: benchmarks/client-nav/jsdom.ts ================================================ import { JSDOM } from 'jsdom' const dom = new JSDOM('', { url: 'http://localhost/', }) const { window } = dom function setGlobal(name: string, value: unknown) { Object.defineProperty(globalThis, name, { value, configurable: true, writable: true, }) } setGlobal('window', window) setGlobal('document', window.document) setGlobal('self', window) setGlobal('navigator', window.navigator) setGlobal('location', window.location) setGlobal('history', window.history) setGlobal('HTMLElement', window.HTMLElement) setGlobal('Element', window.Element) setGlobal('SVGElement', window.SVGElement) setGlobal('DocumentFragment', window.DocumentFragment) setGlobal('Node', window.Node) setGlobal('MutationObserver', window.MutationObserver) setGlobal('sessionStorage', window.sessionStorage) setGlobal('localStorage', window.localStorage) setGlobal('getComputedStyle', window.getComputedStyle.bind(window)) setGlobal( 'requestAnimationFrame', window.requestAnimationFrame?.bind(window) ?? ((callback: (time: number) => void) => setTimeout(() => callback(performance.now()), 16)), ) setGlobal( 'cancelAnimationFrame', window.cancelAnimationFrame?.bind(window) ?? ((handle: number) => clearTimeout(handle)), ) window.scrollTo = () => {} export { window } ================================================ FILE: benchmarks/client-nav/package.json ================================================ { "name": "@benchmarks/client-nav", "private": true, "type": "module", "scripts": { "build:react": "vite build --config ./react/vite.config.ts", "build:solid": "vite build --config ./solid/vite.config.ts", "build:vue": "vite build --config ./vue/vite.config.ts", "test:flame:react": "NODE_ENV=production flame run --md-format=detailed --delay=none --node-options=\"--stack-size=65500\" ./react/speed.flame.ts", "test:flame:solid": "NODE_ENV=production flame run --md-format=detailed --delay=none --node-options=\"--stack-size=65500\" ./solid/speed.flame.ts", "test:flame:vue": "NODE_ENV=production flame run --md-format=detailed --delay=none --node-options=\"--stack-size=65500\" ./vue/speed.flame.ts", "test:perf": "NODE_ENV=production vitest bench --config ./vitest.config.ts", "test:perf:react": "NODE_ENV=production vitest bench --config ./react/vite.config.ts ./react/speed.bench.ts", "test:perf:solid": "NODE_ENV=production vitest bench --config ./solid/vite.config.ts ./solid/speed.bench.ts", "test:perf:vue": "NODE_ENV=production vitest bench --config ./vue/vite.config.ts ./vue/speed.bench.ts", "test:types": "pnpm run test:types:react && pnpm run test:types:solid && pnpm run test:types:vue", "test:types:react": "tsc -p ./react/tsconfig.json --noEmit", "test:types:solid": "tsc -p ./solid/tsconfig.json --noEmit", "test:types:vue": "tsc -p ./vue/tsconfig.json --noEmit" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/router-core": "workspace:^", "@tanstack/solid-router": "workspace:^", "@tanstack/vue-router": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "solid-js": "^1.9.10", "vue": "^3.5.16" }, "devDependencies": { "@platformatic/flame": "^1.6.0", "@codspeed/vitest-plugin": "^5.0.1", "@testing-library/react": "^16.2.0", "@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "@types/jsdom": "28.0.0", "typescript": "^5.7.2", "vite": "^8.0.0", "vite-plugin-solid": "^2.11.11", "vitest": "^4.0.17" }, "nx": { "targets": { "build:react": { "cache": false, "dependsOn": [ { "projects": [ "@tanstack/react-router" ], "target": "build" } ] }, "build:solid": { "cache": false, "dependsOn": [ { "projects": [ "@tanstack/solid-router" ], "target": "build" } ] }, "build:vue": { "cache": false, "dependsOn": [ { "projects": [ "@tanstack/vue-router" ], "target": "build" } ] }, "test:perf": { "cache": false, "dependsOn": [ "build:react", "build:solid", "build:vue" ] }, "test:flame:react": { "cache": false, "dependsOn": [ "build:react" ] }, "test:flame:solid": { "cache": false, "dependsOn": [ "build:solid" ] }, "test:flame:vue": { "cache": false, "dependsOn": [ "build:vue" ] }, "test:perf:react": { "cache": false, "dependsOn": [ "build:react" ] }, "test:perf:solid": { "cache": false, "dependsOn": [ "build:solid" ] }, "test:perf:vue": { "cache": false, "dependsOn": [ "build:vue" ] }, "test:types": { "cache": false, "dependsOn": [ "^build" ] } } } } ================================================ FILE: benchmarks/client-nav/react/app.tsx ================================================ import { Link, Outlet, RouterProvider, createMemoryHistory, createRootRoute, createRoute, createRouter, useParams, useSearch, } from '@tanstack/react-router' import { createRoot } from 'react-dom/client' function runPerfSelectorComputation(seed: number) { let value = Math.trunc(seed) | 0 for (let index = 0; index < 100; index++) { value = (value * 1664525 + 1013904223 + index) >>> 0 } return value } const selectors = Array.from({ length: 20 }, (_, index) => index) function Params() { const params = useParams({ strict: false, select: (params) => runPerfSelectorComputation(Number(params.id ?? 0)), }) void params return null } function Search() { const search = useSearch({ strict: false, select: (search) => runPerfSelectorComputation(Number(search.id ?? 0)), }) void search return null } function Links() { return ( Link ) } function Root() { return ( <> {selectors.map((selector) => ( ))} {selectors.map((selector) => ( ))} {selectors.map((selector) => ( ))} ) } const rootRoute = createRootRoute({ component: Root, }) const route = createRoute({ getParentRoute: () => rootRoute, path: '/$id', component: () =>
, }) export function mountTestApp(container: Element) { const router = createRouter({ history: createMemoryHistory({ initialEntries: ['/0'], }), scrollRestoration: true, routeTree: rootRoute.addChildren([route]), }) const reactRoot = createRoot(container) reactRoot.render() return { router, unmount() { reactRoot.unmount() }, } } ================================================ FILE: benchmarks/client-nav/react/setup.ts ================================================ import type { NavigateOptions } from '@tanstack/router-core' import type * as App from './app' const appModulePath = './dist/app.js' const { mountTestApp } = (await import(appModulePath)) as typeof App export function setup() { if (process.env.NODE_ENV !== 'production') { console.warn( 'client-nav benchmark is running without NODE_ENV=production; React dev overhead will dominate results.', ) } let id = 0 let unmount: (() => void) | undefined = undefined let container: HTMLDivElement | undefined = undefined let unsub = () => {} let next: () => Promise = () => Promise.reject('Test not initialized') async function before() { id = 0 container = document.createElement('div') document.body.append(container) const { router, unmount: dispose } = mountTestApp(container) unmount = dispose let resolve: () => void = () => {} unsub = router.subscribe('onRendered', () => resolve()) const navigate = (opts: NavigateOptions) => new Promise((resolveNext) => { resolve = resolveNext router.navigate(opts) }) next = () => { const nextId = id++ return navigate({ to: '/$id', params: { id: nextId }, // update search every 2 navigations, to still test them, but also measure the impact of granular re-rendering search: { id: Math.floor(nextId / 2) }, replace: true, }) } await router.load() } function after() { unmount?.() container?.remove() unsub() } function tick() { return next() } return { before, tick, after, } } ================================================ FILE: benchmarks/client-nav/react/speed.bench.ts ================================================ import { afterAll, beforeAll, bench, describe } from 'vitest' import { setup } from './setup' describe('client-nav', () => { const test = setup() /** * Running `vitest bench` ignores "suite hooks" like `beforeAll` and `afterAll`, * so we use tinybench's `setup` and `teardown` options to run our setup and teardown logic. * * But CodSpeed calls the benchmarked function directly, bypassing `setup` and `teardown`, * but it does support `beforeAll` and `afterAll`. * * So it looks like we're setting up in duplicate, but in reality, it's only running once per environment, as intended. */ beforeAll(test.before) afterAll(test.after) bench( 'client-side navigation loop (react)', async () => { for (let i = 0; i < 10; i++) { await test.tick() } }, { warmupIterations: 100, time: 10_000, setup: test.before, teardown: test.after, }, ) }) ================================================ FILE: benchmarks/client-nav/react/speed.flame.ts ================================================ import { window } from '../jsdom.ts' import { setup } from './setup.ts' const DURATION_MS = 10_000 const test = setup() try { await test.before() const startedAt = performance.now() while (performance.now() - startedAt < DURATION_MS) { await test.tick() } } finally { test.after() window.close() } ================================================ FILE: benchmarks/client-nav/react/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "jsx": "react-jsx", "allowImportingTsExtensions": true, "jsxImportSource": "react", "types": ["node", "vite/client", "vitest/globals"] }, "include": [ "speed.bench.ts", "speed.flame.ts", "../jsdom.ts", "setup.ts", "vite.config.ts", "../vitest.setup.ts" ] } ================================================ FILE: benchmarks/client-nav/react/vite.config.ts ================================================ import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' import codspeedPlugin from '@codspeed/vitest-plugin' export default defineConfig({ plugins: [ !!(process.env.VITEST && process.env.WITH_INSTRUMENTATION) && codspeedPlugin(), react(), ], build: { outDir: './react/dist', emptyOutDir: true, minify: false, lib: { entry: './react/app.tsx', formats: ['es'], fileName: 'app', }, }, test: { name: '@benchmarks/client-nav (react)', watch: false, environment: 'jsdom', setupFiles: ['./vitest.setup.ts'], }, }) ================================================ FILE: benchmarks/client-nav/solid/app.tsx ================================================ import { For, createEffect } from 'solid-js' import { render } from 'solid-js/web' import { Link, Outlet, RouterProvider, createMemoryHistory, createRootRoute, createRoute, createRouter, useParams, useSearch, } from '@tanstack/solid-router' function runPerfSelectorComputation(seed: number) { let value = Math.trunc(seed) | 0 for (let index = 0; index < 100; index++) { value = (value * 1664525 + 1013904223 + index) >>> 0 } return value } const selectors = Array.from({ length: 20 }, (_, index) => index) function Params() { const params = useParams({ strict: false, select: (params) => runPerfSelectorComputation(Number(params.id ?? 0)), }) createEffect(() => { void params() }) return null } function Search() { const search = useSearch({ strict: false, select: (search) => runPerfSelectorComputation(Number(search.id ?? 0)), }) createEffect(() => { void search() }) return null } function Links() { return ( Link ) } function Root() { return ( <> {() => } {() => } {() => } ) } const root = createRootRoute({ component: Root, }) const route = createRoute({ getParentRoute: () => root, path: '/$id', component: () => { return
}, }) export function mountTestApp(container: Element) { const router = createRouter({ history: createMemoryHistory({ initialEntries: ['/0'], }), scrollRestoration: true, routeTree: root.addChildren([route]), }) const unmount = render(() => , container) return { router, unmount } } ================================================ FILE: benchmarks/client-nav/solid/setup.ts ================================================ import type { NavigateOptions } from '@tanstack/router-core' import type * as App from './app' const appModulePath = './dist/app.js' const { mountTestApp } = (await import(appModulePath)) as typeof App export function setup() { if (process.env.NODE_ENV !== 'production') { console.warn( 'client-nav benchmark is running without NODE_ENV=production; Solid dev overhead will dominate results.', ) } let id = 0 let dispose: (() => void) | undefined = undefined let container: HTMLDivElement | undefined = undefined let unsub = () => {} let next: () => Promise = () => Promise.reject('Test not initialized') async function before() { id = 0 container = document.createElement('div') document.body.append(container) const { router, unmount } = mountTestApp(container) dispose = unmount let resolveRendered: () => void = () => {} unsub = router.subscribe('onRendered', () => { resolveRendered() }) const navigate = (opts: NavigateOptions) => new Promise((resolveNext) => { resolveRendered = resolveNext router.navigate(opts) }) next = () => { const nextId = id++ return navigate({ to: '/$id', params: { id: nextId }, // update search every 2 navigations, to still test them, but also measure the impact of granular re-rendering search: { id: Math.floor(nextId / 2) }, replace: true, }) } await router.load() } function after() { dispose?.() container?.remove() unsub() } function tick() { return next() } return { before, tick, after, } } ================================================ FILE: benchmarks/client-nav/solid/speed.bench.ts ================================================ import { afterAll, beforeAll, bench, describe } from 'vitest' import { setup } from './setup' describe('client-nav', () => { const test = setup() /** * Running `vitest bench` ignores "suite hooks" like `beforeAll` and `afterAll`, * so we use tinybench's `setup` and `teardown` options to run our setup and teardown logic. * * But CodSpeed calls the benchmarked function directly, bypassing `setup` and `teardown`, * but it does support `beforeAll` and `afterAll`. * * So it looks like we're setting up in duplicate, but in reality, it's only running once per environment, as intended. */ beforeAll(test.before) afterAll(test.after) bench( 'client-side navigation loop (solid)', async () => { for (let i = 0; i < 10; i++) { await test.tick() } }, { warmupIterations: 100, time: 10_000, setup: test.before, teardown: test.after, }, ) }) ================================================ FILE: benchmarks/client-nav/solid/speed.flame.ts ================================================ import { window } from '../jsdom.ts' import { setup } from './setup.ts' const DURATION_MS = 10_000 const test = setup() try { await test.before() const startedAt = performance.now() while (performance.now() - startedAt < DURATION_MS) { await test.tick() } } finally { test.after() window.close() } ================================================ FILE: benchmarks/client-nav/solid/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "jsx": "preserve", "allowImportingTsExtensions": true, "jsxImportSource": "solid-js", "types": ["node", "vite/client", "vitest/globals"] }, "include": [ "speed.bench.ts", "speed.flame.ts", "../jsdom.ts", "setup.ts", "vite.config.ts", "../vitest.setup.ts" ] } ================================================ FILE: benchmarks/client-nav/solid/vite.config.ts ================================================ import { defineConfig } from 'vitest/config' import solid from 'vite-plugin-solid' import codspeedPlugin from '@codspeed/vitest-plugin' export default defineConfig({ plugins: [ !!(process.env.VITEST && process.env.WITH_INSTRUMENTATION) && codspeedPlugin(), solid({ hot: false, dev: false }), ], build: { outDir: './solid/dist', emptyOutDir: true, minify: false, lib: { entry: './solid/app.tsx', formats: ['es'], fileName: 'app', }, }, resolve: { conditions: ['solid', 'browser'], }, test: { name: '@benchmarks/client-nav (solid)', watch: false, environment: 'jsdom', setupFiles: ['./vitest.setup.ts'], server: { deps: { inline: [/@solidjs/, /@tanstack\/solid-store/], }, }, }, }) ================================================ FILE: benchmarks/client-nav/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "types": ["node", "vite/client", "vitest/globals"] }, "include": ["vitest.setup.ts"] } ================================================ FILE: benchmarks/client-nav/vitest.config.ts ================================================ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { watch: false, projects: [ './react/vite.config.ts', './solid/vite.config.ts', './vue/vite.config.ts', ], }, }) ================================================ FILE: benchmarks/client-nav/vitest.setup.ts ================================================ import { vi } from 'vitest' // @ts-expect-error global.IS_REACT_ACT_ENVIRONMENT = true window.scrollTo = vi.fn() ================================================ FILE: benchmarks/client-nav/vue/app.tsx ================================================ import * as Vue from 'vue' import { Link, Outlet, RouterProvider, createMemoryHistory, createRootRoute, createRoute, createRouter, useParams, useSearch, } from '@tanstack/vue-router' function runPerfSelectorComputation(seed: number) { let value = Math.trunc(seed) | 0 for (let index = 0; index < 100; index++) { value = (value * 1664525 + 1013904223 + index) >>> 0 } return value } const selectors = Array.from({ length: 20 }, (_, index) => index) const Params = Vue.defineComponent({ setup() { const params = useParams({ strict: false, select: (params) => runPerfSelectorComputation(Number(params.id ?? 0)), }) return () => { void params.value return null } }, }) const Search = Vue.defineComponent({ setup() { const search = useSearch({ strict: false, select: (search) => runPerfSelectorComputation(Number(search.id ?? 0)), }) return () => { void search.value return null } }, }) const Links = Vue.defineComponent({ setup() { return () => ( Link ) }, }) const Root = Vue.defineComponent({ setup() { return () => ( <> {selectors.map((selector) => ( ))} {selectors.map((selector) => ( ))} {selectors.map((selector) => ( ))} ) }, }) const root = createRootRoute({ component: Root, }) const route = createRoute({ getParentRoute: () => root, path: '/$id', component: () =>
, }) export function mountTestApp(container: Element) { const router = createRouter({ history: createMemoryHistory({ initialEntries: ['/0'], }), scrollRestoration: true, routeTree: root.addChildren([route]), }) const component = const app = Vue.createApp({ render: () => component, }) app.mount(container) return { router, unmount() { app.unmount() }, } } ================================================ FILE: benchmarks/client-nav/vue/setup.ts ================================================ import type { NavigateOptions } from '@tanstack/router-core' import type * as App from './app' const appModulePath = './dist/app.js' const { mountTestApp } = (await import(appModulePath)) as typeof App export function setup() { if (process.env.NODE_ENV !== 'production') { console.warn( 'client-nav benchmark is running without NODE_ENV=production; Vue dev overhead will dominate results.', ) } let id = 0 let unmount: (() => void) | undefined = undefined let container: HTMLDivElement | undefined = undefined let unsub = () => {} let next: () => Promise = () => Promise.reject('Test not initialized') async function before() { id = 0 container = document.createElement('div') document.body.append(container) const { router, unmount: dispose } = mountTestApp(container) unmount = dispose let resolveRendered: () => void = () => {} unsub = router.subscribe('onRendered', () => { resolveRendered() }) const navigate = (opts: NavigateOptions) => new Promise((resolveNext) => { resolveRendered = resolveNext router.navigate(opts) }) next = () => { const nextId = id++ return navigate({ to: '/$id', params: { id: nextId }, // update search every 2 navigations, to still test them, but also measure the impact of granular re-rendering search: { id: Math.floor(nextId / 2) }, replace: true, }) } await router.load() } function after() { unmount?.() container?.remove() unsub() } function tick() { return next() } return { before, tick, after, } } ================================================ FILE: benchmarks/client-nav/vue/speed.bench.ts ================================================ import { afterAll, beforeAll, bench, describe } from 'vitest' import { setup } from './setup' describe('client-nav', () => { const test = setup() /** * Running `vitest bench` ignores "suite hooks" like `beforeAll` and `afterAll`, * so we use tinybench's `setup` and `teardown` options to run our setup and teardown logic. * * But CodSpeed calls the benchmarked function directly, bypassing `setup` and `teardown`, * but it does support `beforeAll` and `afterAll`. * * So it looks like we're setting up in duplicate, but in reality, it's only running once per environment, as intended. */ beforeAll(test.before) afterAll(test.after) bench( 'client-side navigation loop (vue)', async () => { for (let i = 0; i < 10; i++) { await test.tick() } }, { warmupIterations: 100, time: 10_000, setup: test.before, teardown: test.after, }, ) }) ================================================ FILE: benchmarks/client-nav/vue/speed.flame.ts ================================================ import { window } from '../jsdom.ts' import { setup } from './setup.ts' const DURATION_MS = 10_000 const test = setup() try { await test.before() const startedAt = performance.now() while (performance.now() - startedAt < DURATION_MS) { await test.tick() } } finally { test.after() window.close() } ================================================ FILE: benchmarks/client-nav/vue/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "jsx": "preserve", "allowImportingTsExtensions": true, "jsxImportSource": "vue", "types": ["node", "vite/client", "vitest/globals"] }, "include": [ "speed.bench.ts", "speed.flame.ts", "../jsdom.ts", "setup.ts", "vite.config.ts", "../vitest.setup.ts" ] } ================================================ FILE: benchmarks/client-nav/vue/vite.config.ts ================================================ import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import codspeedPlugin from '@codspeed/vitest-plugin' export default defineConfig({ define: { 'process.env.NODE_ENV': JSON.stringify('production'), }, plugins: [ !!(process.env.VITEST && process.env.WITH_INSTRUMENTATION) && codspeedPlugin(), vue(), vueJsx(), ], build: { outDir: './vue/dist', emptyOutDir: true, minify: false, lib: { entry: './vue/app.tsx', formats: ['es'], fileName: 'app', }, }, test: { name: '@benchmarks/client-nav (vue)', watch: false, environment: 'jsdom', setupFiles: ['./vitest.setup.ts'], }, }) ================================================ FILE: benchmarks/ssr/README.md ================================================ # SSR Benchmarks Cross-framework SSR request-loop benchmarks for: - `@tanstack/react-start` - `@tanstack/solid-start` - `@tanstack/vue-start` Each benchmark builds a Start app with file-based routes and runs Vitest benches against the built server handler. ## Layout - `react/` - React Start benchmark + Vitest config - `solid/` - Solid Start benchmark + Vitest config - `vue/` - Vue Start benchmark + Vitest config ## Run Run all benchmarks through Nx so dependency builds are part of the graph: ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/ssr:test:perf --outputStyle=stream --skipRemoteCache ``` Run framework-specific benchmarks: ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/ssr:test:perf:react --outputStyle=stream --skipRemoteCache CI=1 NX_DAEMON=false pnpm nx run @benchmarks/ssr:test:perf:solid --outputStyle=stream --skipRemoteCache CI=1 NX_DAEMON=false pnpm nx run @benchmarks/ssr:test:perf:vue --outputStyle=stream --skipRemoteCache ``` Typecheck benchmark sources: ```bash CI=1 NX_DAEMON=false pnpm nx run @benchmarks/ssr:test:types --outputStyle=stream --skipRemoteCache ``` ================================================ FILE: benchmarks/ssr/bench-utils.ts ================================================ export interface StartRequestHandler { fetch: (request: Request) => Promise | Response } export interface RunSsrRequestLoopOptions { seed: number iterations?: number } const requestInit = { method: 'GET', headers: { accept: 'text/html', }, } satisfies RequestInit function createDeterministicRandom(seed: number) { let state = seed >>> 0 return () => { state = (state * 1664525 + 1013904223) >>> 0 return state / 0x100000000 } } function randomSegment(random: () => number) { return Math.floor(random() * 1_000_000_000).toString(36) } function randomSearchValue(random: () => number) { return `q-${randomSegment(random)}` } function randomRequestUrl(random: () => number) { const a = randomSegment(random) const b = randomSegment(random) const c = randomSegment(random) const d = randomSegment(random) const q = randomSearchValue(random) return `http://localhost/${a}/${b}/${c}/${d}?q=${q}` } export async function runSsrRequestLoop( handler: StartRequestHandler, { seed, iterations = 10 }: RunSsrRequestLoopOptions, ) { const random = createDeterministicRandom(seed) const pendingBodyReads: Array> = [] for (let index = 0; index < iterations; index++) { const requestUrl = randomRequestUrl(random) const response = await handler.fetch(new Request(requestUrl, requestInit)) if (response.status !== 200) { await Promise.allSettled(pendingBodyReads) throw new Error( `Request failed with non-200 status ${response.status} (${requestUrl})`, ) } pendingBodyReads.push(response.text().then(() => undefined)) } await Promise.all(pendingBodyReads) } ================================================ FILE: benchmarks/ssr/package.json ================================================ { "name": "@benchmarks/ssr", "private": true, "type": "module", "scripts": { "build:react": "vite build --config ./react/vite.config.ts", "build:solid": "vite build --config ./solid/vite.config.ts", "build:vue": "vite build --config ./vue/vite.config.ts", "test:perf": "vitest bench", "test:perf:react": "vitest bench --config ./react/vite.config.ts ./react/speed.bench.ts", "test:perf:solid": "vitest bench --config ./solid/vite.config.ts ./solid/speed.bench.ts", "test:perf:vue": "vitest bench --config ./vue/vite.config.ts ./vue/speed.bench.ts", "test:types": "pnpm run test:types:react && pnpm run test:types:solid && pnpm run test:types:vue", "test:types:react": "tsc -p ./react/tsconfig.json --noEmit", "test:types:solid": "tsc -p ./solid/tsconfig.json --noEmit", "test:types:vue": "tsc -p ./vue/tsconfig.json --noEmit" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/react-start": "workspace:^", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-start": "workspace:^", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-start": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "solid-js": "^1.9.10", "vue": "^3.5.16" }, "devDependencies": { "@codspeed/vitest-plugin": "^5.0.1", "@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "^5.7.2", "vite": "^8.0.0", "vite-plugin-solid": "^2.11.11", "vitest": "^4.0.17" }, "nx": { "targets": { "build:react": { "cache": false, "dependsOn": [ { "projects": [ "@tanstack/react-start" ], "target": "build" } ] }, "build:solid": { "cache": false, "dependsOn": [ { "projects": [ "@tanstack/solid-start" ], "target": "build" } ] }, "build:vue": { "cache": false, "dependsOn": [ { "projects": [ "@tanstack/vue-start" ], "target": "build" } ] }, "test:perf": { "cache": false, "dependsOn": [ "build:react", "build:solid", "build:vue" ] }, "test:perf:react": { "cache": false, "dependsOn": [ "build:react" ] }, "test:perf:solid": { "cache": false, "dependsOn": [ "build:solid" ] }, "test:perf:vue": { "cache": false, "dependsOn": [ "build:vue" ] }, "test:types": { "cache": false, "dependsOn": [ "^build" ] } } } } ================================================ FILE: benchmarks/ssr/react/speed.bench.ts ================================================ import { afterAll, beforeAll, bench, describe } from 'vitest' import { runSsrRequestLoop } from '../bench-utils' import type { StartRequestHandler } from '../bench-utils' const appModulePath = './dist/server/server.js' const benchmarkSeed = 0xdecafbad const uninitializedHandler: StartRequestHandler = { fetch: () => Promise.reject(new Error('Benchmark not initialized')), } let handler = uninitializedHandler async function setup() { const module = (await import(appModulePath)) as { default: StartRequestHandler } handler = module.default } function teardown() { handler = uninitializedHandler } describe('ssr', () => { /** * Running `vitest bench` ignores "suite hooks" like `beforeAll` and `afterAll`, * so we use tinybench's `setup` and `teardown` options to run our setup and teardown logic. * * But CodSpeed calls the benchmarked function directly, bypassing `setup` and `teardown`, * but it does support `beforeAll` and `afterAll`. * * So it looks like we're setting up in duplicate, but in reality, it's only running once per environment, as intended. */ beforeAll(setup) afterAll(teardown) bench( 'ssr request loop (react)', () => runSsrRequestLoop(handler, { seed: benchmarkSeed }), { warmupIterations: 100, time: 10_000, setup, teardown, throws: true, }, ) }) ================================================ FILE: benchmarks/ssr/react/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as ARouteImport } from './routes/$a' import { Route as ABRouteImport } from './routes/$a.$b' import { Route as ABCRouteImport } from './routes/$a.$b.$c' import { Route as ABCDRouteImport } from './routes/$a.$b.$c.$d' const ARoute = ARouteImport.update({ id: '/$a', path: '/$a', getParentRoute: () => rootRouteImport, } as any) const ABRoute = ABRouteImport.update({ id: '/$b', path: '/$b', getParentRoute: () => ARoute, } as any) const ABCRoute = ABCRouteImport.update({ id: '/$c', path: '/$c', getParentRoute: () => ABRoute, } as any) const ABCDRoute = ABCDRouteImport.update({ id: '/$d', path: '/$d', getParentRoute: () => ABCRoute, } as any) export interface FileRoutesByFullPath { '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRoutesByTo { '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' fileRoutesByTo: FileRoutesByTo to: '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' id: '__root__' | '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' fileRoutesById: FileRoutesById } export interface RootRouteChildren { ARoute: typeof ARouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/$a': { id: '/$a' path: '/$a' fullPath: '/$a' preLoaderRoute: typeof ARouteImport parentRoute: typeof rootRouteImport } '/$a/$b': { id: '/$a/$b' path: '/$b' fullPath: '/$a/$b' preLoaderRoute: typeof ABRouteImport parentRoute: typeof ARoute } '/$a/$b/$c': { id: '/$a/$b/$c' path: '/$c' fullPath: '/$a/$b/$c' preLoaderRoute: typeof ABCRouteImport parentRoute: typeof ABRoute } '/$a/$b/$c/$d': { id: '/$a/$b/$c/$d' path: '/$d' fullPath: '/$a/$b/$c/$d' preLoaderRoute: typeof ABCDRouteImport parentRoute: typeof ABCRoute } } } interface ABCRouteChildren { ABCDRoute: typeof ABCDRoute } const ABCRouteChildren: ABCRouteChildren = { ABCDRoute: ABCDRoute, } const ABCRouteWithChildren = ABCRoute._addFileChildren(ABCRouteChildren) interface ABRouteChildren { ABCRoute: typeof ABCRouteWithChildren } const ABRouteChildren: ABRouteChildren = { ABCRoute: ABCRouteWithChildren, } const ABRouteWithChildren = ABRoute._addFileChildren(ABRouteChildren) interface ARouteChildren { ABRoute: typeof ABRouteWithChildren } const ARouteChildren: ARouteChildren = { ABRoute: ABRouteWithChildren, } const ARouteWithChildren = ARoute._addFileChildren(ARouteChildren) const rootRouteChildren: RootRouteChildren = { ARoute: ARouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/react-start' declare module '@tanstack/react-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: benchmarks/ssr/react/src/router.tsx ================================================ import { createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, defaultPreload: false, scrollRestoration: false, }) } declare module '@tanstack/react-router' { interface Register { router: ReturnType } } ================================================ FILE: benchmarks/ssr/react/src/routes/$a.$b.$c.$d.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b/$c/$d')({ component: LevelDComponent, }) function LevelDComponent() { return } ================================================ FILE: benchmarks/ssr/react/src/routes/$a.$b.$c.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b/$c')({ component: LevelCComponent, }) function LevelCComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/react/src/routes/$a.$b.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b')({ component: LevelBComponent, }) function LevelBComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/react/src/routes/$a.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a')({ component: LevelAComponent, }) function LevelAComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/react/src/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/react-router' export const Route = createRootRoute({ component: RootComponent, validateSearch: (s) => s as { q?: string }, }) function RootComponent() { return ( ) } ================================================ FILE: benchmarks/ssr/react/src/workload.tsx ================================================ import { Link, useParams, useSearch } from '@tanstack/react-router' const probes = Array.from({ length: 10 }, (_, index) => index) function runSelectorWork(input: string, salt: number) { let value = salt for (let index = 0; index < input.length; index++) { value = (value * 33 + input.charCodeAt(index) + index) >>> 0 } for (let index = 0; index < 16; index++) { value = (value ^ (value << 13)) >>> 0 value = (value ^ (value >> 17)) >>> 0 value = (value ^ (value << 5)) >>> 0 } return value } function ParamsProbe({ salt }: { salt: number }) { const params = useParams({ strict: false, select: (nextParams) => runSelectorWork( `${nextParams.a ?? ''}/${nextParams.b ?? ''}/${nextParams.c ?? ''}/${nextParams.d ?? ''}`, salt, ), }) void params return null } function SearchProbe({ salt }: { salt: number }) { const search = useSearch({ strict: false, select: (nextSearch) => runSelectorWork(String(nextSearch.q ?? ''), salt), }) void search return null } function LinkProbe({ salt }: { salt: number }) { const value = String((salt % 97) + 1) return ( Link ) } export function RouteWorkload() { return ( <> {probes.map((probe) => ( ))} {probes.map((probe) => ( ))} {probes.map((probe) => ( ))} ) } ================================================ FILE: benchmarks/ssr/react/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "react", "types": ["node", "vite/client", "vitest/globals"] }, "include": [ "speed.bench.ts", "vite.config.ts", "../bench-utils.ts", "./src/**/*" ] } ================================================ FILE: benchmarks/ssr/react/vite.config.ts ================================================ import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import codspeedPlugin from '@codspeed/vitest-plugin' import { tanstackStart } from '@tanstack/react-start/plugin/vite' import react from '@vitejs/plugin-react' const rootDir = fileURLToPath(new URL('.', import.meta.url)) export default defineConfig({ root: rootDir, plugins: [ !!(process.env.VITEST && process.env.WITH_INSTRUMENTATION) && codspeedPlugin(), tanstackStart({ srcDirectory: 'src', }), react(), ], build: { outDir: './dist', emptyOutDir: true, minify: false, }, test: { name: '@benchmarks/ssr (react)', watch: false, environment: 'node', }, }) ================================================ FILE: benchmarks/ssr/solid/speed.bench.ts ================================================ import { afterAll, beforeAll, bench, describe } from 'vitest' import { runSsrRequestLoop } from '../bench-utils' import type { StartRequestHandler } from '../bench-utils' const appModulePath = './dist/server/server.js' const benchmarkSeed = 0xcafebabe const uninitializedHandler: StartRequestHandler = { fetch: () => Promise.reject(new Error('Benchmark not initialized')), } let handler = uninitializedHandler async function setup() { const module = (await import(appModulePath)) as { default: StartRequestHandler } handler = module.default } function teardown() { handler = uninitializedHandler } describe('ssr', () => { /** * Running `vitest bench` ignores "suite hooks" like `beforeAll` and `afterAll`, * so we use tinybench's `setup` and `teardown` options to run our setup and teardown logic. * * But CodSpeed calls the benchmarked function directly, bypassing `setup` and `teardown`, * but it does support `beforeAll` and `afterAll`. * * So it looks like we're setting up in duplicate, but in reality, it's only running once per environment, as intended. */ beforeAll(setup) afterAll(teardown) bench( 'ssr request loop (solid)', () => runSsrRequestLoop(handler, { seed: benchmarkSeed }), { warmupIterations: 100, time: 10_000, setup, teardown, throws: true, }, ) }) ================================================ FILE: benchmarks/ssr/solid/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as ARouteImport } from './routes/$a' import { Route as ABRouteImport } from './routes/$a.$b' import { Route as ABCRouteImport } from './routes/$a.$b.$c' import { Route as ABCDRouteImport } from './routes/$a.$b.$c.$d' const ARoute = ARouteImport.update({ id: '/$a', path: '/$a', getParentRoute: () => rootRouteImport, } as any) const ABRoute = ABRouteImport.update({ id: '/$b', path: '/$b', getParentRoute: () => ARoute, } as any) const ABCRoute = ABCRouteImport.update({ id: '/$c', path: '/$c', getParentRoute: () => ABRoute, } as any) const ABCDRoute = ABCDRouteImport.update({ id: '/$d', path: '/$d', getParentRoute: () => ABCRoute, } as any) export interface FileRoutesByFullPath { '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRoutesByTo { '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' fileRoutesByTo: FileRoutesByTo to: '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' id: '__root__' | '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' fileRoutesById: FileRoutesById } export interface RootRouteChildren { ARoute: typeof ARouteWithChildren } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/$a': { id: '/$a' path: '/$a' fullPath: '/$a' preLoaderRoute: typeof ARouteImport parentRoute: typeof rootRouteImport } '/$a/$b': { id: '/$a/$b' path: '/$b' fullPath: '/$a/$b' preLoaderRoute: typeof ABRouteImport parentRoute: typeof ARoute } '/$a/$b/$c': { id: '/$a/$b/$c' path: '/$c' fullPath: '/$a/$b/$c' preLoaderRoute: typeof ABCRouteImport parentRoute: typeof ABRoute } '/$a/$b/$c/$d': { id: '/$a/$b/$c/$d' path: '/$d' fullPath: '/$a/$b/$c/$d' preLoaderRoute: typeof ABCDRouteImport parentRoute: typeof ABCRoute } } } interface ABCRouteChildren { ABCDRoute: typeof ABCDRoute } const ABCRouteChildren: ABCRouteChildren = { ABCDRoute: ABCDRoute, } const ABCRouteWithChildren = ABCRoute._addFileChildren(ABCRouteChildren) interface ABRouteChildren { ABCRoute: typeof ABCRouteWithChildren } const ABRouteChildren: ABRouteChildren = { ABCRoute: ABCRouteWithChildren, } const ABRouteWithChildren = ABRoute._addFileChildren(ABRouteChildren) interface ARouteChildren { ABRoute: typeof ABRouteWithChildren } const ARouteChildren: ARouteChildren = { ABRoute: ABRouteWithChildren, } const ARouteWithChildren = ARoute._addFileChildren(ARouteChildren) const rootRouteChildren: RootRouteChildren = { ARoute: ARouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: benchmarks/ssr/solid/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, defaultPreload: false, scrollRestoration: false, }) } declare module '@tanstack/solid-router' { interface Register { router: ReturnType } } ================================================ FILE: benchmarks/ssr/solid/src/routes/$a.$b.$c.$d.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b/$c/$d')({ component: LevelDComponent, }) function LevelDComponent() { return } ================================================ FILE: benchmarks/ssr/solid/src/routes/$a.$b.$c.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b/$c')({ component: LevelCComponent, }) function LevelCComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/solid/src/routes/$a.$b.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b')({ component: LevelBComponent, }) function LevelBComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/solid/src/routes/$a.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a')({ component: LevelAComponent, }) function LevelAComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/solid/src/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' export const Route = createRootRoute({ component: RootComponent, validateSearch: (s) => s as { q?: string }, }) function RootComponent() { return ( ) } ================================================ FILE: benchmarks/ssr/solid/src/workload.tsx ================================================ import { For, createEffect } from 'solid-js' import { Link, useParams, useSearch } from '@tanstack/solid-router' const probes = Array.from({ length: 10 }, (_, index) => index) function runSelectorWork(input: string, salt: number) { let value = salt for (let index = 0; index < input.length; index++) { value = (value * 33 + input.charCodeAt(index) + index) >>> 0 } for (let index = 0; index < 16; index++) { value = (value ^ (value << 13)) >>> 0 value = (value ^ (value >> 17)) >>> 0 value = (value ^ (value << 5)) >>> 0 } return value } function ParamsProbe(props: { salt: number }) { const params = useParams({ strict: false, select: (nextParams) => runSelectorWork( `${nextParams.a ?? ''}/${nextParams.b ?? ''}/${nextParams.c ?? ''}/${nextParams.d ?? ''}`, props.salt, ), }) createEffect(() => { void params() }) return null } function SearchProbe(props: { salt: number }) { const search = useSearch({ strict: false, select: (nextSearch) => runSelectorWork(String(nextSearch.q ?? ''), props.salt), }) createEffect(() => { void search() }) return null } function LinkProbe(props: { salt: number }) { const value = String((props.salt % 97) + 1) return ( Link ) } export function RouteWorkload() { return ( <> {(probe) => } {(probe) => } {(probe) => } ) } ================================================ FILE: benchmarks/ssr/solid/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "jsx": "preserve", "jsxImportSource": "solid-js", "types": ["node", "vite/client", "vitest/globals"] }, "include": [ "speed.bench.ts", "vite.config.ts", "../bench-utils.ts", "./src/**/*" ] } ================================================ FILE: benchmarks/ssr/solid/vite.config.ts ================================================ import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import codspeedPlugin from '@codspeed/vitest-plugin' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import solid from 'vite-plugin-solid' const rootDir = fileURLToPath(new URL('.', import.meta.url)) export default defineConfig({ root: rootDir, plugins: [ !!(process.env.VITEST && process.env.WITH_INSTRUMENTATION) && codspeedPlugin(), tanstackStart({ srcDirectory: 'src', }), solid({ ssr: true, hot: false, dev: false }), ], build: { outDir: './dist', emptyOutDir: true, minify: false, }, test: { name: '@benchmarks/ssr (solid)', watch: false, environment: 'node', server: { deps: { inline: [/@solidjs/, /@tanstack\/solid-store/], }, }, }, }) ================================================ FILE: benchmarks/ssr/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "types": ["node", "vite/client", "vitest/globals"] }, "include": ["bench-utils.ts"] } ================================================ FILE: benchmarks/ssr/vitest.config.ts ================================================ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { watch: false, projects: [ './react/vite.config.ts', './solid/vite.config.ts', './vue/vite.config.ts', ], }, }) ================================================ FILE: benchmarks/ssr/vue/speed.bench.ts ================================================ import { afterAll, beforeAll, bench, describe } from 'vitest' import { runSsrRequestLoop } from '../bench-utils' import type { StartRequestHandler } from '../bench-utils' const appModulePath = './dist/server/server.js' const benchmarkSeed = 0xdeadbeef const uninitializedHandler: StartRequestHandler = { fetch: () => Promise.reject(new Error('Benchmark not initialized')), } let handler = uninitializedHandler async function setup() { const module = (await import(appModulePath)) as { default: StartRequestHandler } handler = module.default } function teardown() { handler = uninitializedHandler } describe('ssr', () => { /** * Running `vitest bench` ignores "suite hooks" like `beforeAll` and `afterAll`, * so we use tinybench's `setup` and `teardown` options to run our setup and teardown logic. * * But CodSpeed calls the benchmarked function directly, bypassing `setup` and `teardown`, * but it does support `beforeAll` and `afterAll`. * * So it looks like we're setting up in duplicate, but in reality, it's only running once per environment, as intended. */ beforeAll(setup) afterAll(teardown) bench( 'ssr request loop (vue)', () => runSsrRequestLoop(handler, { seed: benchmarkSeed }), { warmupIterations: 100, time: 10_000, setup, teardown, throws: true, }, ) }) ================================================ FILE: benchmarks/ssr/vue/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as ARouteImport } from './routes/$a' import { Route as ABRouteImport } from './routes/$a.$b' import { Route as ABCRouteImport } from './routes/$a.$b.$c' import { Route as ABCDRouteImport } from './routes/$a.$b.$c.$d' const ARoute = ARouteImport.update({ id: '/$a', path: '/$a', getParentRoute: () => rootRouteImport, } as any) const ABRoute = ABRouteImport.update({ id: '/$b', path: '/$b', getParentRoute: () => ARoute, } as any) const ABCRoute = ABCRouteImport.update({ id: '/$c', path: '/$c', getParentRoute: () => ABRoute, } as any) const ABCDRoute = ABCDRouteImport.update({ id: '/$d', path: '/$d', getParentRoute: () => ABCRoute, } as any) export interface FileRoutesByFullPath { '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRoutesByTo { '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/$a': typeof ARouteWithChildren '/$a/$b': typeof ABRouteWithChildren '/$a/$b/$c': typeof ABCRouteWithChildren '/$a/$b/$c/$d': typeof ABCDRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' fileRoutesByTo: FileRoutesByTo to: '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' id: '__root__' | '/$a' | '/$a/$b' | '/$a/$b/$c' | '/$a/$b/$c/$d' fileRoutesById: FileRoutesById } export interface RootRouteChildren { ARoute: typeof ARouteWithChildren } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/$a': { id: '/$a' path: '/$a' fullPath: '/$a' preLoaderRoute: typeof ARouteImport parentRoute: typeof rootRouteImport } '/$a/$b': { id: '/$a/$b' path: '/$b' fullPath: '/$a/$b' preLoaderRoute: typeof ABRouteImport parentRoute: typeof ARoute } '/$a/$b/$c': { id: '/$a/$b/$c' path: '/$c' fullPath: '/$a/$b/$c' preLoaderRoute: typeof ABCRouteImport parentRoute: typeof ABRoute } '/$a/$b/$c/$d': { id: '/$a/$b/$c/$d' path: '/$d' fullPath: '/$a/$b/$c/$d' preLoaderRoute: typeof ABCDRouteImport parentRoute: typeof ABCRoute } } } interface ABCRouteChildren { ABCDRoute: typeof ABCDRoute } const ABCRouteChildren: ABCRouteChildren = { ABCDRoute: ABCDRoute, } const ABCRouteWithChildren = ABCRoute._addFileChildren(ABCRouteChildren) interface ABRouteChildren { ABCRoute: typeof ABCRouteWithChildren } const ABRouteChildren: ABRouteChildren = { ABCRoute: ABCRouteWithChildren, } const ABRouteWithChildren = ABRoute._addFileChildren(ABRouteChildren) interface ARouteChildren { ABRoute: typeof ABRouteWithChildren } const ARouteChildren: ARouteChildren = { ABRoute: ABRouteWithChildren, } const ARouteWithChildren = ARoute._addFileChildren(ARouteChildren) const rootRouteChildren: RootRouteChildren = { ARoute: ARouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/vue-start' declare module '@tanstack/vue-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: benchmarks/ssr/vue/src/router.tsx ================================================ import { createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' export function getRouter() { return createRouter({ routeTree, defaultPreload: false, scrollRestoration: false, }) } declare module '@tanstack/vue-router' { interface Register { router: ReturnType } } ================================================ FILE: benchmarks/ssr/vue/src/routes/$a.$b.$c.$d.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b/$c/$d')({ component: LevelDComponent, }) function LevelDComponent() { return } ================================================ FILE: benchmarks/ssr/vue/src/routes/$a.$b.$c.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b/$c')({ component: LevelCComponent, }) function LevelCComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/vue/src/routes/$a.$b.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a/$b')({ component: LevelBComponent, }) function LevelBComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/vue/src/routes/$a.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' import { RouteWorkload } from '../workload' export const Route = createFileRoute('/$a')({ component: LevelAComponent, }) function LevelAComponent() { return ( <> ) } ================================================ FILE: benchmarks/ssr/vue/src/routes/__root.tsx ================================================ import { Body, HeadContent, Html, Outlet, Scripts, createRootRoute, } from '@tanstack/vue-router' export const Route = createRootRoute({ component: RootComponent, validateSearch: (s) => s as { q?: string }, }) function RootComponent() { return ( ) } ================================================ FILE: benchmarks/ssr/vue/src/workload.tsx ================================================ import * as Vue from 'vue' import { Link, useParams, useSearch } from '@tanstack/vue-router' const probes = Array.from({ length: 10 }, (_, index) => index) function runSelectorWork(input: string, salt: number) { let value = salt for (let index = 0; index < input.length; index++) { value = (value * 33 + input.charCodeAt(index) + index) >>> 0 } for (let index = 0; index < 16; index++) { value = (value ^ (value << 13)) >>> 0 value = (value ^ (value >> 17)) >>> 0 value = (value ^ (value << 5)) >>> 0 } return value } const ParamsProbe = Vue.defineComponent({ props: { salt: { type: Number, required: true, }, }, setup(props) { const params = useParams({ strict: false, select: (nextParams) => runSelectorWork( `${nextParams.a ?? ''}/${nextParams.b ?? ''}/${nextParams.c ?? ''}/${nextParams.d ?? ''}`, props.salt, ), }) return () => { void params.value return null } }, }) const SearchProbe = Vue.defineComponent({ props: { salt: { type: Number, required: true, }, }, setup(props) { const search = useSearch({ strict: false, select: (nextSearch) => runSelectorWork(String(nextSearch.q ?? ''), props.salt), }) return () => { void search.value return null } }, }) const LinkProbe = Vue.defineComponent({ props: { salt: { type: Number, required: true, }, }, setup(props) { const value = String((props.salt % 97) + 1) return () => ( Link ) }, }) export const RouteWorkload = Vue.defineComponent({ setup() { return () => ( <> {probes.map((probe) => ( ))} {probes.map((probe) => ( ))} {probes.map((probe) => ( ))} ) }, }) ================================================ FILE: benchmarks/ssr/vue/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "jsx": "preserve", "jsxImportSource": "vue", "types": ["node", "vite/client", "vitest/globals"] }, "include": [ "speed.bench.ts", "vite.config.ts", "../bench-utils.ts", "./src/**/*" ] } ================================================ FILE: benchmarks/ssr/vue/vite.config.ts ================================================ import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import codspeedPlugin from '@codspeed/vitest-plugin' import { tanstackStart } from '@tanstack/vue-start/plugin/vite' import vueJsx from '@vitejs/plugin-vue-jsx' const rootDir = fileURLToPath(new URL('.', import.meta.url)) export default defineConfig({ root: rootDir, plugins: [ !!(process.env.VITEST && process.env.WITH_INSTRUMENTATION) && codspeedPlugin(), tanstackStart({ srcDirectory: 'src', }), vueJsx(), ], build: { outDir: './dist', emptyOutDir: true, minify: false, }, test: { name: '@benchmarks/ssr (vue)', watch: false, environment: 'node', }, }) ================================================ FILE: docs/router/api/file-based-routing.md ================================================ --- title: File-Based Routing API Reference --- TanStack Router's file-based routing is quite flexible and can be configured to suit your project's needs. ## Configuration options The following options are available for configuring the file-based routing: - [`routesDirectory` (required)](#routesdirectory-required) - [`generatedRouteTree` (required)](#generatedroutetree-required) - [`virtualRouteConfig`](#virtualrouteconfig) - [`routeFilePrefix`](#routefileprefix) - [`routeFileIgnorePrefix`](#routefileignoreprefix) - [`routeFileIgnorePattern`](#routefileignorepattern) - [`indexToken`](#indextoken) - [`routeToken`](#routetoken) - [`quoteStyle`](#quotestyle) - [`semicolons`](#semicolons) - [`autoCodeSplitting`](#autocodesplitting) - [`disableTypes`](#disabletypes) - [`addExtensions`](#addextensions) - [`disableLogging`](#disablelogging) - [`routeTreeFileHeader`](#routetreefileheader) - [`routeTreeFileFooter`](#routetreefilefooter) - [`enableRouteTreeFormatting`](#enableroutetreeformatting) - [`tmpDir`](#tmpdir) > [!WARNING] > Do not set the `routeFilePrefix`, `routeFileIgnorePrefix`, or `routeFileIgnorePattern` options, to match any of the tokens used in the **File Naming Conventions** guide, or you may run into unexpected behavior. ### `routesDirectory` (required) This is the path to the directory where the route files are located, relative to the cwd (current working directory). By default, the value is set to the following and cannot be set to an empty `string` or `undefined`. ```txt ./src/routes ``` ### `generatedRouteTree` (required) This is the path to the file where the generated route tree will be saved, relative to the cwd (current working directory). By default, the value is set to the following and cannot be set to an empty `string` or `undefined`. ```txt ./src/routeTree.gen.ts ``` If the [`disableTypes`](#disabletypes) is set to `true`, the generated route tree will be saved with the `.js` extension instead of `.ts`. ### `virtualRouteConfig` This option is used to configure the Virtual File Routes feature. See the "Virtual File Routes" guide for more information. By default, this value is set to `undefined`. ### `routeFilePrefix` This option is used to identify route files in the route directory. This means that only files that start with this prefix will be considered for routing. By default, this value is set to `` and as such all files in the route directory will be considered for routing. ### `routeFileIgnorePrefix` This option is used to ignore specific files and directories in the route directory. This can be useful if you want to "opt-in" certain files or directories that you do not want to be considered for routing. By default, this value is set to `-`. When using this option, it allows you have structures like this where it let's you co-located related files that are not route files: ```txt src/routes ├── posts │ ├── -components // Ignored │ │ ├── Post.tsx │ ├── index.tsx │ ├── route.tsx ``` ### `routeFileIgnorePattern` This option is used to ignore specific files and directories in the route directory. It can be used in regular expression format. For example, `.((css|const).ts)|test-page` will ignore files / directories with names containing `.css.ts`, `.const.ts` or `test-page`. By default, this value is set to `undefined`. ### `routeToken` As mentioned in the Routing Concepts guide, a layout route is rendered at the specified path, and the child routes are rendered within the layout route. The `routeToken` is used to identify the layout route file in the route directory. By default, this value is set to `route`. > 🧠 the following filenames would equal the same runtime URL: ```txt src/routes/posts.tsx -> /posts src/routes/posts.route.tsx -> /posts src/routes/posts/route.tsx -> /posts ``` #### Using regex patterns for `routeToken` You can use a regular expression pattern instead of a literal string to match multiple layout route naming conventions. This is useful when you want more flexibility in your file naming. **In `tsr.config.json`** (JSON config), use an object with `regex` and optional `flags` properties: ```json { "routeToken": { "regex": "[a-z]+-layout", "flags": "i" } } ``` **In code** (inline config), you can use a native `RegExp`: ```ts { routeToken: /[a-z]+-layout/i } ``` With the regex pattern `[a-z]+-layout`, filenames like `dashboard.main-layout.tsx`, `posts.protected-layout.tsx`, or `admin.settings-layout.tsx` would all be recognized as layout routes. > [!NOTE] > The regex is matched against the **entire** final segment of the route path. For example, with `routeToken: { "regex": "[a-z]+-layout" }`: > > - `dashboard.main-layout.tsx` matches (`main-layout` is the full segment) > - `dashboard.my-layout-extra.tsx` does NOT match (the segment is `my-layout-extra`, not just `my-layout`) ### `indexToken` As mentioned in the Routing Concepts guide, an index route is a route that is matched when the URL path is exactly the same as the parent route. The `indexToken` is used to identify the index route file in the route directory. By default, this value is set to `index`. > 🧠 the following filenames would equal the same runtime URL: ```txt src/routes/posts.index.tsx -> /posts/ src/routes/posts/index.tsx -> /posts/ ``` #### Using regex patterns for `indexToken` Similar to `routeToken`, you can use a regular expression pattern for `indexToken` to match multiple index route naming conventions. **In `tsr.config.json`** (JSON config): ```json { "indexToken": { "regex": "[a-z]+-page" } } ``` **In code** (inline config): ```ts { indexToken: /[a-z]+-page/ } ``` With the regex pattern `[a-z]+-page`, filenames like `home-page.tsx`, `posts.list-page.tsx`, or `dashboard.overview-page.tsx` would all be recognized as index routes. #### Escaping regex tokens When using regex tokens, you can still escape a segment to prevent it from being treated as a token by wrapping it in square brackets. For example, if your `indexToken` is `{ "regex": "[a-z]+-page" }` and you want a literal route segment called `home-page`, name your file `[home-page].tsx`. ### `quoteStyle` When your generated route tree is generated and when you first create a new route, those files will be formatted with the quote style you specify here. By default, this value is set to `single`. > [!TIP] > You should ignore the path of your generated route tree file from your linter and formatter to avoid conflicts. ### `semicolons` When your generated route tree is generated and when you first create a new route, those files will be formatted with semicolons if this option is set to `true`. By default, this value is set to `false`. > [!TIP] > You should ignore the path of your generated route tree file from your linter and formatter to avoid conflicts. ### `autoCodeSplitting` This feature is only available if you are using the TanStack Router Bundler Plugin. This option is used to enable automatic code-splitting for non-critical route configuration items. See the "Automatic Code-Splitting" guide for more information. By default, this value is set to `false`. > [!IMPORTANT] > The next major release of TanStack Router (i.e. v2), will have this value defaulted to `true`. ### `disableTypes` This option is used to disable generating types for the route tree. If set to `true`, the generated route tree will not include any types and will be written as a `.js` file instead of a `.ts` file. By default, this value is set to `false`. ### `addExtensions` - Type: `boolean | string` - Default: `false` This option controls file extensions on import paths in the generated route tree. - `false` — Extensions are stripped from import paths (e.g. `'./routes/posts'`) - `true` — The original file extensions are kept (e.g. `'./routes/posts.tsx'`) - `string` — The original extension is replaced with the provided one (e.g. `'js'` produces `'./routes/posts.js'`) Passing a string is useful when your project uses ESM, since Node.js ESM requires `.js` extensions for imports even when the source files are `.ts` or `.tsx`. ```ts // Example: replace .tsx/.ts extensions with .js in generated imports TanStackRouterVite({ addExtensions: 'js', }) ``` ### `disableLogging` This option turns off the console logging for the route generation process. By default, this value is set to `false`. ### `routeTreeFileHeader` This option let's you prepend content to the start of the generated route tree file. By default, this value is set to: ```json [ "/* eslint-disable */", "// @ts-nocheck", "// noinspection JSUnusedGlobalSymbols" ] ``` ### `routeTreeFileFooter` This option let's you append content to the end of the generated route tree file. By default, this value is set to: ```json [] ``` ### `enableRouteTreeFormatting` This option turns on the formatting function on the generated route tree file, which can be time-consuming for large projects. By default, this value is set to `true`. ### `tmpDir` Atomic file writes (route files and the generated route tree file) are implemented by creating a temporary file first and then renaming it to their actual location. This config option allows to configure the path of the temp directory that will be used for creating those temporary files. If it is a relative path, it will be resolved to the current working directory. If this value is not set, `process.env.TSR_TMP_DIR` will be used. If `process.env.TSR_TMP_DIR` is not set, it will default to `.tanstack/tmp` relative to the current working directory. ================================================ FILE: docs/router/api/router/ActiveLinkOptionsType.md ================================================ --- 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. ```tsx type ActiveLinkOptions = LinkOptions & { activeProps?: | React.AnchorHTMLAttributes | (() => React.AnchorHTMLAttributes) inactiveProps?: | React.AnchorHTMLAttributes | (() => React.AnchorHTMLAttributes) } ``` ## ActiveLinkOptions properties The `ActiveLinkOptions` object accepts/contains the following properties: ### `activeProps` - `React.AnchorHTMLAttributes` - Optional - The props that will be applied to the anchor element when the link is active ### `inactiveProps` - Type: `React.AnchorHTMLAttributes` - Optional - The props that will be applied to the anchor element when the link is inactive ================================================ FILE: docs/router/api/router/AsyncRouteComponentType.md ================================================ --- id: AsyncRouteComponentType title: AsyncRouteComponent type --- The `AsyncRouteComponent` type is used to describe a code-split route component that can be preloaded using a `component.preload()` method. ```tsx type AsyncRouteComponent = SyncRouteComponent & { preload?: () => Promise } ``` ================================================ FILE: docs/router/api/router/FileRouteClass.md ================================================ --- id: FileRouteClass 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. 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. ## `FileRoute` constructor The `FileRoute` constructor accepts a single argument: the `path` of the file that the route will be generated for. ### Constructor options - Type: `string` literal - Required, but **automatically inserted and updated by the `tsr generate` and `tsr watch` commands**. - The full path of the file that the route will be generated from. ### Constructor returns - An instance of the `FileRoute` class that can be used to create a route. ## `FileRoute` methods The `FileRoute` class implements the following method(s): ### `.createRoute` method The `createRoute` method is a method that can be used to configure the file route instance. It accepts a single argument: the `options` that will be used to configure the file route instance. #### .createRoute options - Type: `Omit` - [`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. > ⚠️ Note: For `tsr generate` and `tsr watch` to work properly, the file route instance must be exported from the file using the `Route` identifier. ### Examples ```tsx import { FileRoute } from '@tanstack/react-router' export const Route = new FileRoute('/').createRoute({ loader: () => { return 'Hello World' }, component: IndexComponent, }) function IndexComponent() { const data = Route.useLoaderData() return
{data}
} ``` ================================================ FILE: docs/router/api/router/LinkOptionsType.md ================================================ --- 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. ```tsx type LinkOptions = NavigateOptions & { target?: HTMLAnchorElement['target'] activeOptions?: ActiveOptions preload?: false | 'intent' preloadDelay?: number disabled?: boolean } ``` ## LinkOptions properties The `LinkOptions` object accepts/contains the following properties: ### `target` - Type: `HTMLAnchorElement['target']` - Optional - The standard anchor tag target attribute ### `activeOptions` - Type: `ActiveOptions` - Optional - The options that will be used to determine if the link is active ### `preload` - 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. ### `preloadDelay` - Type: `number` - Optional - Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled. ### `disabled` - Type: `boolean` - Optional - If true, will render the link without the href attribute ================================================ FILE: docs/router/api/router/LinkPropsType.md ================================================ --- 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. ```tsx type LinkProps = ActiveLinkOptions & Omit, 'children'> & { children?: | React.ReactNode | ((state: { isActive: boolean }) => React.ReactNode) } ``` ## LinkProps properties - All of the props from [`ActiveLinkOptions`](./ActiveLinkOptionsType.md) - All of the props from `React.AnchorHTMLAttributes` #### `children` - Type: `React.ReactNode | ((state: { isActive: boolean }) => React.ReactNode)` - Optional - The children that will be rendered inside of the anchor element. If a function is provided, it will be called with an object that contains the `isActive` boolean value that can be used to determine if the link is active. ================================================ FILE: docs/router/api/router/MatchRouteOptionsType.md ================================================ --- id: MatchRouteOptionsType title: MatchRouteOptions type --- The `MatchRouteOptions` type is used to describe the options that can be used when matching a route. ```tsx interface MatchRouteOptions { pending?: boolean caseSensitive?: boolean /* @deprecated */ includeSearch?: boolean fuzzy?: boolean } ``` ## MatchRouteOptions properties The `MatchRouteOptions` type has the following properties: ### `pending` property - Type: `boolean` - Optional - If `true`, will match against pending location instead of the current location ### ~~`caseSensitive`~~ property (deprecated) - Type: `boolean` - Optional - If `true`, will match against the current location with case sensitivity - Declare case sensitivity in the route definition instead, or globally for all routes using the `caseSensitive` option on the router ### `includeSearch` property - Type: `boolean` - Optional - If `true`, will match against the current location's search params using a deep inclusive check. e.g. `{ a: 1 }` will match for a current location of `{ a: 1, b: 2 }` ### `fuzzy` property - Type: `boolean` - Optional - If `true`, will match against the current location using a fuzzy match. e.g. `/posts` will match for a current location of `/posts/123` ================================================ FILE: docs/router/api/router/NavigateOptionsType.md ================================================ --- id: NavigateOptionsType title: NavigateOptions type --- The `NavigateOptions` type is used to describe the options that can be used when describing a navigation action in TanStack Router. ```tsx type NavigateOptions = ToOptions & { replace?: boolean resetScroll?: boolean hashScrollIntoView?: boolean | ScrollIntoViewOptions viewTransition?: boolean | ViewTransitionOptions ignoreBlocker?: boolean reloadDocument?: boolean href?: string } ``` ## NavigateOptions properties The `NavigateOptions` object accepts the following properties: ### `replace` - Type: `boolean` - Optional - Defaults to `false`. - If `true`, the location will be committed to the browser history using `history.replace` instead of `history.push`. ### `resetScroll` - Type: `boolean` - Optional - Defaults to `true` so that the scroll position will be reset to 0,0 after the location is committed to the browser history. - If `false`, the scroll position will not be reset to 0,0 after the location is committed to history. ### `hashScrollIntoView` - Type: `boolean | ScrollIntoViewOptions` - Optional - Defaults to `true` so the element with an id matching the hash will be scrolled into view after the location is committed to history. - If `false`, the element with an id matching the hash will not be scrolled into view after the location is committed to history. - If an object is provided, it will be passed to the `scrollIntoView` method as options. - See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) for more information on `ScrollIntoViewOptions`. ### `viewTransition` - Type: `boolean | ViewTransitionOptions` - 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 determine 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 ### `ignoreBlocker` - Type: `boolean` - Optional - Defaults to `false`. - If `true`, navigation will ignore any blockers that might prevent it. ### `reloadDocument` - Type: `boolean` - Optional - Defaults to `false`. - If `true`, navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation. ### `href` - Type: `string` - 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) ================================================ FILE: docs/router/api/router/NotFoundErrorType.md ================================================ --- id: NotFoundErrorType title: NotFoundError --- The `NotFoundError` type is used to represent a not-found error in TanStack Router. ```tsx export type NotFoundError = { global?: boolean data?: any throw?: boolean routeId?: string headers?: HeadersInit } ``` ## NotFoundError properties The `NotFoundError` object accepts/contains the following properties: ### `global` property (⚠️ deprecated, use `routeId: rootRouteId` instead) - Type: `boolean` - Optional - `default: false` - If true, the not-found error will be handled by the `notFoundComponent` of the root route instead of bubbling up from the route that threw it. This has the same behavior as importing the root route and calling `RootRoute.notFound()`. ### `data` property - Type: `any` - Optional - Custom data that is passed into to `notFoundComponent` when the not-found error is handled ### `throw` property - Type: `boolean` - Optional - `default: false` - If provided, will throw the not-found object instead of returning it. This can be useful in places where `throwing` in a function might cause it to have a return type of `never`. In that case, you can use `notFound({ throw: true })` to throw the not-found object instead of returning it. ### `routeId` property - Type: `string` - Optional - The ID of the route that will attempt to handle the not-found error. If the route does not have a `notFoundComponent`, the error will bubble up to the parent route (and be handled by the root route if necessary). By default, TanStack Router will attempt to handle the not-found error with the route that threw it. ### `headers` property - Type: `HeadersInit` - Optional - HTTP headers to be included when the not-found error is handled on the server side. ================================================ FILE: docs/router/api/router/NotFoundRouteClass.md ================================================ --- id: NotFoundRouteClass 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. 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. ## Constructor options The `NotFoundRoute` constructor accepts an object as its only argument. - Type: ```tsx Omit< RouteOptions, | 'path' | 'id' | 'getParentRoute' | 'caseSensitive' | 'parseParams' | 'stringifyParams' > ``` - [RouteOptions](./RouteOptionsType.md) - Required - The options that will be used to configure the not found route instance. ## Examples ```tsx import { NotFoundRoute, createRouter } from '@tanstack/react-router' import { Route as rootRoute } from './routes/__root' import { routeTree } from './routeTree.gen' const notFoundRoute = new NotFoundRoute({ getParentRoute: () => rootRoute, component: () =>
Not found!!!
, }) const router = createRouter({ routeTree, notFoundRoute, }) // ... other code ``` ================================================ FILE: docs/router/api/router/ParsedHistoryStateType.md ================================================ --- id: ParsedHistoryStateType title: ParsedHistoryState type --- The `ParsedHistoryState` type represents a parsed state object. Additionally to `HistoryState`, it contains the index and the unique key of the route. ```tsx export type ParsedHistoryState = HistoryState & { key?: string // TODO: Remove in v2 - use __TSR_key instead __TSR_key?: string __TSR_index: number } ``` ================================================ FILE: docs/router/api/router/ParsedLocationType.md ================================================ --- id: ParsedLocationType title: ParsedLocation type --- The `ParsedLocation` type represents a parsed location in TanStack Router. It contains a lot of useful information about the current location, including the pathname, search params, hash, location state, and route masking information. ```tsx interface ParsedLocation { href: string pathname: string search: TFullSearchSchema searchStr: string state: ParsedHistoryState hash: string maskedLocation?: ParsedLocation unmaskOnReload?: boolean } ``` ================================================ FILE: docs/router/api/router/RedirectType.md ================================================ --- id: RedirectType title: Redirect type --- The `Redirect` type is used to represent a redirect action in TanStack Router. ```tsx export type Redirect = { statusCode?: number throw?: any headers?: HeadersInit } & NavigateOptions ``` - [`NavigateOptions`](./NavigateOptionsType.md) ## Redirect properties The `Redirect` object accepts/contains the following properties: ### `statusCode` property - Type: `number` - Optional - The HTTP status code to use when redirecting ### `throw` property - Type: `any` - Optional - If provided, will throw the redirect object instead of returning it. This can be useful in places where `throwing` in a function might cause it to have a return type of `never`. In that case, you can use `redirect({ throw: true })` to throw the redirect object instead of returning it. ### `headers` property - Type: `HeadersInit` - Optional - The HTTP headers to use when redirecting. ### Navigation Properties Since `Redirect` extends `NavigateOptions`, it also supports navigation properties: - **`to`**: Use for internal application routes (e.g., `/dashboard`, `../profile`) - **`href`**: Use for external URLs (e.g., `https://example.com`, `https://authprovider.com`) > **Important**: For external URLs, always use the `href` property instead of `to`. The `to` property is designed for internal navigation within your application. ================================================ FILE: docs/router/api/router/RegisterType.md ================================================ --- id: RegisterType title: Register type --- This type is used to register a route tree with a router instance. Doing so unlocks the full type safety of TanStack Router, including top-level exports from the `@tanstack/react-router` package. ```tsx export type Register = { // router: [Your router type here] } ``` To register a route tree with a router instance, use declaration merging to add the type of your router instance to the Register interface under the `router` property: ## Examples ```tsx const router = createRouter({ // ... }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } ``` ================================================ FILE: docs/router/api/router/RootRouteClass.md ================================================ --- id: RootRouteClass 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. 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. ## `RootRoute` constructor The `RootRoute` constructor accepts an object as its only argument. ### Constructor options The options that will be used to configure the root route instance. - Type: ```tsx Omit< RouteOptions, | 'path' | 'id' | 'getParentRoute' | 'caseSensitive' | 'parseParams' | 'stringifyParams' > ``` - [`RouteOptions`](./RouteOptionsType.md) - Optional ## Constructor returns A new [`Route`](./RouteType.md) instance. ## Examples ```tsx import { RootRoute, createRouter, Outlet } from '@tanstack/react-router' const rootRoute = new RootRoute({ component: () => , // ... root route options }) const routeTree = rootRoute.addChildren([ // ... other routes ]) const router = createRouter({ routeTree, }) ``` ================================================ FILE: docs/router/api/router/RouteApiClass.md ================================================ --- id: RouteApiClass 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. 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. ## Constructor options The `RouteApi` constructor accepts a single argument: the `options` that will be used to configure the `RouteApi` instance. ### `opts.routeId` option - Type: `string` - Required - The route ID to which the `RouteApi` instance will be bound ## Constructor returns - An instance of the [`RouteApi`](./RouteApiType.md) that is pre-bound to the route ID that it was called with. ## Examples ```tsx import { RouteApi } from '@tanstack/react-router' const routeApi = new RouteApi({ id: '/posts' }) export function PostsPage() { const posts = routeApi.useLoaderData() // ... } ``` ================================================ FILE: docs/router/api/router/RouteApiType.md ================================================ --- id: RouteApiType title: RouteApi Type --- The `RouteApi` describes an instance that provides type-safe versions 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. ## `RouteApi` properties and methods The `RouteApi` has the following properties and methods: ### `useMatch` method ```tsx useMatch(opts?: { select?: (match: TAllContext) => TSelected }): 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. - Options - `opts.select` - Optional - `(match: RouteMatch) => TSelected` - If supplied, this function will be called with the route match and the return value will be returned from `useMatch`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks. - `opts.structuralSharing` - 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. - 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`. ### `useRouteContext` method ```tsx useRouteContext(opts?: { select?: (search: TAllContext) => TSelected }): 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. - Options - `opts.select` - Optional - `(match: RouteContext) => TSelected` - If supplied, this function will be called with the route match and the return value will be returned from `useRouteContext`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `RouteContext` object or a loosened version of the `RouteContext` object if `opts.strict` is `false`. ### `useSearch` method ```tsx useSearch(opts?: { select?: (search: TFullSearchSchema) => TSelected }): 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. - Options - `opts.select` - Optional - `(match: TFullSearchSchema) => TSelected` - If supplied, this function will be called with the route match and the return value will be returned from `useSearch`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks. - `opts.structuralSharing` - 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. - 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`. ### `useParams` method ```tsx useParams(opts?: { select?: (params: TAllParams) => TSelected }): 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. - Options - `opts.select` - Optional - `(match: TAllParams) => TSelected` - If supplied, this function will be called with the route match and the return value will be returned from `useParams`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks. - `opts.structuralSharing` - 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. - 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`. ### `useLoaderData` method ```tsx useLoaderData(opts?: { select?: (search: TLoaderData) => TSelected }): 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. - Options - `opts.select` - Optional - `(match: TLoaderData) => TSelected` - If supplied, this function will be called with the route match and the return value will be returned from `useLoaderData`. This value will also be used to determine if the hook should re-render its parent component using shallow equality checks. - `opts.structuralSharing` - 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. - 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`. ### `useLoaderDeps` method ```tsx useLoaderDeps(opts?: { select?: (search: TLoaderDeps) => TSelected }): 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. - Options - `opts.select` - Optional - `(match: TLoaderDeps) => TSelected` - If supplied, this function will be called with the route match and the return value will be returned from `useLoaderDeps`. - `opts.structuralSharing` - 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. - Returns - If a `select` function is provided, the return value of the `select` function. - If no `select` function is provided, the `TLoaderDeps` object. ### `useNavigate` method ```tsx 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. ### `redirect` method ```tsx redirect(opts?: RedirectOptions): Redirect ``` - A type-safe version of the [`redirect`](./redirectFunction.md) function that is pre-bound to the route ID that the `RouteApi` instance was created with. - The `from` parameter is automatically set to the route ID, enabling type-safe relative redirects. - Options - All options from [`redirect`](./redirectFunction.md) except `from`, which is automatically provided. - Returns - A `Redirect` object that can be thrown from `beforeLoad` or `loader` callbacks. #### Example ```tsx import { getRouteApi } from '@tanstack/react-router' const routeApi = getRouteApi('/dashboard/settings') export const Route = createFileRoute('/dashboard/settings')({ beforeLoad: ({ context }) => { if (!context.user) { // Type-safe redirect - 'from' is automatically '/dashboard/settings' throw routeApi.redirect({ to: '../login', // Relative path to sibling route }) } }, }) ``` ================================================ FILE: docs/router/api/router/RouteClass.md ================================================ --- id: RouteClass 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. 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. ## `Route` constructor The `Route` constructor accepts an object as its only argument. ### Constructor options - Type: [`RouteOptions`](./RouteOptionsType.md) - Required - The options that will be used to configure the route instance ### Constructor returns A new [`Route`](./RouteType.md) instance. ## Examples ```tsx import { Route } from '@tanstack/react-router' import { rootRoute } from './__root' const indexRoute = new Route({ getParentRoute: () => rootRoute, path: '/', loader: () => { return 'Hello World' }, component: IndexComponent, }) function IndexComponent() { const data = indexRoute.useLoaderData() return
{data}
} ``` ================================================ FILE: docs/router/api/router/RouteMaskType.md ================================================ --- 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. ## RouteMask properties The `RouteMask` type accepts an object with the following properties: ### `...ToOptions` - Type: [`ToOptions`](./ToOptionsType.md) - Required - The options that will be used to configure the route mask ### `options.routeTree` - Type: `TRouteTree` - Required - The route tree that this route mask will support ### `options.unmaskOnReload` - Type: `boolean` - Optional - If `true`, the route mask will be removed when the page is reloaded ================================================ FILE: docs/router/api/router/RouteMatchType.md ================================================ --- id: RouteMatchType title: RouteMatch type --- The `RouteMatch` type represents a route match in TanStack Router. ```tsx interface RouteMatch { id: string routeId: string pathname: string params: Route['allParams'] status: 'pending' | 'success' | 'error' | 'redirected' | 'notFound' isFetching: false | 'beforeLoad' | 'loader' showPending: boolean error: unknown paramsError: unknown searchError: unknown updatedAt: number loaderData?: Route['loaderData'] context: Route['allContext'] search: Route['fullSearchSchema'] fetchedAt: number abortController: AbortController cause: 'enter' | 'stay' ssr?: boolean | 'data-only' } ``` ================================================ FILE: docs/router/api/router/RouteOptionsType.md ================================================ --- id: RouteOptionsType title: RouteOptions type --- The `RouteOptions` type is used to describe the options that can be used when creating a route. ## RouteOptions properties The `RouteOptions` type accepts an object with the following properties: ### `getParentRoute` method - Type: `() => TParentRoute` - Required - A function that returns the parent route of the route being created. This is required to provide full type safety to child route configurations and to ensure that the route tree is built correctly. ### `path` property - Type: `string` - Required, unless an `id` is provided to configure the route as a pathless layout route - The path segment that will be used to match the route. ### `id` property - Type: `string` - Optional, but required if a `path` is not provided - The unique identifier for the route if it is to be configured as a pathless layout route. If provided, the route will not match against the location pathname and its routes will be flattened into its parent route for matching. ### `component` property - Type: `RouteComponent` or `LazyRouteComponent` - Optional - Defaults to `` - The content to be rendered when the route is matched. ### `errorComponent` property - Type: `RouteComponent` or `LazyRouteComponent` - Optional - Defaults to `routerOptions.defaultErrorComponent` - The content to be rendered when the route encounters an error. ### `pendingComponent` property - Type: `RouteComponent` or `LazyRouteComponent` - Optional - Defaults to `routerOptions.defaultPendingComponent` - The content to be rendered if and when the route is pending and has reached its pendingMs threshold. ### `notFoundComponent` property - Type: `NotFoundRouteComponent` or `LazyRouteComponent` - Optional - Defaults to `routerOptions.defaultNotFoundComponent` - The content to be rendered when the route is not found. ### `validateSearch` method - Type: `(rawSearchParams: unknown) => TSearchSchema` - Optional - A function that will be called when this route is matched and passed the raw search params from the current location and return valid parsed search 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 search params and the return type will be inferred into the rest of the router. - Optionally, the parameter type can be tagged with the `SearchSchemaInput` type like this: `(searchParams: TSearchSchemaInput & SearchSchemaInput) => TSearchSchema`. If this tag is present, `TSearchSchemaInput` will be used to type the `search` property of `` and `navigate()` **instead of** `TSearchSchema`. The difference between `TSearchSchemaInput` and `TSearchSchema` can be useful, for example, to express optional search parameters. ### `search.middlewares` property - Type: `(({search: TSearchSchema, next: (newSearch: TSearchSchema) => TSearchSchema}) => TSearchSchema)[]` - Optional - 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, 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, use `params.stringify` instead) - Type: `(params: TParams) => Record` - Required if `parseParams` is provided - A function that will be called when this route's parsed params are being used to build a location. This function should return a valid object of `Record` mapping. ### `params.parse` method - 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. ### `params.stringify` method - Type: `(params: TParams) => Record` - A function that will be called when this route's parsed params are being used to build a location. This function should return a valid object of `Record` mapping. ### `beforeLoad` method - Type: ```tsx type beforeLoad = ( opts: RouteMatch & { search: TFullSearchSchema abortController: AbortController preload: boolean params: TAllParams context: TParentContext location: ParsedLocation navigate: NavigateFn // @deprecated buildLocation: BuildLocationFn cause: 'enter' | 'stay' }, ) => Promise | TRouteContext | void ``` - Optional - [`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). ### `loader` method - Type: ```tsx type loaderFn = ( opts: RouteMatch & { abortController: AbortController cause: 'preload' | 'enter' | 'stay' context: TAllContext deps: TLoaderDeps location: ParsedLocation params: TAllParams preload: boolean parentMatchPromise: Promise> navigate: NavigateFn // @deprecated route: AnyRoute }, ) => Promise | TLoaderData | void type loader = | loaderFn | { handler: loaderFn staleReloadMode?: 'background' | 'blocking' } ``` - Optional - [`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. - Use the object form to configure loader-specific behavior like `staleReloadMode`. - `staleReloadMode: 'background'` preserves stale-while-revalidate behavior for stale successful matches. - `staleReloadMode: 'blocking'` waits for the stale loader reload to complete before continuing. > 🚧 `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 - Type: ```tsx type loaderDeps = (opts: { search: TFullSearchSchema }) => Record ``` - Optional - A function that will be called before this route is matched to provide additional unique identification to the route match and serve as a dependency tracker for when the match should be reloaded. It should return any serializable value that can uniquely identify the route match from navigation to navigation. - By default, path params are already used to uniquely identify a route match, so it's unnecessary to return these here. - If your route match relies on search params for unique identification, it's required that you return them here so they can be made available in the `loader`'s `deps` argument. ### `staleTime` property - Type: `number` - Optional - Defaults to `routerOptions.defaultStaleTime`, which defaults to `0` - The amount of time in milliseconds that a route match's loader data will be considered fresh. If a route match is matched again within this time frame, its loader data will not be reloaded. ### `preloadStaleTime` property - Type: `number` - Optional - Defaults to `routerOptions.defaultPreloadStaleTime`, which defaults to `30_000` ms (30 seconds) - The amount of time in milliseconds that a route match's loader data will be considered fresh when preloading. If a route match is preloaded again within this time frame, its loader data will not be reloaded. If a route match is loaded (for navigation) within this time frame, the normal `staleTime` is used instead. ### `gcTime` property - Type: `number` - Optional - Defaults to `routerOptions.defaultGcTime`, which defaults to 30 minutes. - The amount of time in milliseconds that a route match's loader data will be kept in memory after a preload or it is no longer in use. ### `shouldReload` property - Type: `boolean | ((args: LoaderArgs) => boolean)` - Optional - If `false` or returns `false`, the route match's loader data will not be reloaded on subsequent matches. - If `true` or returns `true`, the route match's loader data will be reloaded on subsequent matches. - If `undefined` or returns `undefined`, the route match's loader data will adhere to the default stale-while-revalidate behavior. ### `caseSensitive` property - Type: `boolean` - Optional - If `true`, this route will be matched as case-sensitive. ### `wrapInSuspense` property - Type: `boolean` - Optional - If `true`, this route will be forcefully wrapped in a suspense boundary, regardless if a reason is found to do so from inspecting its provided components. ### `pendingMs` property - Type: `number` - Optional - Defaults to `routerOptions.defaultPendingMs`, which defaults to `1000` - The threshold in milliseconds that a route must be pending before its `pendingComponent` is shown. ### `pendingMinMs` property - Type: `number` - Optional - Defaults to `routerOptions.defaultPendingMinMs` which defaults to `500` - The minimum amount of time in milliseconds that the pending component will be shown for if it is shown. This is useful to prevent the pending component from flashing on the screen for a split second. ### `preloadMaxAge` property - Type: `number` - Optional - Defaults to `30_000` ms (30 seconds) - The maximum amount of time in milliseconds that a route's preloaded route data will be cached for. If a route is not matched within this time frame, its loader data will be discarded. ### `preSearchFilters` property (⚠️ deprecated, use `search.middlewares` instead) - Type: `((search: TFullSearchSchema) => TFullSearchSchema)[]` - Optional - An array of functions that will be called when generating any new links to this route or its grandchildren. - Each function will be called with the current search params and should return a new search params object that will be used to generate the link. - It has a `pre` prefix because it is called before the user-provided function that is passed to `navigate`/`Link` etc has a chance to modify the search params. ### `postSearchFilters` property (⚠️ deprecated, use `search.middlewares` instead) - Type: `((search: TFullSearchSchema) => TFullSearchSchema)[]` - Optional - An array of functions that will be called when generating any new links to this route or its grandchildren. - Each function will be called with the current search params and should return a new search params object that will be used to generate the link. - It has a `post` prefix because it is called after the user-provided function that is passed to `navigate`/`Link` etc has modified the search params. ### `onError` property - 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. ### `onEnter` property - Type: `(match: RouteMatch) => void` - Optional - A function that will be called when a route is matched and loaded after not being matched in the previous location. ### `onStay` property - Type: `(match: RouteMatch) => void` - Optional - A function that will be called when a route is matched and loaded after being matched in the previous location. ### `onLeave` property - Type: `(match: RouteMatch) => void` - Optional - A function that will be called when a route is no longer matched after being matched in the previous location. ### `onCatch` property - Type: `(error: Error, errorInfo: ErrorInfo) => void` - Optional - Defaults to `routerOptions.defaultOnCatch` - A function that will be called when errors are caught when the route encounters an error. ### `remountDeps` method - Type: ```tsx type remountDeps = (opts: RemountDepsOptions) => any interface RemountDepsOptions< in out TRouteId, in out TFullSearchSchema, in out TAllParams, in out TLoaderDeps, > { routeId: TRouteId search: TFullSearchSchema params: TAllParams loaderDeps: TLoaderDeps } ``` - Optional - A function that will be called to determine whether a route component shall be remounted after navigation. If this function returns a different value than previously, it will remount. - 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: If you want to configure to remount a route component upon `params` change, use: ```tsx remountDeps: ({ params }) => params ``` ### `headers` method - Type: ```tsx type headers = (opts: { matches: Array match: RouteMatch params: TAllParams loaderData?: TLoaderData }) => Promise> | Record ``` - Optional - Allows you to specify custom HTTP headers to be sent when this route is rendered during SSR. The function receives the current match context and should return a plain object of header name/value pairs. ### `head` method - Type: ```tsx type head = (ctx: { matches: Array match: RouteMatch params: TAllParams loaderData?: TLoaderData }) => | Promise<{ links?: RouteMatch['links'] scripts?: RouteMatch['headScripts'] meta?: RouteMatch['meta'] styles?: RouteMatch['styles'] }> | { links?: RouteMatch['links'] scripts?: RouteMatch['headScripts'] meta?: RouteMatch['meta'] styles?: RouteMatch['styles'] } ``` - Optional - Returns additional elements to inject into the document `` for this route. Use it to add route-level SEO metadata, preload links, inline styles, or custom scripts. ### `scripts` method - Type: ```tsx type scripts = (ctx: { matches: Array match: RouteMatch params: TAllParams loaderData?: TLoaderData }) => Promise | RouteMatch['scripts'] ``` - Optional - A shorthand helper to return only ` ================================================ FILE: e2e/react-router/basepath-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-basepath-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basepath-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basepath-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, basepath: '/app', }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basepath-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as ScrollErrorRouteImport } from './routes/scroll-error' import { Route as RedirectReloadRouteImport } from './routes/redirectReload' import { Route as RedirectRouteImport } from './routes/redirect' import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' const ScrollErrorRoute = ScrollErrorRouteImport.update({ id: '/scroll-error', path: '/scroll-error', getParentRoute: () => rootRouteImport, } as any) const RedirectReloadRoute = RedirectReloadRouteImport.update({ id: '/redirectReload', path: '/redirectReload', getParentRoute: () => rootRouteImport, } as any) const RedirectRoute = RedirectRouteImport.update({ id: '/redirect', path: '/redirect', getParentRoute: () => rootRouteImport, } as any) const AboutRoute = AboutRouteImport.update({ id: '/about', path: '/about', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/about': typeof AboutRoute '/redirect': typeof RedirectRoute '/redirectReload': typeof RedirectReloadRoute '/scroll-error': typeof ScrollErrorRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute '/redirect': typeof RedirectRoute '/redirectReload': typeof RedirectReloadRoute '/scroll-error': typeof ScrollErrorRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/about': typeof AboutRoute '/redirect': typeof RedirectRoute '/redirectReload': typeof RedirectReloadRoute '/scroll-error': typeof ScrollErrorRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/about' | '/redirect' | '/redirectReload' | '/scroll-error' fileRoutesByTo: FileRoutesByTo to: '/' | '/about' | '/redirect' | '/redirectReload' | '/scroll-error' id: | '__root__' | '/' | '/about' | '/redirect' | '/redirectReload' | '/scroll-error' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute AboutRoute: typeof AboutRoute RedirectRoute: typeof RedirectRoute RedirectReloadRoute: typeof RedirectReloadRoute ScrollErrorRoute: typeof ScrollErrorRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/scroll-error': { id: '/scroll-error' path: '/scroll-error' fullPath: '/scroll-error' preLoaderRoute: typeof ScrollErrorRouteImport parentRoute: typeof rootRouteImport } '/redirectReload': { id: '/redirectReload' path: '/redirectReload' fullPath: '/redirectReload' preLoaderRoute: typeof RedirectReloadRouteImport parentRoute: typeof rootRouteImport } '/redirect': { id: '/redirect' path: '/redirect' fullPath: '/redirect' preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } '/about': { id: '/about' path: '/about' fullPath: '/about' preLoaderRoute: typeof AboutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AboutRoute: AboutRoute, RedirectRoute: RedirectRoute, RedirectReloadRoute: RedirectReloadRoute, ScrollErrorRoute: ScrollErrorRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/basepath-file-based/src/routes/__root.tsx ================================================ import { createRootRoute } from '@tanstack/react-router' export const Route = createRootRoute() ================================================ FILE: e2e/react-router/basepath-file-based/src/routes/about.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/about')({ component: RouteComponent, }) function RouteComponent() { const navigate = Route.useNavigate() return (
) } ================================================ FILE: e2e/react-router/basepath-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: App, }) function App() { const navigate = Route.useNavigate() return (
{' '} {' '} {' '} {' '}
) } ================================================ FILE: e2e/react-router/basepath-file-based/src/routes/redirect.tsx ================================================ import { createFileRoute, redirect } from '@tanstack/react-router' export const Route = createFileRoute('/redirect')({ beforeLoad: () => { throw redirect({ to: '/about' }) }, component: RouteComponent, }) function RouteComponent() { return
Hello "/redirect"!
} ================================================ FILE: e2e/react-router/basepath-file-based/src/routes/redirectReload.tsx ================================================ import { createFileRoute, redirect } from '@tanstack/react-router' export const Route = createFileRoute('/redirectReload')({ beforeLoad: () => { throw redirect({ to: '/about', reloadDocument: true }) }, component: RouteComponent, }) function RouteComponent() { return
Hello "/redirectReload"!
} ================================================ FILE: e2e/react-router/basepath-file-based/src/routes/scroll-error.tsx ================================================ import { Link, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/scroll-error')({ component: ScrollErrorComponent, }) function ScrollErrorComponent() { return (
About

Scroll Error Test

) } ================================================ FILE: e2e/react-router/basepath-file-based/tests/reload-document.test.ts ================================================ import { expect, test } from '@playwright/test' test('navigate() respects basepath for when reloadDocument=true', async ({ page, }) => { await page.goto(`/app/`) await expect(page.getByTestId(`home-component`)).toBeInViewport() const aboutBtn = page.getByTestId(`to-about-btn`) await aboutBtn.click() await page.waitForURL('/app/about') await expect(page.getByTestId(`about-component`)).toBeInViewport() const homeBtn = page.getByTestId(`to-home-btn`) await homeBtn.click() await page.waitForURL('/app/') await expect(page.getByTestId(`home-component`)).toBeInViewport() }) test('redirect respects basepath', async ({ page }) => { await page.goto(`/app/`) await expect(page.getByTestId(`home-component`)).toBeInViewport() const redirectBtn = page.getByTestId(`to-redirect-btn`) await redirectBtn.click() await page.waitForURL('/app/about') await expect(page.getByTestId(`about-component`)).toBeInViewport() }) test('redirect respects basepath with reloadDocument = true on redirect', async ({ page, }) => { await page.goto(`/app/`) await expect(page.getByTestId(`home-component`)).toBeInViewport() const redirectBtn = page.getByTestId(`to-redirect-reload-btn`) await redirectBtn.click() await page.waitForURL('/app/about') await expect(page.getByTestId(`about-component`)).toBeInViewport() }) test('navigate() with href containing basepath', async ({ page }) => { await page.goto(`/app/`) await expect(page.getByTestId(`home-component`)).toBeInViewport() const aboutBtn = page.getByTestId(`to-about-href-with-basepath-btn`) await aboutBtn.click() // Should navigate to /app/about, NOT /app/app/about await page.waitForURL('/app/about') await expect(page.getByTestId(`about-component`)).toBeInViewport() }) test('navigate() with href containing basepath and reloadDocument=true', async ({ page, }) => { await page.goto(`/app/`) await expect(page.getByTestId(`home-component`)).toBeInViewport() const aboutBtn = page.getByTestId(`to-about-href-with-basepath-reload-btn`) await aboutBtn.click() // Should navigate to /app/about, NOT stay on current page await page.waitForURL('/app/about') await expect(page.getByTestId(`about-component`)).toBeInViewport() }) ================================================ FILE: e2e/react-router/basepath-file-based/tests/scroll-restoration-session-storage-error.test.ts ================================================ /* eslint-disable */ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' const trackConsole = (page: Page) => { const consoleWarnings: Array = [] page.on('console', (msg) => { if (msg.type() === 'warning') { consoleWarnings.push(msg.text()) } }) return consoleWarnings } test.describe('Scroll Restoration with Session Storage Error', () => { test('should not crash when sessionStorage.setItem throws an error', async ({ page, }) => { const consoleWarnings = trackConsole(page) await page.goto('/app/scroll-error') await page.waitForLoadState('networkidle') await page.evaluate(() => { sessionStorage.setItem = () => { throw new Error('Test Error') } }) await page.evaluate(() => window.scrollTo(0, 200)) await page.waitForTimeout(150) await page.click('a[href="/app/about"]') await page.waitForLoadState('networkidle') await page.goBack() await page.waitForLoadState('networkidle') expect( consoleWarnings.some((warning) => warning.includes( '[ts-router] Could not persist scroll restoration state to sessionStorage.', ), ), ).toBeTruthy() const heading = page.locator('h1:has-text("Scroll Error Test")') await expect(heading).toBeVisible() const scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).not.toBe(200) }) test('should surface warning when sessionStorage quota is exceeded', async ({ page, }) => { const consoleWarnings = trackConsole(page) await page.goto('/app/scroll-error') await page.waitForLoadState('networkidle') await page.evaluate(() => { let i = 0 const chunk = 'x'.repeat(32) try { while (true) { sessionStorage.setItem(`key_${i}`, chunk) i += 1 } } catch { console.log(`Stored ${i} keys in session storage`) } }) await page.evaluate(() => window.scrollTo(0, 200)) await page.waitForTimeout(150) await page.click('a[href="/app/about"]') await page.waitForLoadState('networkidle') await page.goBack() await page.waitForLoadState('networkidle') expect( consoleWarnings.some((warning) => warning.includes( '[ts-router] Could not persist scroll restoration state to sessionStorage.', ), ), ).toBeTruthy() const heading = page.locator('h1:has-text("Scroll Error Test")') await expect(heading).toBeVisible() const scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).not.toBe(200) }) }) ================================================ FILE: e2e/react-router/basepath-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basepath-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basepath-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basepath-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' // https://vitejs.dev/config/ export default defineConfig({ base: '/app/', plugins: [tanstackRouter({ target: 'react' }), react()], }) ================================================ FILE: e2e/react-router/basic/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic/package.json ================================================ { "name": "tanstack-router-e2e-react-basic", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic/src/main.tsx ================================================ import ReactDOM from 'react-dom/client' import { ErrorComponent, Link, Outlet, RouterProvider, createRootRoute, createRoute, createRouter, redirect, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' import { NotFoundError, fetchPost, fetchPosts } from './posts' import './styles.css' import type { ErrorComponentProps } from '@tanstack/react-router' const rootRoute = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} View Transition {' '} View Transition types {' '} Layout {' '} This Route Does Not Exist {' '}
Link in SVG
) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: IndexComponent, }) function IndexComponent() { return (

Welcome Home!

) } export const postsRoute = createRoute({ getParentRoute: () => rootRoute, path: 'posts', loader: () => fetchPosts(), }).lazy(() => import('./posts.lazy').then((d) => d.Route)) const postsIndexRoute = createRoute({ getParentRoute: () => postsRoute, path: '/', component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} const postRoute = createRoute({ getParentRoute: () => postsRoute, path: '$postId', errorComponent: PostErrorComponent, loader: ({ params }) => fetchPost(params.postId), component: PostComponent, }) function PostErrorComponent({ error }: ErrorComponentProps) { if (error instanceof NotFoundError) { return
{error.message}
} return } function PostComponent() { const post = postRoute.useLoaderData() return (

{post.title}


{post.body}
) } const layoutRoute = createRoute({ getParentRoute: () => rootRoute, id: '_layout', component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } const layout2Route = createRoute({ getParentRoute: () => layoutRoute, id: '_layout-2', component: Layout2Component, }) function Layout2Component() { return (
I'm a nested layout
Layout A Layout B
) } const layoutARoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-a', component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} const layoutBRoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-b', component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} const paramsPsRoute = createRoute({ getParentRoute: () => rootRoute, path: '/params-ps', }) const paramsPsIndexRoute = createRoute({ getParentRoute: () => paramsPsRoute, path: '/', component: function ParamsIndex() { return (

Named path params

  • /params-ps/named/$foo
  • /params-ps/named/{'prefix{$foo}'}
  • /params-ps/named/{'{$foo}suffix'}

Wildcard path params

  • /params-ps/wildcard/$
  • /params-ps/wildcard/{'prefix{$}'}
  • /params-ps/wildcard/{'{$}suffix'}
) }, }) const paramsPsNamedRoute = createRoute({ getParentRoute: () => paramsPsRoute, path: '/named', }) const paramsPsNamedIndexRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/', beforeLoad: () => { throw redirect({ to: '/params-ps' }) }, }) const paramsPsNamedFooRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/$foo', component: function ParamsNamedFoo() { const p = paramsPsNamedFooRoute.useParams() return (

ParamsNamedFoo

{JSON.stringify(p)}
) }, }) const paramsPsNamedFooPrefixRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/prefix{$foo}', component: function ParamsNamedFooMarkdown() { const p = paramsPsNamedFooPrefixRoute.useParams() return (

ParamsNamedFooPrefix

{JSON.stringify(p)}
) }, }) const paramsPsNamedFooSuffixRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/{$foo}suffix', component: function ParamsNamedFooSuffix() { const p = paramsPsNamedFooSuffixRoute.useParams() return (

ParamsNamedFooSuffix

{JSON.stringify(p)}
) }, }) const paramsPsWildcardRoute = createRoute({ getParentRoute: () => paramsPsRoute, path: '/wildcard', }) const paramsPsWildcardIndexRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: '/', beforeLoad: () => { throw redirect({ to: '/params-ps' }) }, }) const paramsPsWildcardSplatRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: '$', component: function ParamsWildcardSplat() { const p = paramsPsWildcardSplatRoute.useParams() return (

ParamsWildcardSplat

{JSON.stringify(p)}
) }, }) const paramsPsWildcardSplatPrefixRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: 'prefix{$}', component: function ParamsWildcardSplatPrefix() { const p = paramsPsWildcardSplatPrefixRoute.useParams() return (

ParamsWildcardSplatPrefix

{JSON.stringify(p)}
) }, }) const paramsPsWildcardSplatSuffixRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: '{$}suffix', component: function ParamsWildcardSplatSuffix() { const p = paramsPsWildcardSplatSuffixRoute.useParams() return (

ParamsWildcardSplatSuffix

{JSON.stringify(p)}
) }, }) const routeTree = rootRoute.addChildren([ postsRoute.addChildren([postRoute, postsIndexRoute]), layoutRoute.addChildren([ layout2Route.addChildren([layoutARoute, layoutBRoute]), ]), paramsPsRoute.addChildren([ paramsPsNamedRoute.addChildren([ paramsPsNamedFooPrefixRoute, paramsPsNamedFooSuffixRoute, paramsPsNamedFooRoute, paramsPsNamedIndexRoute, ]), paramsPsWildcardRoute.addChildren([ paramsPsWildcardSplatRoute, paramsPsWildcardSplatPrefixRoute, paramsPsWildcardSplatSuffixRoute, paramsPsWildcardIndexRoute, ]), paramsPsIndexRoute, ]), indexRoute, ]) // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic/src/posts.lazy.tsx ================================================ import * as React from 'react' import { Link, Outlet, createLazyRoute } from '@tanstack/react-router' export const Route = createLazyRoute('/posts')({ component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}
) } ================================================ FILE: e2e/react-router/basic/src/posts.ts ================================================ import axios from 'redaxios' export class NotFoundError extends Error {} type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } ================================================ FILE: e2e/react-router/basic/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test('Navigating to a post page with viewTransition', async ({ page }) => { await page.getByRole('link', { name: 'View Transition', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating to a post page with viewTransition types', async ({ page, }) => { await page.getByRole('link', { name: 'View Transition types' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Link in SVG does not trigger a full page reload', async ({ page }) => { let fullPageLoad = false page.on('domcontentloaded', () => { fullPageLoad = true }) await page.getByRole('link', { name: 'Open posts from SVG' }).click() const url = `http://localhost:${PORT}/posts` await page.waitForURL(url) expect(fullPageLoad).toBeFalsy() }) ================================================ FILE: e2e/react-router/basic/tests/params.spec.ts ================================================ import { expect, test } from '@playwright/test' test.describe('params operations + prefix/suffix', () => { test.beforeEach(async ({ page }) => { await page.goto('/params-ps') }) test.describe('named params', () => { const NAMED_PARAMS_PAIRS = [ // Test ID | Expected href { id: 'l-to-named-foo', pathname: '/params-ps/named/foo', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFoo', }, { id: 'l-to-named-prefixfoo', pathname: '/params-ps/named/prefixfoo', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFooPrefix', }, { id: 'l-to-named-foosuffix', pathname: '/params-ps/named/foosuffix', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFooSuffix', }, ] satisfies Array<{ id: string pathname: string params: Record destHeadingId: string }> test.describe('Link', () => { NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => { test(`interpolation for testid="${id}" has href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await expect(link).toHaveAttribute('href', pathname) }) }) NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => { test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await link.click() await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) }) }) }) NAMED_PARAMS_PAIRS.forEach(({ pathname, params, destHeadingId }) => { test(`on first-load to "${pathname}" has correct params`, async ({ page, }) => { await page.goto(pathname) await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) const headingEl = page.getByRole('heading', { name: destHeadingId }) await expect(headingEl).toBeVisible() const paramsEl = page.getByTestId('params-output') const paramsText = await paramsEl.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual(params) }) }) }) test.describe('wildcard param', () => { const WILDCARD_PARAM_PAIRS = [ // Test ID | Expected href { id: 'l-to-wildcard-foo', pathname: '/params-ps/wildcard/foo', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplat', }, { id: 'l-to-wildcard-prefixfoo', pathname: '/params-ps/wildcard/prefixfoo', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplatPrefix', }, { id: 'l-to-wildcard-foosuffix', pathname: '/params-ps/wildcard/foosuffix', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplatSuffix', }, ] satisfies Array<{ id: string pathname: string params: Record destHeadingId: string }> test.describe('Link', () => { WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => { test(`interpolation for testid="${id}" has href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await expect(link).toHaveAttribute('href', pathname) }) }) WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => { test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await link.click() await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) }) }) }) WILDCARD_PARAM_PAIRS.forEach(({ pathname, params, destHeadingId }) => { test(`on first-load to "${pathname}" has correct params`, async ({ page, }) => { await page.goto(pathname) await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) const headingEl = page.getByRole('heading', { name: destHeadingId }) await expect(headingEl).toBeVisible() const paramsEl = page.getByTestId('params-output') const paramsText = await paramsEl.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual(params) }) }) }) }) ================================================ FILE: e2e/react-router/basic/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "resolveJsonModule": true, "allowJs": true, "skipLibCheck": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), react()], }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-esbuild-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-esbuild-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-esbuild-file-based", "private": true, "type": "module", "scripts": { "dev": "esbuild src/main.tsx --serve=5601 --bundle --outfile=dist/main.js --watch --servedir=.", "build": "esbuild src/main.tsx --bundle --outfile=dist/main.js && tsc --noEmit", "serve": "esbuild src/main.tsx --bundle --outfile=dist/main.js --servedir=.", "start": "dev", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "esbuild": "^0.27.4" } } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `pnpm run build && pnpm run serve --serve=${PORT} --define:process.env.NODE_ENV=\\"test\\" --define:process.env.EXTERNAL_PORT="${EXTERNAL_PORT}"`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/esbuild.config.js ================================================ import { tanstackRouter } from '@tanstack/router-plugin/esbuild' export default { // ... plugins: [ tanstackRouter({ autoCodeSplitting: true, }), ], } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (process.env.NODE_ENV === 'test') { queryURL = `http://localhost:${process.env.EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routeTree.gen.ts ================================================ /* prettier-ignore-start */ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file is auto-generated by TanStack Router import { createFileRoute } from '@tanstack/react-router' // Import Routes import { Route as rootRoute } from './routes/__root' import { Route as PostsImport } from './routes/posts' import { Route as LayoutImport } from './routes/_layout' import { Route as IndexImport } from './routes/index' import { Route as PostsIndexImport } from './routes/posts.index' import { Route as PostsPostIdImport } from './routes/posts.$postId' import { Route as LayoutLayout2Import } from './routes/_layout/_layout-2' import { Route as groupLazyinsideImport } from './routes/(group)/lazyinside' import { Route as groupLayoutImport } from './routes/(group)/_layout' import { Route as LayoutLayout2LayoutBImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutAImport } from './routes/_layout/_layout-2/layout-a' import { Route as groupLayoutInsideImport } from './routes/(group)/_layout.inside' // Create Virtual Routes const groupImport = createFileRoute('/(group)')() // Create/Update Routes const groupRoute = groupImport.update({ id: '/(group)', getParentRoute: () => rootRoute, } as any) const PostsRoute = PostsImport.update({ path: '/posts', getParentRoute: () => rootRoute, } as any) const LayoutRoute = LayoutImport.update({ id: '/_layout', getParentRoute: () => rootRoute, } as any) const IndexRoute = IndexImport.update({ path: '/', getParentRoute: () => rootRoute, } as any) const PostsIndexRoute = PostsIndexImport.update({ path: '/', getParentRoute: () => PostsRoute, } as any) const PostsPostIdRoute = PostsPostIdImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2Import.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const groupLazyinsideRoute = groupLazyinsideImport .update({ path: '/lazyinside', getParentRoute: () => groupRoute, } as any) .lazy(() => import('./routes/(group)/lazyinside.lazy').then((d) => d.Route)) const groupLayoutRoute = groupLayoutImport.update({ id: '/_layout', getParentRoute: () => groupRoute, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBImport.update({ path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutAImport.update({ path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) const groupLayoutInsideRoute = groupLayoutInsideImport.update({ path: '/inside', getParentRoute: () => groupLayoutRoute, } as any) // Populate the FileRoutesByPath interface declare module '@tanstack/react-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexImport parentRoute: typeof rootRoute } '/_layout': { id: '/_layout' path: '' fullPath: '' preLoaderRoute: typeof LayoutImport parentRoute: typeof rootRoute } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsImport parentRoute: typeof rootRoute } '/(group)': { id: '/(group)' path: '/' fullPath: '/' preLoaderRoute: typeof groupImport parentRoute: typeof rootRoute } '/(group)/_layout': { id: '/(group)/_layout' path: '/' fullPath: '/' preLoaderRoute: typeof groupLayoutImport parentRoute: typeof groupRoute } '/(group)/lazyinside': { id: '/(group)/lazyinside' path: '/lazyinside' fullPath: '/lazyinside' preLoaderRoute: typeof groupLazyinsideImport parentRoute: typeof groupImport } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '' preLoaderRoute: typeof LayoutLayout2Import parentRoute: typeof LayoutImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdImport parentRoute: typeof PostsImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexImport parentRoute: typeof PostsImport } '/(group)/_layout/inside': { id: '/(group)/_layout/inside' path: '/inside' fullPath: '/inside' preLoaderRoute: typeof groupLayoutInsideImport parentRoute: typeof groupLayoutImport } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutAImport parentRoute: typeof LayoutLayout2Import } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBImport parentRoute: typeof LayoutLayout2Import } } } // Create and export the route tree interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) interface groupLayoutRouteChildren { groupLayoutInsideRoute: typeof groupLayoutInsideRoute } const groupLayoutRouteChildren: groupLayoutRouteChildren = { groupLayoutInsideRoute: groupLayoutInsideRoute, } const groupLayoutRouteWithChildren = groupLayoutRoute._addFileChildren( groupLayoutRouteChildren, ) interface groupRouteChildren { groupLayoutRoute: typeof groupLayoutRouteWithChildren groupLazyinsideRoute: typeof groupLazyinsideRoute } const groupRouteChildren: groupRouteChildren = { groupLayoutRoute: groupLayoutRouteWithChildren, groupLazyinsideRoute: groupLazyinsideRoute, } const groupRouteWithChildren = groupRoute._addFileChildren(groupRouteChildren) export interface FileRoutesByFullPath { '/': typeof groupLayoutRouteWithChildren '': typeof LayoutLayout2RouteWithChildren '/posts': typeof PostsRouteWithChildren '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/inside': typeof groupLayoutInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesByTo { '/': typeof groupLayoutRouteWithChildren '': typeof LayoutLayout2RouteWithChildren '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/inside': typeof groupLayoutInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesById { __root__: typeof rootRoute '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/posts': typeof PostsRouteWithChildren '/(group)': typeof groupRouteWithChildren '/(group)/_layout': typeof groupLayoutRouteWithChildren '/(group)/lazyinside': typeof groupLazyinsideRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/(group)/_layout/inside': typeof groupLayoutInsideRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '' | '/posts' | '/lazyinside' | '/posts/$postId' | '/posts/' | '/inside' | '/layout-a' | '/layout-b' fileRoutesByTo: FileRoutesByTo to: | '/' | '' | '/lazyinside' | '/posts/$postId' | '/posts' | '/inside' | '/layout-a' | '/layout-b' id: | '__root__' | '/' | '/_layout' | '/posts' | '/(group)' | '/(group)/_layout' | '/(group)/lazyinside' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/(group)/_layout/inside' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren PostsRoute: typeof PostsRouteWithChildren groupRoute: typeof groupRouteWithChildren } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, PostsRoute: PostsRouteWithChildren, groupRoute: groupRouteWithChildren, } export const routeTree = rootRoute ._addFileChildren(rootRouteChildren) ._addFileTypes() /* prettier-ignore-end */ /* ROUTE_MANIFEST_START { "routes": { "__root__": { "filePath": "__root.tsx", "children": [ "/", "/_layout", "/posts", "/(group)" ] }, "/": { "filePath": "index.tsx" }, "/_layout": { "filePath": "_layout.tsx", "children": [ "/_layout/_layout-2" ] }, "/posts": { "filePath": "posts.tsx", "children": [ "/posts/$postId", "/posts/" ] }, "/(group)": { "filePath": "(group)", "children": [ "/(group)/_layout", "/(group)/lazyinside" ] }, "/(group)/_layout": { "filePath": "(group)/_layout.tsx", "parent": "/(group)", "children": [ "/(group)/_layout/inside" ] }, "/(group)/lazyinside": { "filePath": "(group)/lazyinside.tsx", "parent": "/(group)" }, "/_layout/_layout-2": { "filePath": "_layout/_layout-2.tsx", "parent": "/_layout", "children": [ "/_layout/_layout-2/layout-a", "/_layout/_layout-2/layout-b" ] }, "/posts/$postId": { "filePath": "posts.$postId.tsx", "parent": "/posts" }, "/posts/": { "filePath": "posts.index.tsx", "parent": "/posts" }, "/(group)/_layout/inside": { "filePath": "(group)/_layout.inside.tsx", "parent": "/(group)/_layout" }, "/_layout/_layout-2/layout-a": { "filePath": "_layout/_layout-2/layout-a.tsx", "parent": "/_layout/_layout-2" }, "/_layout/_layout-2/layout-b": { "filePath": "_layout/_layout-2/layout-b.tsx", "parent": "/_layout/_layout-2" } } } ROUTE_MANIFEST_END */ ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/(group)/_layout.inside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' const routeApi = getRouteApi('/(group)/_layout/inside') export const Route = createFileRoute('/(group)/_layout/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: () => { const searchViaHook = useSearch({ from: '/(group)/_layout/inside' }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/(group)/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/(group)/_layout')({ component: () => ( <>
/(group)/_layout!
), }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/(group)/lazyinside.lazy.tsx ================================================ import { createLazyFileRoute, getRouteApi, useSearch, } from '@tanstack/react-router' const routeApi = getRouteApi('/(group)/lazyinside') export const Route = createLazyFileRoute('/(group)/lazyinside')({ component: () => { const searchViaHook = useSearch({ from: '/(group)/lazyinside' }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/(group)/lazyinside.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/lazyinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Inside Group {' '} Lazy Inside Group {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/index.tsx ================================================ import * as React from 'react' import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/posts.$postId.tsx ================================================ import * as React from 'react' import { ErrorComponent, createFileRoute } from '@tanstack/react-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/posts.index.tsx ================================================ import * as React from 'react' import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/basic-esbuild-file-based/src/routes/posts.tsx ================================================ import * as React from 'react' import { Link, Outlet, createFileRoute } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test('Navigating to a route inside a route group', async ({ page }) => { await page.getByTestId('link-to-route-inside-group').click() await expect(page.getByTestId('search-via-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-api')).toContainText('world') }) test('Navigating to a lazy route inside a route group', async ({ page }) => { await page.getByTestId('link-to-lazy-route-inside-group').click() await expect(page.getByTestId('search-via-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-api')).toContainText('world') }) ================================================ FILE: e2e/react-router/basic-esbuild-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-esbuild-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-file-based/index.html ================================================
================================================ FILE: e2e/react-router/basic-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "pnpm run test:e2e:default", "test:e2e:default": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "combinate": "^1.1.11", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` const command = `pnpm build && pnpm preview --port ${PORT}` console.info('Running with mode: ', process.env.MODE || 'default') /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', env: { MODE: process.env.MODE || '', VITE_MODE: process.env.MODE || '', VITE_NODE_ENV: 'test', VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), VITE_SERVER_PORT: String(PORT), PORT: String(PORT), }, }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-file-based/src/components/RenderCounter.tsx ================================================ import { useRef } from 'react' export const RenderCounter = () => { const renderCounter = useRef(0) renderCounter.current = renderCounter.current + 1 return <>{renderCounter.current} } ================================================ FILE: e2e/react-router/basic-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouteMask, createRouter, } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' const mask = createRouteMask({ routeTree, from: '/masks/admin/$userId', to: '/masks/public/$username', params: (prev) => ({ username: `user-${prev.userId}`, }), }) // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, routeMasks: [mask], }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/basic-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as RemountDepsRouteImport } from './routes/remountDeps' import { Route as PostsRouteImport } from './routes/posts' import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' import { Route as MasksRouteImport } from './routes/masks' import { Route as LazyErrorRouteImport } from './routes/lazy-error' import { Route as HoverPreloadHashRouteImport } from './routes/hover-preload-hash' import { Route as EditingBRouteImport } from './routes/editing-b' import { Route as EditingARouteImport } from './routes/editing-a' import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test' import { Route as AnchorRouteImport } from './routes/anchor' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as Char45824Char54620Char48124Char44397RouteRouteImport } from './routes/대한민국/route' import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' import { Route as PathlessLayoutRouteRouteImport } from './routes/pathless-layout/route' import { Route as NonNestedRouteRouteImport } from './routes/non-nested/route' import { Route as FullpathTestRouteRouteImport } from './routes/fullpath-test/route' import { Route as IndexRouteImport } from './routes/index' import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' import { Route as RelativeIndexRouteImport } from './routes/relative/index' import { Route as RedirectIndexRouteImport } from './routes/redirect/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as ParamsPsIndexRouteImport } from './routes/params-ps/index' import { Route as StructuralSharingEnabledRouteImport } from './routes/structural-sharing.$enabled' import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' import { Route as groupInsideRouteImport } from './routes/(group)/inside' import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route' import { Route as RelativeLinkRouteRouteImport } from './routes/relative/link/route' import { Route as PathlessLayoutLayoutRouteRouteImport } from './routes/pathless-layout/_layout/route' import { Route as ParamsPsStrictFalseRouteRouteImport } from './routes/params-ps/strict-false/route' import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route' import { Route as NonNestedSuffixRouteRouteImport } from './routes/non-nested/suffix/route' import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/prefix/route' import { Route as NonNestedPathRouteRouteImport } from './routes/non-nested/path/route' import { Route as NonNestedNamedRouteRouteImport } from './routes/non-nested/named/route' import { Route as NonNestedDeepRouteRouteImport } from './routes/non-nested/deep/route' import { Route as FullpathTestLayoutRouteRouteImport } from './routes/fullpath-test/_layout/route' import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' import { Route as PathlessLayoutLayoutIndexRouteImport } from './routes/pathless-layout/_layout/index' import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' import { Route as ParamsPsNamedIndexRouteImport } from './routes/params-ps/named/index' import { Route as FullpathTestLayoutIndexRouteImport } from './routes/fullpath-test/_layout/index' import { Route as Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport } from './routes/대한민국/🚀.$id' import { Route as Char45824Char54620Char48124Char44397WildcardSplatRouteImport } from './routes/대한민국/wildcard.$' import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' import { Route as RelativeUseNavigateRelativeUseNavigateARouteImport } from './routes/relative/useNavigate/relative-useNavigate-a' import { Route as RelativeLinkRelativeLinkBRouteImport } from './routes/relative/link/relative-link-b' import { Route as RelativeLinkRelativeLinkARouteImport } from './routes/relative/link/relative-link-a' import { Route as RedirectPreloadThirdRouteImport } from './routes/redirect/preload/third' import { Route as RedirectPreloadSecondRouteImport } from './routes/redirect/preload/second' import { Route as RedirectPreloadFirstRouteImport } from './routes/redirect/preload/first' import { Route as RedirectTargetViaRouteApiRedirectLoaderRouteImport } from './routes/redirect/$target/via-routeApi-redirect-loader' import { Route as RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport } from './routes/redirect/$target/via-routeApi-redirect-beforeLoad' import { Route as RedirectTargetViaRouteRedirectLoaderRouteImport } from './routes/redirect/$target/via-route-redirect-loader' import { Route as RedirectTargetViaRouteRedirectBeforeLoadRouteImport } from './routes/redirect/$target/via-route-redirect-beforeLoad' import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' import { Route as RedirectTargetDestinationRouteImport } from './routes/redirect/$target/destination' import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' import { Route as PathlessLayoutLayoutChildRouteImport } from './routes/pathless-layout/_layout/child' import { Route as ParamsSingleValueRouteImport } from './routes/params.single.$value' import { Route as ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport } from './routes/params-ps/wildcard/{$}suffix@대' import { Route as ParamsPsWildcardChar123Char125suffixRouteImport } from './routes/params-ps/wildcard/{$}suffix' import { Route as ParamsPsWildcardPrefixChar123Char125RouteImport } from './routes/params-ps/wildcard/prefix{$}' import { Route as ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport } from './routes/params-ps/wildcard/prefix@대{$}' import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wildcard/$' import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix' import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}' import { Route as MasksPublicUsernameRouteImport } from './routes/masks.public.$username' import { Route as MasksAdminUserIdRouteImport } from './routes/masks.admin.$userId' import { Route as FullpathTestLayoutIdRouteImport } from './routes/fullpath-test/_layout/$id' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' import { Route as ParamsPsStrictFalseVersionRouteRouteImport } from './routes/params-ps/strict-false/$version.route' import { Route as ParamsPsNonNestedFooRouteRouteImport } from './routes/params-ps/non-nested/$foo_/route' import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route' import { Route as NonNestedSuffixChar123bazChar125suffixRouteRouteImport } from './routes/non-nested/suffix/{$baz}suffix.route' import { Route as NonNestedPrefixPrefixChar123bazChar125RouteRouteImport } from './routes/non-nested/prefix/prefix{$baz}.route' import { Route as NonNestedPathBazRouteRouteImport } from './routes/non-nested/path/baz.route' import { Route as NonNestedNamedBazRouteRouteImport } from './routes/non-nested/named/$baz.route' import { Route as NonNestedDeepBazRouteRouteImport } from './routes/non-nested/deep/$baz.route' import { Route as RelativeUseNavigateWithSearchIndexRouteImport } from './routes/relative/useNavigate/with-search/index' import { Route as RelativeUseNavigatePathIndexRouteImport } from './routes/relative/useNavigate/path/index' import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/relative/useNavigate/nested/index' import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' import { Route as NonNestedSuffixChar123bazChar125suffixIndexRouteImport } from './routes/non-nested/suffix/{$baz}suffix.index' import { Route as NonNestedPrefixPrefixChar123bazChar125IndexRouteImport } from './routes/non-nested/prefix/prefix{$baz}.index' import { Route as NonNestedPathBazIndexRouteImport } from './routes/non-nested/path/baz.index' import { Route as NonNestedNamedBazIndexRouteImport } from './routes/non-nested/named/$baz.index' import { Route as NonNestedDeepBazIndexRouteImport } from './routes/non-nested/deep/$baz.index' import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' import { Route as NonNestedSuffixChar123bazChar125suffixBarRouteImport } from './routes/non-nested/suffix/{$baz}suffix_.bar' import { Route as NonNestedSuffixChar123bazChar125suffixFooRouteImport } from './routes/non-nested/suffix/{$baz}suffix.foo' import { Route as NonNestedPrefixPrefixChar123bazChar125BarRouteImport } from './routes/non-nested/prefix/prefix{$baz}_.bar' import { Route as NonNestedPrefixPrefixChar123bazChar125FooRouteImport } from './routes/non-nested/prefix/prefix{$baz}.foo' import { Route as NonNestedPathBazBarRouteImport } from './routes/non-nested/path/baz_.bar' import { Route as NonNestedPathBazFooRouteImport } from './routes/non-nested/path/baz.foo' import { Route as NonNestedNamedBazBarRouteImport } from './routes/non-nested/named/$baz_.bar' import { Route as NonNestedNamedBazFooRouteImport } from './routes/non-nested/named/$baz.foo' import { Route as ParamsPsNamedFooBarRouteRouteImport } from './routes/params-ps/named/$foo/$bar.route' import { Route as NonNestedDeepBazBarRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.route' import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' import { Route as NonNestedDeepBazBarIndexRouteImport } from './routes/non-nested/deep/$baz_.bar.index' import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' import { Route as NonNestedDeepBazBarQuxRouteImport } from './routes/non-nested/deep/$baz_.bar_.qux' import { Route as NonNestedDeepBazBarFooRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo.route' import { Route as NonNestedDeepBazBarFooIndexRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo.index' import { Route as NonNestedDeepBazBarFooQuxRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo_.qux' const RemountDepsRoute = RemountDepsRouteImport.update({ id: '/remountDeps', path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ id: '/notRemountDeps', path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) const MasksRoute = MasksRouteImport.update({ id: '/masks', path: '/masks', getParentRoute: () => rootRouteImport, } as any) const LazyErrorRoute = LazyErrorRouteImport.update({ id: '/lazy-error', path: '/lazy-error', getParentRoute: () => rootRouteImport, } as any).lazy(() => import('./routes/lazy-error.lazy').then((d) => d.Route)) const HoverPreloadHashRoute = HoverPreloadHashRouteImport.update({ id: '/hover-preload-hash', path: '/hover-preload-hash', getParentRoute: () => rootRouteImport, } as any) const EditingBRoute = EditingBRouteImport.update({ id: '/editing-b', path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) const EditingARoute = EditingARouteImport.update({ id: '/editing-a', path: '/editing-a', getParentRoute: () => rootRouteImport, } as any) const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({ id: '/component-types-test', path: '/component-types-test', getParentRoute: () => rootRouteImport, } as any) const AnchorRoute = AnchorRouteImport.update({ id: '/anchor', path: '/anchor', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const Char45824Char54620Char48124Char44397RouteRoute = Char45824Char54620Char48124Char44397RouteRouteImport.update({ id: '/대한민국', path: '/대한민국', getParentRoute: () => rootRouteImport, } as any) const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ id: '/search-params', path: '/search-params', getParentRoute: () => rootRouteImport, } as any) const PathlessLayoutRouteRoute = PathlessLayoutRouteRouteImport.update({ id: '/pathless-layout', path: '/pathless-layout', getParentRoute: () => rootRouteImport, } as any) const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ id: '/non-nested', path: '/non-nested', getParentRoute: () => rootRouteImport, } as any) const FullpathTestRouteRoute = FullpathTestRouteRouteImport.update({ id: '/fullpath-test', path: '/fullpath-test', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => SearchParamsRouteRoute, } as any) const RelativeIndexRoute = RelativeIndexRouteImport.update({ id: '/relative/', path: '/relative/', getParentRoute: () => rootRouteImport, } as any) const RedirectIndexRoute = RedirectIndexRouteImport.update({ id: '/redirect/', path: '/redirect/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const ParamsPsIndexRoute = ParamsPsIndexRouteImport.update({ id: '/params-ps/', path: '/params-ps/', getParentRoute: () => rootRouteImport, } as any) const StructuralSharingEnabledRoute = StructuralSharingEnabledRouteImport.update({ id: '/structural-sharing/$enabled', path: '/structural-sharing/$enabled', getParentRoute: () => rootRouteImport, } as any) const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ id: '/default', path: '/default', getParentRoute: () => SearchParamsRouteRoute, } as any) const RedirectTargetRoute = RedirectTargetRouteImport.update({ id: '/redirect/$target', path: '/redirect/$target', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const groupLazyinsideRoute = groupLazyinsideRouteImport .update({ id: '/(group)/lazyinside', path: '/lazyinside', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(group)/lazyinside.lazy').then((d) => d.Route)) const groupInsideRoute = groupInsideRouteImport.update({ id: '/(group)/inside', path: '/inside', getParentRoute: () => rootRouteImport, } as any) const groupLayoutRoute = groupLayoutRouteImport.update({ id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport.update({ id: '/(another-group)/onlyrouteinside', path: '/onlyrouteinside', getParentRoute: () => rootRouteImport, } as any) const RelativeUseNavigateRouteRoute = RelativeUseNavigateRouteRouteImport.update({ id: '/relative/useNavigate', path: '/relative/useNavigate', getParentRoute: () => rootRouteImport, } as any) const RelativeLinkRouteRoute = RelativeLinkRouteRouteImport.update({ id: '/relative/link', path: '/relative/link', getParentRoute: () => rootRouteImport, } as any) const PathlessLayoutLayoutRouteRoute = PathlessLayoutLayoutRouteRouteImport.update({ id: '/_layout', getParentRoute: () => PathlessLayoutRouteRoute, } as any) const ParamsPsStrictFalseRouteRoute = ParamsPsStrictFalseRouteRouteImport.update({ id: '/params-ps/strict-false', path: '/params-ps/strict-false', getParentRoute: () => rootRouteImport, } as any) const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({ id: '/params-ps/non-nested', path: '/params-ps/non-nested', getParentRoute: () => rootRouteImport, } as any) const NonNestedSuffixRouteRoute = NonNestedSuffixRouteRouteImport.update({ id: '/suffix', path: '/suffix', getParentRoute: () => NonNestedRouteRoute, } as any) const NonNestedPrefixRouteRoute = NonNestedPrefixRouteRouteImport.update({ id: '/prefix', path: '/prefix', getParentRoute: () => NonNestedRouteRoute, } as any) const NonNestedPathRouteRoute = NonNestedPathRouteRouteImport.update({ id: '/path', path: '/path', getParentRoute: () => NonNestedRouteRoute, } as any) const NonNestedNamedRouteRoute = NonNestedNamedRouteRouteImport.update({ id: '/named', path: '/named', getParentRoute: () => NonNestedRouteRoute, } as any) const NonNestedDeepRouteRoute = NonNestedDeepRouteRouteImport.update({ id: '/deep', path: '/deep', getParentRoute: () => NonNestedRouteRoute, } as any) const FullpathTestLayoutRouteRoute = FullpathTestLayoutRouteRouteImport.update({ id: '/_layout', getParentRoute: () => FullpathTestRouteRoute, } as any) const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => RedirectTargetRoute, } as any) const PathlessLayoutLayoutIndexRoute = PathlessLayoutLayoutIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PathlessLayoutLayoutRouteRoute, } as any) const ParamsPsWildcardIndexRoute = ParamsPsWildcardIndexRouteImport.update({ id: '/params-ps/wildcard/', path: '/params-ps/wildcard/', getParentRoute: () => rootRouteImport, } as any) const ParamsPsNamedIndexRoute = ParamsPsNamedIndexRouteImport.update({ id: '/params-ps/named/', path: '/params-ps/named/', getParentRoute: () => rootRouteImport, } as any) const FullpathTestLayoutIndexRoute = FullpathTestLayoutIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => FullpathTestLayoutRouteRoute, } as any) const Char45824Char54620Char48124Char44397Char55357Char56960IdRoute = Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport.update({ id: '/🚀/$id', path: '/🚀/$id', getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, } as any) const Char45824Char54620Char48124Char44397WildcardSplatRoute = Char45824Char54620Char48124Char44397WildcardSplatRouteImport.update({ id: '/wildcard/$', path: '/wildcard/$', getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, } as any) const RelativeUseNavigateRelativeUseNavigateBRoute = RelativeUseNavigateRelativeUseNavigateBRouteImport.update({ id: '/relative-useNavigate-b', path: '/relative-useNavigate-b', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeUseNavigateRelativeUseNavigateARoute = RelativeUseNavigateRelativeUseNavigateARouteImport.update({ id: '/relative-useNavigate-a', path: '/relative-useNavigate-a', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeLinkRelativeLinkBRoute = RelativeLinkRelativeLinkBRouteImport.update({ id: '/relative-link-b', path: '/relative-link-b', getParentRoute: () => RelativeLinkRouteRoute, } as any) const RelativeLinkRelativeLinkARoute = RelativeLinkRelativeLinkARouteImport.update({ id: '/relative-link-a', path: '/relative-link-a', getParentRoute: () => RelativeLinkRouteRoute, } as any) const RedirectPreloadThirdRoute = RedirectPreloadThirdRouteImport.update({ id: '/redirect/preload/third', path: '/redirect/preload/third', getParentRoute: () => rootRouteImport, } as any) const RedirectPreloadSecondRoute = RedirectPreloadSecondRouteImport.update({ id: '/redirect/preload/second', path: '/redirect/preload/second', getParentRoute: () => rootRouteImport, } as any) const RedirectPreloadFirstRoute = RedirectPreloadFirstRouteImport.update({ id: '/redirect/preload/first', path: '/redirect/preload/first', getParentRoute: () => rootRouteImport, } as any) const RedirectTargetViaRouteApiRedirectLoaderRoute = RedirectTargetViaRouteApiRedirectLoaderRouteImport.update({ id: '/via-routeApi-redirect-loader', path: '/via-routeApi-redirect-loader', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaRouteApiRedirectBeforeLoadRoute = RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport.update({ id: '/via-routeApi-redirect-beforeLoad', path: '/via-routeApi-redirect-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaRouteRedirectLoaderRoute = RedirectTargetViaRouteRedirectLoaderRouteImport.update({ id: '/via-route-redirect-loader', path: '/via-route-redirect-loader', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaRouteRedirectBeforeLoadRoute = RedirectTargetViaRouteRedirectBeforeLoadRouteImport.update({ id: '/via-route-redirect-beforeLoad', path: '/via-route-redirect-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ id: '/via-loader', path: '/via-loader', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaBeforeLoadRoute = RedirectTargetViaBeforeLoadRouteImport.update({ id: '/via-beforeLoad', path: '/via-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetDestinationRoute = RedirectTargetDestinationRouteImport.update({ id: '/destination', path: '/destination', getParentRoute: () => RedirectTargetRoute, } as any) const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ id: '/posts_/$postId/edit', path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any) const PathlessLayoutLayoutChildRoute = PathlessLayoutLayoutChildRouteImport.update({ id: '/child', path: '/child', getParentRoute: () => PathlessLayoutLayoutRouteRoute, } as any) const ParamsSingleValueRoute = ParamsSingleValueRouteImport.update({ id: '/params/single/$value', path: '/params/single/$value', getParentRoute: () => rootRouteImport, } as any) const ParamsPsWildcardChar123Char125suffixAtChar45824Route = ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport.update({ id: '/params-ps/wildcard/{$}suffix@대', path: '/params-ps/wildcard/{$}suffix@대', getParentRoute: () => rootRouteImport, } as any) const ParamsPsWildcardChar123Char125suffixRoute = ParamsPsWildcardChar123Char125suffixRouteImport.update({ id: '/params-ps/wildcard/{$}suffix', path: '/params-ps/wildcard/{$}suffix', getParentRoute: () => rootRouteImport, } as any) const ParamsPsWildcardPrefixChar123Char125Route = ParamsPsWildcardPrefixChar123Char125RouteImport.update({ id: '/params-ps/wildcard/prefix{$}', path: '/params-ps/wildcard/prefix{$}', getParentRoute: () => rootRouteImport, } as any) const ParamsPsWildcardPrefixAtChar45824Char123Char125Route = ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport.update({ id: '/params-ps/wildcard/prefix@대{$}', path: '/params-ps/wildcard/prefix@대{$}', getParentRoute: () => rootRouteImport, } as any) const ParamsPsWildcardSplatRoute = ParamsPsWildcardSplatRouteImport.update({ id: '/params-ps/wildcard/$', path: '/params-ps/wildcard/$', getParentRoute: () => rootRouteImport, } as any) const ParamsPsNamedChar123fooChar125suffixRoute = ParamsPsNamedChar123fooChar125suffixRouteImport.update({ id: '/params-ps/named/{$foo}suffix', path: '/params-ps/named/{$foo}suffix', getParentRoute: () => rootRouteImport, } as any) const ParamsPsNamedPrefixChar123fooChar125Route = ParamsPsNamedPrefixChar123fooChar125RouteImport.update({ id: '/params-ps/named/prefix{$foo}', path: '/params-ps/named/prefix{$foo}', getParentRoute: () => rootRouteImport, } as any) const MasksPublicUsernameRoute = MasksPublicUsernameRouteImport.update({ id: '/public/$username', path: '/public/$username', getParentRoute: () => MasksRoute, } as any) const MasksAdminUserIdRoute = MasksAdminUserIdRouteImport.update({ id: '/admin/$userId', path: '/admin/$userId', getParentRoute: () => MasksRoute, } as any) const FullpathTestLayoutIdRoute = FullpathTestLayoutIdRouteImport.update({ id: '/$id', path: '/$id', getParentRoute: () => FullpathTestLayoutRouteRoute, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ id: '/(group)/subfolder/inside', path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ id: '/insidelayout', path: '/insidelayout', getParentRoute: () => groupLayoutRoute, } as any) const ParamsPsStrictFalseVersionRouteRoute = ParamsPsStrictFalseVersionRouteRouteImport.update({ id: '/$version', path: '/$version', getParentRoute: () => ParamsPsStrictFalseRouteRoute, } as any) const ParamsPsNonNestedFooRouteRoute = ParamsPsNonNestedFooRouteRouteImport.update({ id: '/$foo_', path: '/$foo', getParentRoute: () => ParamsPsNonNestedRouteRoute, } as any) const ParamsPsNamedFooRouteRoute = ParamsPsNamedFooRouteRouteImport.update({ id: '/params-ps/named/$foo', path: '/params-ps/named/$foo', getParentRoute: () => rootRouteImport, } as any) const NonNestedSuffixChar123bazChar125suffixRouteRoute = NonNestedSuffixChar123bazChar125suffixRouteRouteImport.update({ id: '/{$baz}suffix', path: '/{$baz}suffix', getParentRoute: () => NonNestedSuffixRouteRoute, } as any) const NonNestedPrefixPrefixChar123bazChar125RouteRoute = NonNestedPrefixPrefixChar123bazChar125RouteRouteImport.update({ id: '/prefix{$baz}', path: '/prefix{$baz}', getParentRoute: () => NonNestedPrefixRouteRoute, } as any) const NonNestedPathBazRouteRoute = NonNestedPathBazRouteRouteImport.update({ id: '/baz', path: '/baz', getParentRoute: () => NonNestedPathRouteRoute, } as any) const NonNestedNamedBazRouteRoute = NonNestedNamedBazRouteRouteImport.update({ id: '/$baz', path: '/$baz', getParentRoute: () => NonNestedNamedRouteRoute, } as any) const NonNestedDeepBazRouteRoute = NonNestedDeepBazRouteRouteImport.update({ id: '/$baz', path: '/$baz', getParentRoute: () => NonNestedDeepRouteRoute, } as any) const RelativeUseNavigateWithSearchIndexRoute = RelativeUseNavigateWithSearchIndexRouteImport.update({ id: '/with-search/', path: '/with-search/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeUseNavigatePathIndexRoute = RelativeUseNavigatePathIndexRouteImport.update({ id: '/path/', path: '/path/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeUseNavigateNestedIndexRoute = RelativeUseNavigateNestedIndexRouteImport.update({ id: '/nested/', path: '/nested/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeLinkWithSearchIndexRoute = RelativeLinkWithSearchIndexRouteImport.update({ id: '/with-search/', path: '/with-search/', getParentRoute: () => RelativeLinkRouteRoute, } as any) const RelativeLinkPathIndexRoute = RelativeLinkPathIndexRouteImport.update({ id: '/path/', path: '/path/', getParentRoute: () => RelativeLinkRouteRoute, } as any) const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ id: '/nested/', path: '/nested/', getParentRoute: () => RelativeLinkRouteRoute, } as any) const NonNestedSuffixChar123bazChar125suffixIndexRoute = NonNestedSuffixChar123bazChar125suffixIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, } as any) const NonNestedPrefixPrefixChar123bazChar125IndexRoute = NonNestedPrefixPrefixChar123bazChar125IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, } as any) const NonNestedPathBazIndexRoute = NonNestedPathBazIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedPathBazRouteRoute, } as any) const NonNestedNamedBazIndexRoute = NonNestedNamedBazIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedNamedBazRouteRoute, } as any) const NonNestedDeepBazIndexRoute = NonNestedDeepBazIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedDeepBazRouteRoute, } as any) const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ id: '/$bar', path: '/$bar', getParentRoute: () => ParamsPsNonNestedFooRouteRoute, } as any) const NonNestedSuffixChar123bazChar125suffixBarRoute = NonNestedSuffixChar123bazChar125suffixBarRouteImport.update({ id: '/{$baz}suffix_/bar', path: '/{$baz}suffix/bar', getParentRoute: () => NonNestedSuffixRouteRoute, } as any) const NonNestedSuffixChar123bazChar125suffixFooRoute = NonNestedSuffixChar123bazChar125suffixFooRouteImport.update({ id: '/foo', path: '/foo', getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, } as any) const NonNestedPrefixPrefixChar123bazChar125BarRoute = NonNestedPrefixPrefixChar123bazChar125BarRouteImport.update({ id: '/prefix{$baz}_/bar', path: '/prefix{$baz}/bar', getParentRoute: () => NonNestedPrefixRouteRoute, } as any) const NonNestedPrefixPrefixChar123bazChar125FooRoute = NonNestedPrefixPrefixChar123bazChar125FooRouteImport.update({ id: '/foo', path: '/foo', getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, } as any) const NonNestedPathBazBarRoute = NonNestedPathBazBarRouteImport.update({ id: '/baz_/bar', path: '/baz/bar', getParentRoute: () => NonNestedPathRouteRoute, } as any) const NonNestedPathBazFooRoute = NonNestedPathBazFooRouteImport.update({ id: '/foo', path: '/foo', getParentRoute: () => NonNestedPathBazRouteRoute, } as any) const NonNestedNamedBazBarRoute = NonNestedNamedBazBarRouteImport.update({ id: '/$baz_/bar', path: '/$baz/bar', getParentRoute: () => NonNestedNamedRouteRoute, } as any) const NonNestedNamedBazFooRoute = NonNestedNamedBazFooRouteImport.update({ id: '/foo', path: '/foo', getParentRoute: () => NonNestedNamedBazRouteRoute, } as any) const ParamsPsNamedFooBarRouteRoute = ParamsPsNamedFooBarRouteRouteImport.update({ id: '/$bar', path: '/$bar', getParentRoute: () => ParamsPsNamedFooRouteRoute, } as any) const NonNestedDeepBazBarRouteRoute = NonNestedDeepBazBarRouteRouteImport.update({ id: '/$baz_/bar', path: '/$baz/bar', getParentRoute: () => NonNestedDeepRouteRoute, } as any) const RelativeUseNavigatePathPathIndexRoute = RelativeUseNavigatePathPathIndexRouteImport.update({ id: '/path/$path/', path: '/path/$path/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeUseNavigateNestedDeepIndexRoute = RelativeUseNavigateNestedDeepIndexRouteImport.update({ id: '/nested/deep/', path: '/nested/deep/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const RelativeLinkPathPathIndexRoute = RelativeLinkPathPathIndexRouteImport.update({ id: '/path/$path/', path: '/path/$path/', getParentRoute: () => RelativeLinkRouteRoute, } as any) const RelativeLinkNestedDeepIndexRoute = RelativeLinkNestedDeepIndexRouteImport.update({ id: '/nested/deep/', path: '/nested/deep/', getParentRoute: () => RelativeLinkRouteRoute, } as any) const NonNestedDeepBazBarIndexRoute = NonNestedDeepBazBarIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ id: '/$baz', path: '/$baz', getParentRoute: () => ParamsPsNamedFooBarRouteRoute, } as any) const NonNestedDeepBazBarQuxRoute = NonNestedDeepBazBarQuxRouteImport.update({ id: '/$baz_/bar_/qux', path: '/$baz/bar/qux', getParentRoute: () => NonNestedDeepRouteRoute, } as any) const NonNestedDeepBazBarFooRouteRoute = NonNestedDeepBazBarFooRouteRouteImport.update({ id: '/$foo', path: '/$foo', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) const NonNestedDeepBazBarFooIndexRoute = NonNestedDeepBazBarFooIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedDeepBazBarFooRouteRoute, } as any) const NonNestedDeepBazBarFooQuxRoute = NonNestedDeepBazBarFooQuxRouteImport.update({ id: '/$foo_/qux', path: '/$foo/qux', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/fullpath-test': typeof FullpathTestLayoutRouteRouteWithChildren '/non-nested': typeof NonNestedRouteRouteWithChildren '/pathless-layout': typeof PathlessLayoutLayoutRouteRouteWithChildren '/search-params': typeof SearchParamsRouteRouteWithChildren '/대한민국': typeof Char45824Char54620Char48124Char44397RouteRouteWithChildren '/anchor': typeof AnchorRoute '/component-types-test': typeof ComponentTypesTestRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/hover-preload-hash': typeof HoverPreloadHashRoute '/lazy-error': typeof LazyErrorRoute '/masks': typeof MasksRouteWithChildren '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/non-nested/deep': typeof NonNestedDeepRouteRouteWithChildren '/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren '/non-nested/path': typeof NonNestedPathRouteRouteWithChildren '/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren '/non-nested/suffix': typeof NonNestedSuffixRouteRouteWithChildren '/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren '/params-ps/strict-false': typeof ParamsPsStrictFalseRouteRouteWithChildren '/relative/link': typeof RelativeLinkRouteRouteWithChildren '/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/redirect/$target': typeof RedirectTargetRouteWithChildren '/search-params/default': typeof SearchParamsDefaultRoute '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute '/params-ps/': typeof ParamsPsIndexRoute '/posts/': typeof PostsIndexRoute '/redirect/': typeof RedirectIndexRoute '/relative/': typeof RelativeIndexRoute '/search-params/': typeof SearchParamsIndexRoute '/non-nested/deep/$baz': typeof NonNestedDeepBazRouteRouteWithChildren '/non-nested/named/$baz': typeof NonNestedNamedBazRouteRouteWithChildren '/non-nested/path/baz': typeof NonNestedPathBazRouteRouteWithChildren '/non-nested/prefix/prefix{$baz}': typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteWithChildren '/non-nested/suffix/{$baz}suffix': typeof NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren '/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren '/params-ps/non-nested/$foo': typeof ParamsPsNonNestedFooRouteRouteWithChildren '/params-ps/strict-false/$version': typeof ParamsPsStrictFalseVersionRouteRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/fullpath-test/$id': typeof FullpathTestLayoutIdRoute '/masks/admin/$userId': typeof MasksAdminUserIdRoute '/masks/public/$username': typeof MasksPublicUsernameRoute '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route '/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute '/params-ps/wildcard/$': typeof ParamsPsWildcardSplatRoute '/params-ps/wildcard/prefix@대{$}': typeof ParamsPsWildcardPrefixAtChar45824Char123Char125Route '/params-ps/wildcard/prefix{$}': typeof ParamsPsWildcardPrefixChar123Char125Route '/params-ps/wildcard/{$}suffix': typeof ParamsPsWildcardChar123Char125suffixRoute '/params-ps/wildcard/{$}suffix@대': typeof ParamsPsWildcardChar123Char125suffixAtChar45824Route '/params/single/$value': typeof ParamsSingleValueRoute '/pathless-layout/child': typeof PathlessLayoutLayoutChildRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute '/redirect/$target/destination': typeof RedirectTargetDestinationRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute '/redirect/$target/via-route-redirect-beforeLoad': typeof RedirectTargetViaRouteRedirectBeforeLoadRoute '/redirect/$target/via-route-redirect-loader': typeof RedirectTargetViaRouteRedirectLoaderRoute '/redirect/$target/via-routeApi-redirect-beforeLoad': typeof RedirectTargetViaRouteApiRedirectBeforeLoadRoute '/redirect/$target/via-routeApi-redirect-loader': typeof RedirectTargetViaRouteApiRedirectLoaderRoute '/redirect/preload/first': typeof RedirectPreloadFirstRoute '/redirect/preload/second': typeof RedirectPreloadSecondRoute '/redirect/preload/third': typeof RedirectPreloadThirdRoute '/relative/link/relative-link-a': typeof RelativeLinkRelativeLinkARoute '/relative/link/relative-link-b': typeof RelativeLinkRelativeLinkBRoute '/relative/useNavigate/relative-useNavigate-a': typeof RelativeUseNavigateRelativeUseNavigateARoute '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute '/대한민국/wildcard/$': typeof Char45824Char54620Char48124Char44397WildcardSplatRoute '/대한민국/🚀/$id': typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute '/fullpath-test/': typeof FullpathTestLayoutIndexRoute '/params-ps/named/': typeof ParamsPsNamedIndexRoute '/params-ps/wildcard/': typeof ParamsPsWildcardIndexRoute '/pathless-layout/': typeof PathlessLayoutLayoutIndexRoute '/redirect/$target/': typeof RedirectTargetIndexRoute '/non-nested/deep/$baz/bar': typeof NonNestedDeepBazBarRouteRouteWithChildren '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren '/non-nested/named/$baz/foo': typeof NonNestedNamedBazFooRoute '/non-nested/named/$baz/bar': typeof NonNestedNamedBazBarRoute '/non-nested/path/baz/foo': typeof NonNestedPathBazFooRoute '/non-nested/path/baz/bar': typeof NonNestedPathBazBarRoute '/non-nested/prefix/prefix{$baz}/foo': typeof NonNestedPrefixPrefixChar123bazChar125FooRoute '/non-nested/prefix/prefix{$baz}/bar': typeof NonNestedPrefixPrefixChar123bazChar125BarRoute '/non-nested/suffix/{$baz}suffix/foo': typeof NonNestedSuffixChar123bazChar125suffixFooRoute '/non-nested/suffix/{$baz}suffix/bar': typeof NonNestedSuffixChar123bazChar125suffixBarRoute '/params-ps/non-nested/$foo/$bar': typeof ParamsPsNonNestedFooBarRoute '/non-nested/deep/$baz/': typeof NonNestedDeepBazIndexRoute '/non-nested/named/$baz/': typeof NonNestedNamedBazIndexRoute '/non-nested/path/baz/': typeof NonNestedPathBazIndexRoute '/non-nested/prefix/prefix{$baz}/': typeof NonNestedPrefixPrefixChar123bazChar125IndexRoute '/non-nested/suffix/{$baz}suffix/': typeof NonNestedSuffixChar123bazChar125suffixIndexRoute '/relative/link/nested/': typeof RelativeLinkNestedIndexRoute '/relative/link/path/': typeof RelativeLinkPathIndexRoute '/relative/link/with-search/': typeof RelativeLinkWithSearchIndexRoute '/relative/useNavigate/nested/': typeof RelativeUseNavigateNestedIndexRoute '/relative/useNavigate/path/': typeof RelativeUseNavigatePathIndexRoute '/relative/useNavigate/with-search/': typeof RelativeUseNavigateWithSearchIndexRoute '/non-nested/deep/$baz/bar/$foo': typeof NonNestedDeepBazBarFooRouteRouteWithChildren '/non-nested/deep/$baz/bar/qux': typeof NonNestedDeepBazBarQuxRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute '/non-nested/deep/$baz/bar/': typeof NonNestedDeepBazBarIndexRoute '/relative/link/nested/deep/': typeof RelativeLinkNestedDeepIndexRoute '/relative/link/path/$path/': typeof RelativeLinkPathPathIndexRoute '/relative/useNavigate/nested/deep/': typeof RelativeUseNavigateNestedDeepIndexRoute '/relative/useNavigate/path/$path/': typeof RelativeUseNavigatePathPathIndexRoute '/non-nested/deep/$baz/bar/$foo/qux': typeof NonNestedDeepBazBarFooQuxRoute '/non-nested/deep/$baz/bar/$foo/': typeof NonNestedDeepBazBarFooIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/fullpath-test': typeof FullpathTestLayoutIndexRoute '/non-nested': typeof NonNestedRouteRouteWithChildren '/pathless-layout': typeof PathlessLayoutLayoutIndexRoute '/대한민국': typeof Char45824Char54620Char48124Char44397RouteRouteWithChildren '/anchor': typeof AnchorRoute '/component-types-test': typeof ComponentTypesTestRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/hover-preload-hash': typeof HoverPreloadHashRoute '/lazy-error': typeof LazyErrorRoute '/masks': typeof MasksRouteWithChildren '/notRemountDeps': typeof NotRemountDepsRoute '/remountDeps': typeof RemountDepsRoute '/non-nested/deep': typeof NonNestedDeepRouteRouteWithChildren '/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren '/non-nested/path': typeof NonNestedPathRouteRouteWithChildren '/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren '/non-nested/suffix': typeof NonNestedSuffixRouteRouteWithChildren '/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren '/params-ps/strict-false': typeof ParamsPsStrictFalseRouteRouteWithChildren '/relative/link': typeof RelativeLinkRouteRouteWithChildren '/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/search-params/default': typeof SearchParamsDefaultRoute '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute '/params-ps': typeof ParamsPsIndexRoute '/posts': typeof PostsIndexRoute '/redirect': typeof RedirectIndexRoute '/relative': typeof RelativeIndexRoute '/search-params': typeof SearchParamsIndexRoute '/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren '/params-ps/non-nested/$foo': typeof ParamsPsNonNestedFooRouteRouteWithChildren '/params-ps/strict-false/$version': typeof ParamsPsStrictFalseVersionRouteRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/fullpath-test/$id': typeof FullpathTestLayoutIdRoute '/masks/admin/$userId': typeof MasksAdminUserIdRoute '/masks/public/$username': typeof MasksPublicUsernameRoute '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route '/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute '/params-ps/wildcard/$': typeof ParamsPsWildcardSplatRoute '/params-ps/wildcard/prefix@대{$}': typeof ParamsPsWildcardPrefixAtChar45824Char123Char125Route '/params-ps/wildcard/prefix{$}': typeof ParamsPsWildcardPrefixChar123Char125Route '/params-ps/wildcard/{$}suffix': typeof ParamsPsWildcardChar123Char125suffixRoute '/params-ps/wildcard/{$}suffix@대': typeof ParamsPsWildcardChar123Char125suffixAtChar45824Route '/params/single/$value': typeof ParamsSingleValueRoute '/pathless-layout/child': typeof PathlessLayoutLayoutChildRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute '/redirect/$target/destination': typeof RedirectTargetDestinationRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute '/redirect/$target/via-route-redirect-beforeLoad': typeof RedirectTargetViaRouteRedirectBeforeLoadRoute '/redirect/$target/via-route-redirect-loader': typeof RedirectTargetViaRouteRedirectLoaderRoute '/redirect/$target/via-routeApi-redirect-beforeLoad': typeof RedirectTargetViaRouteApiRedirectBeforeLoadRoute '/redirect/$target/via-routeApi-redirect-loader': typeof RedirectTargetViaRouteApiRedirectLoaderRoute '/redirect/preload/first': typeof RedirectPreloadFirstRoute '/redirect/preload/second': typeof RedirectPreloadSecondRoute '/redirect/preload/third': typeof RedirectPreloadThirdRoute '/relative/link/relative-link-a': typeof RelativeLinkRelativeLinkARoute '/relative/link/relative-link-b': typeof RelativeLinkRelativeLinkBRoute '/relative/useNavigate/relative-useNavigate-a': typeof RelativeUseNavigateRelativeUseNavigateARoute '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute '/대한민국/wildcard/$': typeof Char45824Char54620Char48124Char44397WildcardSplatRoute '/대한민국/🚀/$id': typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute '/params-ps/named': typeof ParamsPsNamedIndexRoute '/params-ps/wildcard': typeof ParamsPsWildcardIndexRoute '/redirect/$target': typeof RedirectTargetIndexRoute '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren '/non-nested/named/$baz/foo': typeof NonNestedNamedBazFooRoute '/non-nested/named/$baz/bar': typeof NonNestedNamedBazBarRoute '/non-nested/path/baz/foo': typeof NonNestedPathBazFooRoute '/non-nested/path/baz/bar': typeof NonNestedPathBazBarRoute '/non-nested/prefix/prefix{$baz}/foo': typeof NonNestedPrefixPrefixChar123bazChar125FooRoute '/non-nested/prefix/prefix{$baz}/bar': typeof NonNestedPrefixPrefixChar123bazChar125BarRoute '/non-nested/suffix/{$baz}suffix/foo': typeof NonNestedSuffixChar123bazChar125suffixFooRoute '/non-nested/suffix/{$baz}suffix/bar': typeof NonNestedSuffixChar123bazChar125suffixBarRoute '/params-ps/non-nested/$foo/$bar': typeof ParamsPsNonNestedFooBarRoute '/non-nested/deep/$baz': typeof NonNestedDeepBazIndexRoute '/non-nested/named/$baz': typeof NonNestedNamedBazIndexRoute '/non-nested/path/baz': typeof NonNestedPathBazIndexRoute '/non-nested/prefix/prefix{$baz}': typeof NonNestedPrefixPrefixChar123bazChar125IndexRoute '/non-nested/suffix/{$baz}suffix': typeof NonNestedSuffixChar123bazChar125suffixIndexRoute '/relative/link/nested': typeof RelativeLinkNestedIndexRoute '/relative/link/path': typeof RelativeLinkPathIndexRoute '/relative/link/with-search': typeof RelativeLinkWithSearchIndexRoute '/relative/useNavigate/nested': typeof RelativeUseNavigateNestedIndexRoute '/relative/useNavigate/path': typeof RelativeUseNavigatePathIndexRoute '/relative/useNavigate/with-search': typeof RelativeUseNavigateWithSearchIndexRoute '/non-nested/deep/$baz/bar/qux': typeof NonNestedDeepBazBarQuxRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute '/non-nested/deep/$baz/bar': typeof NonNestedDeepBazBarIndexRoute '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute '/non-nested/deep/$baz/bar/$foo/qux': typeof NonNestedDeepBazBarFooQuxRoute '/non-nested/deep/$baz/bar/$foo': typeof NonNestedDeepBazBarFooIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/fullpath-test': typeof FullpathTestRouteRouteWithChildren '/non-nested': typeof NonNestedRouteRouteWithChildren '/pathless-layout': typeof PathlessLayoutRouteRouteWithChildren '/search-params': typeof SearchParamsRouteRouteWithChildren '/대한민국': typeof Char45824Char54620Char48124Char44397RouteRouteWithChildren '/_layout': typeof LayoutRouteWithChildren '/anchor': typeof AnchorRoute '/component-types-test': typeof ComponentTypesTestRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/hover-preload-hash': typeof HoverPreloadHashRoute '/lazy-error': typeof LazyErrorRoute '/masks': typeof MasksRouteWithChildren '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/fullpath-test/_layout': typeof FullpathTestLayoutRouteRouteWithChildren '/non-nested/deep': typeof NonNestedDeepRouteRouteWithChildren '/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren '/non-nested/path': typeof NonNestedPathRouteRouteWithChildren '/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren '/non-nested/suffix': typeof NonNestedSuffixRouteRouteWithChildren '/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren '/params-ps/strict-false': typeof ParamsPsStrictFalseRouteRouteWithChildren '/pathless-layout/_layout': typeof PathlessLayoutLayoutRouteRouteWithChildren '/relative/link': typeof RelativeLinkRouteRouteWithChildren '/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren '/(another-group)/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/(group)/_layout': typeof groupLayoutRouteWithChildren '/(group)/inside': typeof groupInsideRoute '/(group)/lazyinside': typeof groupLazyinsideRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/redirect/$target': typeof RedirectTargetRouteWithChildren '/search-params/default': typeof SearchParamsDefaultRoute '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute '/params-ps/': typeof ParamsPsIndexRoute '/posts/': typeof PostsIndexRoute '/redirect/': typeof RedirectIndexRoute '/relative/': typeof RelativeIndexRoute '/search-params/': typeof SearchParamsIndexRoute '/non-nested/deep/$baz': typeof NonNestedDeepBazRouteRouteWithChildren '/non-nested/named/$baz': typeof NonNestedNamedBazRouteRouteWithChildren '/non-nested/path/baz': typeof NonNestedPathBazRouteRouteWithChildren '/non-nested/prefix/prefix{$baz}': typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteWithChildren '/non-nested/suffix/{$baz}suffix': typeof NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren '/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren '/params-ps/non-nested/$foo_': typeof ParamsPsNonNestedFooRouteRouteWithChildren '/params-ps/strict-false/$version': typeof ParamsPsStrictFalseVersionRouteRoute '/(group)/_layout/insidelayout': typeof groupLayoutInsidelayoutRoute '/(group)/subfolder/inside': typeof groupSubfolderInsideRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute '/fullpath-test/_layout/$id': typeof FullpathTestLayoutIdRoute '/masks/admin/$userId': typeof MasksAdminUserIdRoute '/masks/public/$username': typeof MasksPublicUsernameRoute '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route '/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute '/params-ps/wildcard/$': typeof ParamsPsWildcardSplatRoute '/params-ps/wildcard/prefix@대{$}': typeof ParamsPsWildcardPrefixAtChar45824Char123Char125Route '/params-ps/wildcard/prefix{$}': typeof ParamsPsWildcardPrefixChar123Char125Route '/params-ps/wildcard/{$}suffix': typeof ParamsPsWildcardChar123Char125suffixRoute '/params-ps/wildcard/{$}suffix@대': typeof ParamsPsWildcardChar123Char125suffixAtChar45824Route '/params/single/$value': typeof ParamsSingleValueRoute '/pathless-layout/_layout/child': typeof PathlessLayoutLayoutChildRoute '/posts_/$postId/edit': typeof PostsPostIdEditRoute '/redirect/$target/destination': typeof RedirectTargetDestinationRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute '/redirect/$target/via-route-redirect-beforeLoad': typeof RedirectTargetViaRouteRedirectBeforeLoadRoute '/redirect/$target/via-route-redirect-loader': typeof RedirectTargetViaRouteRedirectLoaderRoute '/redirect/$target/via-routeApi-redirect-beforeLoad': typeof RedirectTargetViaRouteApiRedirectBeforeLoadRoute '/redirect/$target/via-routeApi-redirect-loader': typeof RedirectTargetViaRouteApiRedirectLoaderRoute '/redirect/preload/first': typeof RedirectPreloadFirstRoute '/redirect/preload/second': typeof RedirectPreloadSecondRoute '/redirect/preload/third': typeof RedirectPreloadThirdRoute '/relative/link/relative-link-a': typeof RelativeLinkRelativeLinkARoute '/relative/link/relative-link-b': typeof RelativeLinkRelativeLinkBRoute '/relative/useNavigate/relative-useNavigate-a': typeof RelativeUseNavigateRelativeUseNavigateARoute '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute '/대한민국/wildcard/$': typeof Char45824Char54620Char48124Char44397WildcardSplatRoute '/대한민국/🚀/$id': typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute '/fullpath-test/_layout/': typeof FullpathTestLayoutIndexRoute '/params-ps/named/': typeof ParamsPsNamedIndexRoute '/params-ps/wildcard/': typeof ParamsPsWildcardIndexRoute '/pathless-layout/_layout/': typeof PathlessLayoutLayoutIndexRoute '/redirect/$target/': typeof RedirectTargetIndexRoute '/non-nested/deep/$baz_/bar': typeof NonNestedDeepBazBarRouteRouteWithChildren '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren '/non-nested/named/$baz/foo': typeof NonNestedNamedBazFooRoute '/non-nested/named/$baz_/bar': typeof NonNestedNamedBazBarRoute '/non-nested/path/baz/foo': typeof NonNestedPathBazFooRoute '/non-nested/path/baz_/bar': typeof NonNestedPathBazBarRoute '/non-nested/prefix/prefix{$baz}/foo': typeof NonNestedPrefixPrefixChar123bazChar125FooRoute '/non-nested/prefix/prefix{$baz}_/bar': typeof NonNestedPrefixPrefixChar123bazChar125BarRoute '/non-nested/suffix/{$baz}suffix/foo': typeof NonNestedSuffixChar123bazChar125suffixFooRoute '/non-nested/suffix/{$baz}suffix_/bar': typeof NonNestedSuffixChar123bazChar125suffixBarRoute '/params-ps/non-nested/$foo_/$bar': typeof ParamsPsNonNestedFooBarRoute '/non-nested/deep/$baz/': typeof NonNestedDeepBazIndexRoute '/non-nested/named/$baz/': typeof NonNestedNamedBazIndexRoute '/non-nested/path/baz/': typeof NonNestedPathBazIndexRoute '/non-nested/prefix/prefix{$baz}/': typeof NonNestedPrefixPrefixChar123bazChar125IndexRoute '/non-nested/suffix/{$baz}suffix/': typeof NonNestedSuffixChar123bazChar125suffixIndexRoute '/relative/link/nested/': typeof RelativeLinkNestedIndexRoute '/relative/link/path/': typeof RelativeLinkPathIndexRoute '/relative/link/with-search/': typeof RelativeLinkWithSearchIndexRoute '/relative/useNavigate/nested/': typeof RelativeUseNavigateNestedIndexRoute '/relative/useNavigate/path/': typeof RelativeUseNavigatePathIndexRoute '/relative/useNavigate/with-search/': typeof RelativeUseNavigateWithSearchIndexRoute '/non-nested/deep/$baz_/bar/$foo': typeof NonNestedDeepBazBarFooRouteRouteWithChildren '/non-nested/deep/$baz_/bar_/qux': typeof NonNestedDeepBazBarQuxRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute '/non-nested/deep/$baz_/bar/': typeof NonNestedDeepBazBarIndexRoute '/relative/link/nested/deep/': typeof RelativeLinkNestedDeepIndexRoute '/relative/link/path/$path/': typeof RelativeLinkPathPathIndexRoute '/relative/useNavigate/nested/deep/': typeof RelativeUseNavigateNestedDeepIndexRoute '/relative/useNavigate/path/$path/': typeof RelativeUseNavigatePathPathIndexRoute '/non-nested/deep/$baz_/bar/$foo_/qux': typeof NonNestedDeepBazBarFooQuxRoute '/non-nested/deep/$baz_/bar/$foo/': typeof NonNestedDeepBazBarFooIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/fullpath-test' | '/non-nested' | '/pathless-layout' | '/search-params' | '/대한민국' | '/anchor' | '/component-types-test' | '/editing-a' | '/editing-b' | '/hover-preload-hash' | '/lazy-error' | '/masks' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/non-nested/deep' | '/non-nested/named' | '/non-nested/path' | '/non-nested/prefix' | '/non-nested/suffix' | '/params-ps/non-nested' | '/params-ps/strict-false' | '/relative/link' | '/relative/useNavigate' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/redirect/$target' | '/search-params/default' | '/structural-sharing/$enabled' | '/params-ps/' | '/posts/' | '/redirect/' | '/relative/' | '/search-params/' | '/non-nested/deep/$baz' | '/non-nested/named/$baz' | '/non-nested/path/baz' | '/non-nested/prefix/prefix{$baz}' | '/non-nested/suffix/{$baz}suffix' | '/params-ps/named/$foo' | '/params-ps/non-nested/$foo' | '/params-ps/strict-false/$version' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/fullpath-test/$id' | '/masks/admin/$userId' | '/masks/public/$username' | '/params-ps/named/prefix{$foo}' | '/params-ps/named/{$foo}suffix' | '/params-ps/wildcard/$' | '/params-ps/wildcard/prefix@대{$}' | '/params-ps/wildcard/prefix{$}' | '/params-ps/wildcard/{$}suffix' | '/params-ps/wildcard/{$}suffix@대' | '/params/single/$value' | '/pathless-layout/child' | '/posts/$postId/edit' | '/redirect/$target/destination' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' | '/redirect/$target/via-route-redirect-beforeLoad' | '/redirect/$target/via-route-redirect-loader' | '/redirect/$target/via-routeApi-redirect-beforeLoad' | '/redirect/$target/via-routeApi-redirect-loader' | '/redirect/preload/first' | '/redirect/preload/second' | '/redirect/preload/third' | '/relative/link/relative-link-a' | '/relative/link/relative-link-b' | '/relative/useNavigate/relative-useNavigate-a' | '/relative/useNavigate/relative-useNavigate-b' | '/대한민국/wildcard/$' | '/대한민국/🚀/$id' | '/fullpath-test/' | '/params-ps/named/' | '/params-ps/wildcard/' | '/pathless-layout/' | '/redirect/$target/' | '/non-nested/deep/$baz/bar' | '/params-ps/named/$foo/$bar' | '/non-nested/named/$baz/foo' | '/non-nested/named/$baz/bar' | '/non-nested/path/baz/foo' | '/non-nested/path/baz/bar' | '/non-nested/prefix/prefix{$baz}/foo' | '/non-nested/prefix/prefix{$baz}/bar' | '/non-nested/suffix/{$baz}suffix/foo' | '/non-nested/suffix/{$baz}suffix/bar' | '/params-ps/non-nested/$foo/$bar' | '/non-nested/deep/$baz/' | '/non-nested/named/$baz/' | '/non-nested/path/baz/' | '/non-nested/prefix/prefix{$baz}/' | '/non-nested/suffix/{$baz}suffix/' | '/relative/link/nested/' | '/relative/link/path/' | '/relative/link/with-search/' | '/relative/useNavigate/nested/' | '/relative/useNavigate/path/' | '/relative/useNavigate/with-search/' | '/non-nested/deep/$baz/bar/$foo' | '/non-nested/deep/$baz/bar/qux' | '/params-ps/named/$foo/$bar/$baz' | '/non-nested/deep/$baz/bar/' | '/relative/link/nested/deep/' | '/relative/link/path/$path/' | '/relative/useNavigate/nested/deep/' | '/relative/useNavigate/path/$path/' | '/non-nested/deep/$baz/bar/$foo/qux' | '/non-nested/deep/$baz/bar/$foo/' fileRoutesByTo: FileRoutesByTo to: | '/' | '/fullpath-test' | '/non-nested' | '/pathless-layout' | '/대한민국' | '/anchor' | '/component-types-test' | '/editing-a' | '/editing-b' | '/hover-preload-hash' | '/lazy-error' | '/masks' | '/notRemountDeps' | '/remountDeps' | '/non-nested/deep' | '/non-nested/named' | '/non-nested/path' | '/non-nested/prefix' | '/non-nested/suffix' | '/params-ps/non-nested' | '/params-ps/strict-false' | '/relative/link' | '/relative/useNavigate' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/search-params/default' | '/structural-sharing/$enabled' | '/params-ps' | '/posts' | '/redirect' | '/relative' | '/search-params' | '/params-ps/named/$foo' | '/params-ps/non-nested/$foo' | '/params-ps/strict-false/$version' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/fullpath-test/$id' | '/masks/admin/$userId' | '/masks/public/$username' | '/params-ps/named/prefix{$foo}' | '/params-ps/named/{$foo}suffix' | '/params-ps/wildcard/$' | '/params-ps/wildcard/prefix@대{$}' | '/params-ps/wildcard/prefix{$}' | '/params-ps/wildcard/{$}suffix' | '/params-ps/wildcard/{$}suffix@대' | '/params/single/$value' | '/pathless-layout/child' | '/posts/$postId/edit' | '/redirect/$target/destination' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' | '/redirect/$target/via-route-redirect-beforeLoad' | '/redirect/$target/via-route-redirect-loader' | '/redirect/$target/via-routeApi-redirect-beforeLoad' | '/redirect/$target/via-routeApi-redirect-loader' | '/redirect/preload/first' | '/redirect/preload/second' | '/redirect/preload/third' | '/relative/link/relative-link-a' | '/relative/link/relative-link-b' | '/relative/useNavigate/relative-useNavigate-a' | '/relative/useNavigate/relative-useNavigate-b' | '/대한민국/wildcard/$' | '/대한민국/🚀/$id' | '/params-ps/named' | '/params-ps/wildcard' | '/redirect/$target' | '/params-ps/named/$foo/$bar' | '/non-nested/named/$baz/foo' | '/non-nested/named/$baz/bar' | '/non-nested/path/baz/foo' | '/non-nested/path/baz/bar' | '/non-nested/prefix/prefix{$baz}/foo' | '/non-nested/prefix/prefix{$baz}/bar' | '/non-nested/suffix/{$baz}suffix/foo' | '/non-nested/suffix/{$baz}suffix/bar' | '/params-ps/non-nested/$foo/$bar' | '/non-nested/deep/$baz' | '/non-nested/named/$baz' | '/non-nested/path/baz' | '/non-nested/prefix/prefix{$baz}' | '/non-nested/suffix/{$baz}suffix' | '/relative/link/nested' | '/relative/link/path' | '/relative/link/with-search' | '/relative/useNavigate/nested' | '/relative/useNavigate/path' | '/relative/useNavigate/with-search' | '/non-nested/deep/$baz/bar/qux' | '/params-ps/named/$foo/$bar/$baz' | '/non-nested/deep/$baz/bar' | '/relative/link/nested/deep' | '/relative/link/path/$path' | '/relative/useNavigate/nested/deep' | '/relative/useNavigate/path/$path' | '/non-nested/deep/$baz/bar/$foo/qux' | '/non-nested/deep/$baz/bar/$foo' id: | '__root__' | '/' | '/fullpath-test' | '/non-nested' | '/pathless-layout' | '/search-params' | '/대한민국' | '/_layout' | '/anchor' | '/component-types-test' | '/editing-a' | '/editing-b' | '/hover-preload-hash' | '/lazy-error' | '/masks' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/fullpath-test/_layout' | '/non-nested/deep' | '/non-nested/named' | '/non-nested/path' | '/non-nested/prefix' | '/non-nested/suffix' | '/params-ps/non-nested' | '/params-ps/strict-false' | '/pathless-layout/_layout' | '/relative/link' | '/relative/useNavigate' | '/(another-group)/onlyrouteinside' | '/(group)/_layout' | '/(group)/inside' | '/(group)/lazyinside' | '/_layout/_layout-2' | '/posts/$postId' | '/redirect/$target' | '/search-params/default' | '/structural-sharing/$enabled' | '/params-ps/' | '/posts/' | '/redirect/' | '/relative/' | '/search-params/' | '/non-nested/deep/$baz' | '/non-nested/named/$baz' | '/non-nested/path/baz' | '/non-nested/prefix/prefix{$baz}' | '/non-nested/suffix/{$baz}suffix' | '/params-ps/named/$foo' | '/params-ps/non-nested/$foo_' | '/params-ps/strict-false/$version' | '/(group)/_layout/insidelayout' | '/(group)/subfolder/inside' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' | '/fullpath-test/_layout/$id' | '/masks/admin/$userId' | '/masks/public/$username' | '/params-ps/named/prefix{$foo}' | '/params-ps/named/{$foo}suffix' | '/params-ps/wildcard/$' | '/params-ps/wildcard/prefix@대{$}' | '/params-ps/wildcard/prefix{$}' | '/params-ps/wildcard/{$}suffix' | '/params-ps/wildcard/{$}suffix@대' | '/params/single/$value' | '/pathless-layout/_layout/child' | '/posts_/$postId/edit' | '/redirect/$target/destination' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' | '/redirect/$target/via-route-redirect-beforeLoad' | '/redirect/$target/via-route-redirect-loader' | '/redirect/$target/via-routeApi-redirect-beforeLoad' | '/redirect/$target/via-routeApi-redirect-loader' | '/redirect/preload/first' | '/redirect/preload/second' | '/redirect/preload/third' | '/relative/link/relative-link-a' | '/relative/link/relative-link-b' | '/relative/useNavigate/relative-useNavigate-a' | '/relative/useNavigate/relative-useNavigate-b' | '/대한민국/wildcard/$' | '/대한민국/🚀/$id' | '/fullpath-test/_layout/' | '/params-ps/named/' | '/params-ps/wildcard/' | '/pathless-layout/_layout/' | '/redirect/$target/' | '/non-nested/deep/$baz_/bar' | '/params-ps/named/$foo/$bar' | '/non-nested/named/$baz/foo' | '/non-nested/named/$baz_/bar' | '/non-nested/path/baz/foo' | '/non-nested/path/baz_/bar' | '/non-nested/prefix/prefix{$baz}/foo' | '/non-nested/prefix/prefix{$baz}_/bar' | '/non-nested/suffix/{$baz}suffix/foo' | '/non-nested/suffix/{$baz}suffix_/bar' | '/params-ps/non-nested/$foo_/$bar' | '/non-nested/deep/$baz/' | '/non-nested/named/$baz/' | '/non-nested/path/baz/' | '/non-nested/prefix/prefix{$baz}/' | '/non-nested/suffix/{$baz}suffix/' | '/relative/link/nested/' | '/relative/link/path/' | '/relative/link/with-search/' | '/relative/useNavigate/nested/' | '/relative/useNavigate/path/' | '/relative/useNavigate/with-search/' | '/non-nested/deep/$baz_/bar/$foo' | '/non-nested/deep/$baz_/bar_/qux' | '/params-ps/named/$foo/$bar/$baz' | '/non-nested/deep/$baz_/bar/' | '/relative/link/nested/deep/' | '/relative/link/path/$path/' | '/relative/useNavigate/nested/deep/' | '/relative/useNavigate/path/$path/' | '/non-nested/deep/$baz_/bar/$foo_/qux' | '/non-nested/deep/$baz_/bar/$foo/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute FullpathTestRouteRoute: typeof FullpathTestRouteRouteWithChildren NonNestedRouteRoute: typeof NonNestedRouteRouteWithChildren PathlessLayoutRouteRoute: typeof PathlessLayoutRouteRouteWithChildren SearchParamsRouteRoute: typeof SearchParamsRouteRouteWithChildren Char45824Char54620Char48124Char44397RouteRoute: typeof Char45824Char54620Char48124Char44397RouteRouteWithChildren LayoutRoute: typeof LayoutRouteWithChildren AnchorRoute: typeof AnchorRoute ComponentTypesTestRoute: typeof ComponentTypesTestRoute EditingARoute: typeof EditingARoute EditingBRoute: typeof EditingBRoute HoverPreloadHashRoute: typeof HoverPreloadHashRoute LazyErrorRoute: typeof LazyErrorRoute MasksRoute: typeof MasksRouteWithChildren NotRemountDepsRoute: typeof NotRemountDepsRoute PostsRoute: typeof PostsRouteWithChildren RemountDepsRoute: typeof RemountDepsRoute ParamsPsNonNestedRouteRoute: typeof ParamsPsNonNestedRouteRouteWithChildren ParamsPsStrictFalseRouteRoute: typeof ParamsPsStrictFalseRouteRouteWithChildren RelativeLinkRouteRoute: typeof RelativeLinkRouteRouteWithChildren RelativeUseNavigateRouteRoute: typeof RelativeUseNavigateRouteRouteWithChildren anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute groupLayoutRoute: typeof groupLayoutRouteWithChildren groupInsideRoute: typeof groupInsideRoute groupLazyinsideRoute: typeof groupLazyinsideRoute RedirectTargetRoute: typeof RedirectTargetRouteWithChildren StructuralSharingEnabledRoute: typeof StructuralSharingEnabledRoute ParamsPsIndexRoute: typeof ParamsPsIndexRoute RedirectIndexRoute: typeof RedirectIndexRoute RelativeIndexRoute: typeof RelativeIndexRoute ParamsPsNamedFooRouteRoute: typeof ParamsPsNamedFooRouteRouteWithChildren groupSubfolderInsideRoute: typeof groupSubfolderInsideRoute ParamsPsNamedPrefixChar123fooChar125Route: typeof ParamsPsNamedPrefixChar123fooChar125Route ParamsPsNamedChar123fooChar125suffixRoute: typeof ParamsPsNamedChar123fooChar125suffixRoute ParamsPsWildcardSplatRoute: typeof ParamsPsWildcardSplatRoute ParamsPsWildcardPrefixAtChar45824Char123Char125Route: typeof ParamsPsWildcardPrefixAtChar45824Char123Char125Route ParamsPsWildcardPrefixChar123Char125Route: typeof ParamsPsWildcardPrefixChar123Char125Route ParamsPsWildcardChar123Char125suffixRoute: typeof ParamsPsWildcardChar123Char125suffixRoute ParamsPsWildcardChar123Char125suffixAtChar45824Route: typeof ParamsPsWildcardChar123Char125suffixAtChar45824Route ParamsSingleValueRoute: typeof ParamsSingleValueRoute PostsPostIdEditRoute: typeof PostsPostIdEditRoute RedirectPreloadFirstRoute: typeof RedirectPreloadFirstRoute RedirectPreloadSecondRoute: typeof RedirectPreloadSecondRoute RedirectPreloadThirdRoute: typeof RedirectPreloadThirdRoute ParamsPsNamedIndexRoute: typeof ParamsPsNamedIndexRoute ParamsPsWildcardIndexRoute: typeof ParamsPsWildcardIndexRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/remountDeps': { id: '/remountDeps' path: '/remountDeps' fullPath: '/remountDeps' preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { id: '/notRemountDeps' path: '/notRemountDeps' fullPath: '/notRemountDeps' preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } '/masks': { id: '/masks' path: '/masks' fullPath: '/masks' preLoaderRoute: typeof MasksRouteImport parentRoute: typeof rootRouteImport } '/lazy-error': { id: '/lazy-error' path: '/lazy-error' fullPath: '/lazy-error' preLoaderRoute: typeof LazyErrorRouteImport parentRoute: typeof rootRouteImport } '/hover-preload-hash': { id: '/hover-preload-hash' path: '/hover-preload-hash' fullPath: '/hover-preload-hash' preLoaderRoute: typeof HoverPreloadHashRouteImport parentRoute: typeof rootRouteImport } '/editing-b': { id: '/editing-b' path: '/editing-b' fullPath: '/editing-b' preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/editing-a': { id: '/editing-a' path: '/editing-a' fullPath: '/editing-a' preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } '/component-types-test': { id: '/component-types-test' path: '/component-types-test' fullPath: '/component-types-test' preLoaderRoute: typeof ComponentTypesTestRouteImport parentRoute: typeof rootRouteImport } '/anchor': { id: '/anchor' path: '/anchor' fullPath: '/anchor' preLoaderRoute: typeof AnchorRouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/대한민국': { id: '/대한민국' path: '/대한민국' fullPath: '/대한민국' preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteRouteImport parentRoute: typeof rootRouteImport } '/search-params': { id: '/search-params' path: '/search-params' fullPath: '/search-params' preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } '/pathless-layout': { id: '/pathless-layout' path: '/pathless-layout' fullPath: '/pathless-layout' preLoaderRoute: typeof PathlessLayoutRouteRouteImport parentRoute: typeof rootRouteImport } '/non-nested': { id: '/non-nested' path: '/non-nested' fullPath: '/non-nested' preLoaderRoute: typeof NonNestedRouteRouteImport parentRoute: typeof rootRouteImport } '/fullpath-test': { id: '/fullpath-test' path: '/fullpath-test' fullPath: '/fullpath-test' preLoaderRoute: typeof FullpathTestRouteRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/search-params/': { id: '/search-params/' path: '/' fullPath: '/search-params/' preLoaderRoute: typeof SearchParamsIndexRouteImport parentRoute: typeof SearchParamsRouteRoute } '/relative/': { id: '/relative/' path: '/relative' fullPath: '/relative/' preLoaderRoute: typeof RelativeIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect/': { id: '/redirect/' path: '/redirect' fullPath: '/redirect/' preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/params-ps/': { id: '/params-ps/' path: '/params-ps' fullPath: '/params-ps/' preLoaderRoute: typeof ParamsPsIndexRouteImport parentRoute: typeof rootRouteImport } '/structural-sharing/$enabled': { id: '/structural-sharing/$enabled' path: '/structural-sharing/$enabled' fullPath: '/structural-sharing/$enabled' preLoaderRoute: typeof StructuralSharingEnabledRouteImport parentRoute: typeof rootRouteImport } '/search-params/default': { id: '/search-params/default' path: '/default' fullPath: '/search-params/default' preLoaderRoute: typeof SearchParamsDefaultRouteImport parentRoute: typeof SearchParamsRouteRoute } '/redirect/$target': { id: '/redirect/$target' path: '/redirect/$target' fullPath: '/redirect/$target' preLoaderRoute: typeof RedirectTargetRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/(group)/lazyinside': { id: '/(group)/lazyinside' path: '/lazyinside' fullPath: '/lazyinside' preLoaderRoute: typeof groupLazyinsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/inside': { id: '/(group)/inside' path: '/inside' fullPath: '/inside' preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout': { id: '/(group)/_layout' path: '' fullPath: '' preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } '/(another-group)/onlyrouteinside': { id: '/(another-group)/onlyrouteinside' path: '/onlyrouteinside' fullPath: '/onlyrouteinside' preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport parentRoute: typeof rootRouteImport } '/relative/useNavigate': { id: '/relative/useNavigate' path: '/relative/useNavigate' fullPath: '/relative/useNavigate' preLoaderRoute: typeof RelativeUseNavigateRouteRouteImport parentRoute: typeof rootRouteImport } '/relative/link': { id: '/relative/link' path: '/relative/link' fullPath: '/relative/link' preLoaderRoute: typeof RelativeLinkRouteRouteImport parentRoute: typeof rootRouteImport } '/pathless-layout/_layout': { id: '/pathless-layout/_layout' path: '' fullPath: '/pathless-layout' preLoaderRoute: typeof PathlessLayoutLayoutRouteRouteImport parentRoute: typeof PathlessLayoutRouteRoute } '/params-ps/strict-false': { id: '/params-ps/strict-false' path: '/params-ps/strict-false' fullPath: '/params-ps/strict-false' preLoaderRoute: typeof ParamsPsStrictFalseRouteRouteImport parentRoute: typeof rootRouteImport } '/params-ps/non-nested': { id: '/params-ps/non-nested' path: '/params-ps/non-nested' fullPath: '/params-ps/non-nested' preLoaderRoute: typeof ParamsPsNonNestedRouteRouteImport parentRoute: typeof rootRouteImport } '/non-nested/suffix': { id: '/non-nested/suffix' path: '/suffix' fullPath: '/non-nested/suffix' preLoaderRoute: typeof NonNestedSuffixRouteRouteImport parentRoute: typeof NonNestedRouteRoute } '/non-nested/prefix': { id: '/non-nested/prefix' path: '/prefix' fullPath: '/non-nested/prefix' preLoaderRoute: typeof NonNestedPrefixRouteRouteImport parentRoute: typeof NonNestedRouteRoute } '/non-nested/path': { id: '/non-nested/path' path: '/path' fullPath: '/non-nested/path' preLoaderRoute: typeof NonNestedPathRouteRouteImport parentRoute: typeof NonNestedRouteRoute } '/non-nested/named': { id: '/non-nested/named' path: '/named' fullPath: '/non-nested/named' preLoaderRoute: typeof NonNestedNamedRouteRouteImport parentRoute: typeof NonNestedRouteRoute } '/non-nested/deep': { id: '/non-nested/deep' path: '/deep' fullPath: '/non-nested/deep' preLoaderRoute: typeof NonNestedDeepRouteRouteImport parentRoute: typeof NonNestedRouteRoute } '/fullpath-test/_layout': { id: '/fullpath-test/_layout' path: '' fullPath: '/fullpath-test' preLoaderRoute: typeof FullpathTestLayoutRouteRouteImport parentRoute: typeof FullpathTestRouteRoute } '/redirect/$target/': { id: '/redirect/$target/' path: '/' fullPath: '/redirect/$target/' preLoaderRoute: typeof RedirectTargetIndexRouteImport parentRoute: typeof RedirectTargetRoute } '/pathless-layout/_layout/': { id: '/pathless-layout/_layout/' path: '/' fullPath: '/pathless-layout/' preLoaderRoute: typeof PathlessLayoutLayoutIndexRouteImport parentRoute: typeof PathlessLayoutLayoutRouteRoute } '/params-ps/wildcard/': { id: '/params-ps/wildcard/' path: '/params-ps/wildcard' fullPath: '/params-ps/wildcard/' preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport parentRoute: typeof rootRouteImport } '/params-ps/named/': { id: '/params-ps/named/' path: '/params-ps/named' fullPath: '/params-ps/named/' preLoaderRoute: typeof ParamsPsNamedIndexRouteImport parentRoute: typeof rootRouteImport } '/fullpath-test/_layout/': { id: '/fullpath-test/_layout/' path: '/' fullPath: '/fullpath-test/' preLoaderRoute: typeof FullpathTestLayoutIndexRouteImport parentRoute: typeof FullpathTestLayoutRouteRoute } '/대한민국/🚀/$id': { id: '/대한민국/🚀/$id' path: '/🚀/$id' fullPath: '/대한민국/🚀/$id' preLoaderRoute: typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute } '/대한민국/wildcard/$': { id: '/대한민국/wildcard/$' path: '/wildcard/$' fullPath: '/대한민국/wildcard/$' preLoaderRoute: typeof Char45824Char54620Char48124Char44397WildcardSplatRouteImport parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute } '/relative/useNavigate/relative-useNavigate-b': { id: '/relative/useNavigate/relative-useNavigate-b' path: '/relative-useNavigate-b' fullPath: '/relative/useNavigate/relative-useNavigate-b' preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateBRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/relative-useNavigate-a': { id: '/relative/useNavigate/relative-useNavigate-a' path: '/relative-useNavigate-a' fullPath: '/relative/useNavigate/relative-useNavigate-a' preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateARouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/relative-link-b': { id: '/relative/link/relative-link-b' path: '/relative-link-b' fullPath: '/relative/link/relative-link-b' preLoaderRoute: typeof RelativeLinkRelativeLinkBRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/relative-link-a': { id: '/relative/link/relative-link-a' path: '/relative-link-a' fullPath: '/relative/link/relative-link-a' preLoaderRoute: typeof RelativeLinkRelativeLinkARouteImport parentRoute: typeof RelativeLinkRouteRoute } '/redirect/preload/third': { id: '/redirect/preload/third' path: '/redirect/preload/third' fullPath: '/redirect/preload/third' preLoaderRoute: typeof RedirectPreloadThirdRouteImport parentRoute: typeof rootRouteImport } '/redirect/preload/second': { id: '/redirect/preload/second' path: '/redirect/preload/second' fullPath: '/redirect/preload/second' preLoaderRoute: typeof RedirectPreloadSecondRouteImport parentRoute: typeof rootRouteImport } '/redirect/preload/first': { id: '/redirect/preload/first' path: '/redirect/preload/first' fullPath: '/redirect/preload/first' preLoaderRoute: typeof RedirectPreloadFirstRouteImport parentRoute: typeof rootRouteImport } '/redirect/$target/via-routeApi-redirect-loader': { id: '/redirect/$target/via-routeApi-redirect-loader' path: '/via-routeApi-redirect-loader' fullPath: '/redirect/$target/via-routeApi-redirect-loader' preLoaderRoute: typeof RedirectTargetViaRouteApiRedirectLoaderRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/via-routeApi-redirect-beforeLoad': { id: '/redirect/$target/via-routeApi-redirect-beforeLoad' path: '/via-routeApi-redirect-beforeLoad' fullPath: '/redirect/$target/via-routeApi-redirect-beforeLoad' preLoaderRoute: typeof RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/via-route-redirect-loader': { id: '/redirect/$target/via-route-redirect-loader' path: '/via-route-redirect-loader' fullPath: '/redirect/$target/via-route-redirect-loader' preLoaderRoute: typeof RedirectTargetViaRouteRedirectLoaderRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/via-route-redirect-beforeLoad': { id: '/redirect/$target/via-route-redirect-beforeLoad' path: '/via-route-redirect-beforeLoad' fullPath: '/redirect/$target/via-route-redirect-beforeLoad' preLoaderRoute: typeof RedirectTargetViaRouteRedirectBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/via-loader': { id: '/redirect/$target/via-loader' path: '/via-loader' fullPath: '/redirect/$target/via-loader' preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/via-beforeLoad': { id: '/redirect/$target/via-beforeLoad' path: '/via-beforeLoad' fullPath: '/redirect/$target/via-beforeLoad' preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/destination': { id: '/redirect/$target/destination' path: '/destination' fullPath: '/redirect/$target/destination' preLoaderRoute: typeof RedirectTargetDestinationRouteImport parentRoute: typeof RedirectTargetRoute } '/posts_/$postId/edit': { id: '/posts_/$postId/edit' path: '/posts/$postId/edit' fullPath: '/posts/$postId/edit' preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } '/pathless-layout/_layout/child': { id: '/pathless-layout/_layout/child' path: '/child' fullPath: '/pathless-layout/child' preLoaderRoute: typeof PathlessLayoutLayoutChildRouteImport parentRoute: typeof PathlessLayoutLayoutRouteRoute } '/params/single/$value': { id: '/params/single/$value' path: '/params/single/$value' fullPath: '/params/single/$value' preLoaderRoute: typeof ParamsSingleValueRouteImport parentRoute: typeof rootRouteImport } '/params-ps/wildcard/{$}suffix@대': { id: '/params-ps/wildcard/{$}suffix@대' path: '/params-ps/wildcard/{$}suffix@대' fullPath: '/params-ps/wildcard/{$}suffix@대' preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport parentRoute: typeof rootRouteImport } '/params-ps/wildcard/{$}suffix': { id: '/params-ps/wildcard/{$}suffix' path: '/params-ps/wildcard/{$}suffix' fullPath: '/params-ps/wildcard/{$}suffix' preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixRouteImport parentRoute: typeof rootRouteImport } '/params-ps/wildcard/prefix{$}': { id: '/params-ps/wildcard/prefix{$}' path: '/params-ps/wildcard/prefix{$}' fullPath: '/params-ps/wildcard/prefix{$}' preLoaderRoute: typeof ParamsPsWildcardPrefixChar123Char125RouteImport parentRoute: typeof rootRouteImport } '/params-ps/wildcard/prefix@대{$}': { id: '/params-ps/wildcard/prefix@대{$}' path: '/params-ps/wildcard/prefix@대{$}' fullPath: '/params-ps/wildcard/prefix@대{$}' preLoaderRoute: typeof ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport parentRoute: typeof rootRouteImport } '/params-ps/wildcard/$': { id: '/params-ps/wildcard/$' path: '/params-ps/wildcard/$' fullPath: '/params-ps/wildcard/$' preLoaderRoute: typeof ParamsPsWildcardSplatRouteImport parentRoute: typeof rootRouteImport } '/params-ps/named/{$foo}suffix': { id: '/params-ps/named/{$foo}suffix' path: '/params-ps/named/{$foo}suffix' fullPath: '/params-ps/named/{$foo}suffix' preLoaderRoute: typeof ParamsPsNamedChar123fooChar125suffixRouteImport parentRoute: typeof rootRouteImport } '/params-ps/named/prefix{$foo}': { id: '/params-ps/named/prefix{$foo}' path: '/params-ps/named/prefix{$foo}' fullPath: '/params-ps/named/prefix{$foo}' preLoaderRoute: typeof ParamsPsNamedPrefixChar123fooChar125RouteImport parentRoute: typeof rootRouteImport } '/masks/public/$username': { id: '/masks/public/$username' path: '/public/$username' fullPath: '/masks/public/$username' preLoaderRoute: typeof MasksPublicUsernameRouteImport parentRoute: typeof MasksRoute } '/masks/admin/$userId': { id: '/masks/admin/$userId' path: '/admin/$userId' fullPath: '/masks/admin/$userId' preLoaderRoute: typeof MasksAdminUserIdRouteImport parentRoute: typeof MasksRoute } '/fullpath-test/_layout/$id': { id: '/fullpath-test/_layout/$id' path: '/$id' fullPath: '/fullpath-test/$id' preLoaderRoute: typeof FullpathTestLayoutIdRouteImport parentRoute: typeof FullpathTestLayoutRouteRoute } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } '/(group)/subfolder/inside': { id: '/(group)/subfolder/inside' path: '/subfolder/inside' fullPath: '/subfolder/inside' preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout/insidelayout': { id: '/(group)/_layout/insidelayout' path: '/insidelayout' fullPath: '/insidelayout' preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport parentRoute: typeof groupLayoutRoute } '/params-ps/strict-false/$version': { id: '/params-ps/strict-false/$version' path: '/$version' fullPath: '/params-ps/strict-false/$version' preLoaderRoute: typeof ParamsPsStrictFalseVersionRouteRouteImport parentRoute: typeof ParamsPsStrictFalseRouteRoute } '/params-ps/non-nested/$foo_': { id: '/params-ps/non-nested/$foo_' path: '/$foo' fullPath: '/params-ps/non-nested/$foo' preLoaderRoute: typeof ParamsPsNonNestedFooRouteRouteImport parentRoute: typeof ParamsPsNonNestedRouteRoute } '/params-ps/named/$foo': { id: '/params-ps/named/$foo' path: '/params-ps/named/$foo' fullPath: '/params-ps/named/$foo' preLoaderRoute: typeof ParamsPsNamedFooRouteRouteImport parentRoute: typeof rootRouteImport } '/non-nested/suffix/{$baz}suffix': { id: '/non-nested/suffix/{$baz}suffix' path: '/{$baz}suffix' fullPath: '/non-nested/suffix/{$baz}suffix' preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRouteImport parentRoute: typeof NonNestedSuffixRouteRoute } '/non-nested/prefix/prefix{$baz}': { id: '/non-nested/prefix/prefix{$baz}' path: '/prefix{$baz}' fullPath: '/non-nested/prefix/prefix{$baz}' preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteImport parentRoute: typeof NonNestedPrefixRouteRoute } '/non-nested/path/baz': { id: '/non-nested/path/baz' path: '/baz' fullPath: '/non-nested/path/baz' preLoaderRoute: typeof NonNestedPathBazRouteRouteImport parentRoute: typeof NonNestedPathRouteRoute } '/non-nested/named/$baz': { id: '/non-nested/named/$baz' path: '/$baz' fullPath: '/non-nested/named/$baz' preLoaderRoute: typeof NonNestedNamedBazRouteRouteImport parentRoute: typeof NonNestedNamedRouteRoute } '/non-nested/deep/$baz': { id: '/non-nested/deep/$baz' path: '/$baz' fullPath: '/non-nested/deep/$baz' preLoaderRoute: typeof NonNestedDeepBazRouteRouteImport parentRoute: typeof NonNestedDeepRouteRoute } '/relative/useNavigate/with-search/': { id: '/relative/useNavigate/with-search/' path: '/with-search' fullPath: '/relative/useNavigate/with-search/' preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/path/': { id: '/relative/useNavigate/path/' path: '/path' fullPath: '/relative/useNavigate/path/' preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/nested/': { id: '/relative/useNavigate/nested/' path: '/nested' fullPath: '/relative/useNavigate/nested/' preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/with-search/': { id: '/relative/link/with-search/' path: '/with-search' fullPath: '/relative/link/with-search/' preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/path/': { id: '/relative/link/path/' path: '/path' fullPath: '/relative/link/path/' preLoaderRoute: typeof RelativeLinkPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/nested/': { id: '/relative/link/nested/' path: '/nested' fullPath: '/relative/link/nested/' preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/non-nested/suffix/{$baz}suffix/': { id: '/non-nested/suffix/{$baz}suffix/' path: '/' fullPath: '/non-nested/suffix/{$baz}suffix/' preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixIndexRouteImport parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute } '/non-nested/prefix/prefix{$baz}/': { id: '/non-nested/prefix/prefix{$baz}/' path: '/' fullPath: '/non-nested/prefix/prefix{$baz}/' preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125IndexRouteImport parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute } '/non-nested/path/baz/': { id: '/non-nested/path/baz/' path: '/' fullPath: '/non-nested/path/baz/' preLoaderRoute: typeof NonNestedPathBazIndexRouteImport parentRoute: typeof NonNestedPathBazRouteRoute } '/non-nested/named/$baz/': { id: '/non-nested/named/$baz/' path: '/' fullPath: '/non-nested/named/$baz/' preLoaderRoute: typeof NonNestedNamedBazIndexRouteImport parentRoute: typeof NonNestedNamedBazRouteRoute } '/non-nested/deep/$baz/': { id: '/non-nested/deep/$baz/' path: '/' fullPath: '/non-nested/deep/$baz/' preLoaderRoute: typeof NonNestedDeepBazIndexRouteImport parentRoute: typeof NonNestedDeepBazRouteRoute } '/params-ps/non-nested/$foo_/$bar': { id: '/params-ps/non-nested/$foo_/$bar' path: '/$bar' fullPath: '/params-ps/non-nested/$foo/$bar' preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport parentRoute: typeof ParamsPsNonNestedFooRouteRoute } '/non-nested/suffix/{$baz}suffix_/bar': { id: '/non-nested/suffix/{$baz}suffix_/bar' path: '/{$baz}suffix/bar' fullPath: '/non-nested/suffix/{$baz}suffix/bar' preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixBarRouteImport parentRoute: typeof NonNestedSuffixRouteRoute } '/non-nested/suffix/{$baz}suffix/foo': { id: '/non-nested/suffix/{$baz}suffix/foo' path: '/foo' fullPath: '/non-nested/suffix/{$baz}suffix/foo' preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixFooRouteImport parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute } '/non-nested/prefix/prefix{$baz}_/bar': { id: '/non-nested/prefix/prefix{$baz}_/bar' path: '/prefix{$baz}/bar' fullPath: '/non-nested/prefix/prefix{$baz}/bar' preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125BarRouteImport parentRoute: typeof NonNestedPrefixRouteRoute } '/non-nested/prefix/prefix{$baz}/foo': { id: '/non-nested/prefix/prefix{$baz}/foo' path: '/foo' fullPath: '/non-nested/prefix/prefix{$baz}/foo' preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125FooRouteImport parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute } '/non-nested/path/baz_/bar': { id: '/non-nested/path/baz_/bar' path: '/baz/bar' fullPath: '/non-nested/path/baz/bar' preLoaderRoute: typeof NonNestedPathBazBarRouteImport parentRoute: typeof NonNestedPathRouteRoute } '/non-nested/path/baz/foo': { id: '/non-nested/path/baz/foo' path: '/foo' fullPath: '/non-nested/path/baz/foo' preLoaderRoute: typeof NonNestedPathBazFooRouteImport parentRoute: typeof NonNestedPathBazRouteRoute } '/non-nested/named/$baz_/bar': { id: '/non-nested/named/$baz_/bar' path: '/$baz/bar' fullPath: '/non-nested/named/$baz/bar' preLoaderRoute: typeof NonNestedNamedBazBarRouteImport parentRoute: typeof NonNestedNamedRouteRoute } '/non-nested/named/$baz/foo': { id: '/non-nested/named/$baz/foo' path: '/foo' fullPath: '/non-nested/named/$baz/foo' preLoaderRoute: typeof NonNestedNamedBazFooRouteImport parentRoute: typeof NonNestedNamedBazRouteRoute } '/params-ps/named/$foo/$bar': { id: '/params-ps/named/$foo/$bar' path: '/$bar' fullPath: '/params-ps/named/$foo/$bar' preLoaderRoute: typeof ParamsPsNamedFooBarRouteRouteImport parentRoute: typeof ParamsPsNamedFooRouteRoute } '/non-nested/deep/$baz_/bar': { id: '/non-nested/deep/$baz_/bar' path: '/$baz/bar' fullPath: '/non-nested/deep/$baz/bar' preLoaderRoute: typeof NonNestedDeepBazBarRouteRouteImport parentRoute: typeof NonNestedDeepRouteRoute } '/relative/useNavigate/path/$path/': { id: '/relative/useNavigate/path/$path/' path: '/path/$path' fullPath: '/relative/useNavigate/path/$path/' preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/nested/deep/': { id: '/relative/useNavigate/nested/deep/' path: '/nested/deep' fullPath: '/relative/useNavigate/nested/deep/' preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/path/$path/': { id: '/relative/link/path/$path/' path: '/path/$path' fullPath: '/relative/link/path/$path/' preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/nested/deep/': { id: '/relative/link/nested/deep/' path: '/nested/deep' fullPath: '/relative/link/nested/deep/' preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/non-nested/deep/$baz_/bar/': { id: '/non-nested/deep/$baz_/bar/' path: '/' fullPath: '/non-nested/deep/$baz/bar/' preLoaderRoute: typeof NonNestedDeepBazBarIndexRouteImport parentRoute: typeof NonNestedDeepBazBarRouteRoute } '/params-ps/named/$foo/$bar/$baz': { id: '/params-ps/named/$foo/$bar/$baz' path: '/$baz' fullPath: '/params-ps/named/$foo/$bar/$baz' preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport parentRoute: typeof ParamsPsNamedFooBarRouteRoute } '/non-nested/deep/$baz_/bar_/qux': { id: '/non-nested/deep/$baz_/bar_/qux' path: '/$baz/bar/qux' fullPath: '/non-nested/deep/$baz/bar/qux' preLoaderRoute: typeof NonNestedDeepBazBarQuxRouteImport parentRoute: typeof NonNestedDeepRouteRoute } '/non-nested/deep/$baz_/bar/$foo': { id: '/non-nested/deep/$baz_/bar/$foo' path: '/$foo' fullPath: '/non-nested/deep/$baz/bar/$foo' preLoaderRoute: typeof NonNestedDeepBazBarFooRouteRouteImport parentRoute: typeof NonNestedDeepBazBarRouteRoute } '/non-nested/deep/$baz_/bar/$foo/': { id: '/non-nested/deep/$baz_/bar/$foo/' path: '/' fullPath: '/non-nested/deep/$baz/bar/$foo/' preLoaderRoute: typeof NonNestedDeepBazBarFooIndexRouteImport parentRoute: typeof NonNestedDeepBazBarFooRouteRoute } '/non-nested/deep/$baz_/bar/$foo_/qux': { id: '/non-nested/deep/$baz_/bar/$foo_/qux' path: '/$foo/qux' fullPath: '/non-nested/deep/$baz/bar/$foo/qux' preLoaderRoute: typeof NonNestedDeepBazBarFooQuxRouteImport parentRoute: typeof NonNestedDeepBazBarRouteRoute } } } interface FullpathTestLayoutRouteRouteChildren { FullpathTestLayoutIdRoute: typeof FullpathTestLayoutIdRoute FullpathTestLayoutIndexRoute: typeof FullpathTestLayoutIndexRoute } const FullpathTestLayoutRouteRouteChildren: FullpathTestLayoutRouteRouteChildren = { FullpathTestLayoutIdRoute: FullpathTestLayoutIdRoute, FullpathTestLayoutIndexRoute: FullpathTestLayoutIndexRoute, } const FullpathTestLayoutRouteRouteWithChildren = FullpathTestLayoutRouteRoute._addFileChildren( FullpathTestLayoutRouteRouteChildren, ) interface FullpathTestRouteRouteChildren { FullpathTestLayoutRouteRoute: typeof FullpathTestLayoutRouteRouteWithChildren } const FullpathTestRouteRouteChildren: FullpathTestRouteRouteChildren = { FullpathTestLayoutRouteRoute: FullpathTestLayoutRouteRouteWithChildren, } const FullpathTestRouteRouteWithChildren = FullpathTestRouteRoute._addFileChildren(FullpathTestRouteRouteChildren) interface NonNestedDeepBazRouteRouteChildren { NonNestedDeepBazIndexRoute: typeof NonNestedDeepBazIndexRoute } const NonNestedDeepBazRouteRouteChildren: NonNestedDeepBazRouteRouteChildren = { NonNestedDeepBazIndexRoute: NonNestedDeepBazIndexRoute, } const NonNestedDeepBazRouteRouteWithChildren = NonNestedDeepBazRouteRoute._addFileChildren( NonNestedDeepBazRouteRouteChildren, ) interface NonNestedDeepBazBarFooRouteRouteChildren { NonNestedDeepBazBarFooIndexRoute: typeof NonNestedDeepBazBarFooIndexRoute } const NonNestedDeepBazBarFooRouteRouteChildren: NonNestedDeepBazBarFooRouteRouteChildren = { NonNestedDeepBazBarFooIndexRoute: NonNestedDeepBazBarFooIndexRoute, } const NonNestedDeepBazBarFooRouteRouteWithChildren = NonNestedDeepBazBarFooRouteRoute._addFileChildren( NonNestedDeepBazBarFooRouteRouteChildren, ) interface NonNestedDeepBazBarRouteRouteChildren { NonNestedDeepBazBarFooRouteRoute: typeof NonNestedDeepBazBarFooRouteRouteWithChildren NonNestedDeepBazBarIndexRoute: typeof NonNestedDeepBazBarIndexRoute NonNestedDeepBazBarFooQuxRoute: typeof NonNestedDeepBazBarFooQuxRoute } const NonNestedDeepBazBarRouteRouteChildren: NonNestedDeepBazBarRouteRouteChildren = { NonNestedDeepBazBarFooRouteRoute: NonNestedDeepBazBarFooRouteRouteWithChildren, NonNestedDeepBazBarIndexRoute: NonNestedDeepBazBarIndexRoute, NonNestedDeepBazBarFooQuxRoute: NonNestedDeepBazBarFooQuxRoute, } const NonNestedDeepBazBarRouteRouteWithChildren = NonNestedDeepBazBarRouteRoute._addFileChildren( NonNestedDeepBazBarRouteRouteChildren, ) interface NonNestedDeepRouteRouteChildren { NonNestedDeepBazRouteRoute: typeof NonNestedDeepBazRouteRouteWithChildren NonNestedDeepBazBarRouteRoute: typeof NonNestedDeepBazBarRouteRouteWithChildren NonNestedDeepBazBarQuxRoute: typeof NonNestedDeepBazBarQuxRoute } const NonNestedDeepRouteRouteChildren: NonNestedDeepRouteRouteChildren = { NonNestedDeepBazRouteRoute: NonNestedDeepBazRouteRouteWithChildren, NonNestedDeepBazBarRouteRoute: NonNestedDeepBazBarRouteRouteWithChildren, NonNestedDeepBazBarQuxRoute: NonNestedDeepBazBarQuxRoute, } const NonNestedDeepRouteRouteWithChildren = NonNestedDeepRouteRoute._addFileChildren(NonNestedDeepRouteRouteChildren) interface NonNestedNamedBazRouteRouteChildren { NonNestedNamedBazFooRoute: typeof NonNestedNamedBazFooRoute NonNestedNamedBazIndexRoute: typeof NonNestedNamedBazIndexRoute } const NonNestedNamedBazRouteRouteChildren: NonNestedNamedBazRouteRouteChildren = { NonNestedNamedBazFooRoute: NonNestedNamedBazFooRoute, NonNestedNamedBazIndexRoute: NonNestedNamedBazIndexRoute, } const NonNestedNamedBazRouteRouteWithChildren = NonNestedNamedBazRouteRoute._addFileChildren( NonNestedNamedBazRouteRouteChildren, ) interface NonNestedNamedRouteRouteChildren { NonNestedNamedBazRouteRoute: typeof NonNestedNamedBazRouteRouteWithChildren NonNestedNamedBazBarRoute: typeof NonNestedNamedBazBarRoute } const NonNestedNamedRouteRouteChildren: NonNestedNamedRouteRouteChildren = { NonNestedNamedBazRouteRoute: NonNestedNamedBazRouteRouteWithChildren, NonNestedNamedBazBarRoute: NonNestedNamedBazBarRoute, } const NonNestedNamedRouteRouteWithChildren = NonNestedNamedRouteRoute._addFileChildren(NonNestedNamedRouteRouteChildren) interface NonNestedPathBazRouteRouteChildren { NonNestedPathBazFooRoute: typeof NonNestedPathBazFooRoute NonNestedPathBazIndexRoute: typeof NonNestedPathBazIndexRoute } const NonNestedPathBazRouteRouteChildren: NonNestedPathBazRouteRouteChildren = { NonNestedPathBazFooRoute: NonNestedPathBazFooRoute, NonNestedPathBazIndexRoute: NonNestedPathBazIndexRoute, } const NonNestedPathBazRouteRouteWithChildren = NonNestedPathBazRouteRoute._addFileChildren( NonNestedPathBazRouteRouteChildren, ) interface NonNestedPathRouteRouteChildren { NonNestedPathBazRouteRoute: typeof NonNestedPathBazRouteRouteWithChildren NonNestedPathBazBarRoute: typeof NonNestedPathBazBarRoute } const NonNestedPathRouteRouteChildren: NonNestedPathRouteRouteChildren = { NonNestedPathBazRouteRoute: NonNestedPathBazRouteRouteWithChildren, NonNestedPathBazBarRoute: NonNestedPathBazBarRoute, } const NonNestedPathRouteRouteWithChildren = NonNestedPathRouteRoute._addFileChildren(NonNestedPathRouteRouteChildren) interface NonNestedPrefixPrefixChar123bazChar125RouteRouteChildren { NonNestedPrefixPrefixChar123bazChar125FooRoute: typeof NonNestedPrefixPrefixChar123bazChar125FooRoute NonNestedPrefixPrefixChar123bazChar125IndexRoute: typeof NonNestedPrefixPrefixChar123bazChar125IndexRoute } const NonNestedPrefixPrefixChar123bazChar125RouteRouteChildren: NonNestedPrefixPrefixChar123bazChar125RouteRouteChildren = { NonNestedPrefixPrefixChar123bazChar125FooRoute: NonNestedPrefixPrefixChar123bazChar125FooRoute, NonNestedPrefixPrefixChar123bazChar125IndexRoute: NonNestedPrefixPrefixChar123bazChar125IndexRoute, } const NonNestedPrefixPrefixChar123bazChar125RouteRouteWithChildren = NonNestedPrefixPrefixChar123bazChar125RouteRoute._addFileChildren( NonNestedPrefixPrefixChar123bazChar125RouteRouteChildren, ) interface NonNestedPrefixRouteRouteChildren { NonNestedPrefixPrefixChar123bazChar125RouteRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteWithChildren NonNestedPrefixPrefixChar123bazChar125BarRoute: typeof NonNestedPrefixPrefixChar123bazChar125BarRoute } const NonNestedPrefixRouteRouteChildren: NonNestedPrefixRouteRouteChildren = { NonNestedPrefixPrefixChar123bazChar125RouteRoute: NonNestedPrefixPrefixChar123bazChar125RouteRouteWithChildren, NonNestedPrefixPrefixChar123bazChar125BarRoute: NonNestedPrefixPrefixChar123bazChar125BarRoute, } const NonNestedPrefixRouteRouteWithChildren = NonNestedPrefixRouteRoute._addFileChildren(NonNestedPrefixRouteRouteChildren) interface NonNestedSuffixChar123bazChar125suffixRouteRouteChildren { NonNestedSuffixChar123bazChar125suffixFooRoute: typeof NonNestedSuffixChar123bazChar125suffixFooRoute NonNestedSuffixChar123bazChar125suffixIndexRoute: typeof NonNestedSuffixChar123bazChar125suffixIndexRoute } const NonNestedSuffixChar123bazChar125suffixRouteRouteChildren: NonNestedSuffixChar123bazChar125suffixRouteRouteChildren = { NonNestedSuffixChar123bazChar125suffixFooRoute: NonNestedSuffixChar123bazChar125suffixFooRoute, NonNestedSuffixChar123bazChar125suffixIndexRoute: NonNestedSuffixChar123bazChar125suffixIndexRoute, } const NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren = NonNestedSuffixChar123bazChar125suffixRouteRoute._addFileChildren( NonNestedSuffixChar123bazChar125suffixRouteRouteChildren, ) interface NonNestedSuffixRouteRouteChildren { NonNestedSuffixChar123bazChar125suffixRouteRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren NonNestedSuffixChar123bazChar125suffixBarRoute: typeof NonNestedSuffixChar123bazChar125suffixBarRoute } const NonNestedSuffixRouteRouteChildren: NonNestedSuffixRouteRouteChildren = { NonNestedSuffixChar123bazChar125suffixRouteRoute: NonNestedSuffixChar123bazChar125suffixRouteRouteWithChildren, NonNestedSuffixChar123bazChar125suffixBarRoute: NonNestedSuffixChar123bazChar125suffixBarRoute, } const NonNestedSuffixRouteRouteWithChildren = NonNestedSuffixRouteRoute._addFileChildren(NonNestedSuffixRouteRouteChildren) interface NonNestedRouteRouteChildren { NonNestedDeepRouteRoute: typeof NonNestedDeepRouteRouteWithChildren NonNestedNamedRouteRoute: typeof NonNestedNamedRouteRouteWithChildren NonNestedPathRouteRoute: typeof NonNestedPathRouteRouteWithChildren NonNestedPrefixRouteRoute: typeof NonNestedPrefixRouteRouteWithChildren NonNestedSuffixRouteRoute: typeof NonNestedSuffixRouteRouteWithChildren } const NonNestedRouteRouteChildren: NonNestedRouteRouteChildren = { NonNestedDeepRouteRoute: NonNestedDeepRouteRouteWithChildren, NonNestedNamedRouteRoute: NonNestedNamedRouteRouteWithChildren, NonNestedPathRouteRoute: NonNestedPathRouteRouteWithChildren, NonNestedPrefixRouteRoute: NonNestedPrefixRouteRouteWithChildren, NonNestedSuffixRouteRoute: NonNestedSuffixRouteRouteWithChildren, } const NonNestedRouteRouteWithChildren = NonNestedRouteRoute._addFileChildren( NonNestedRouteRouteChildren, ) interface PathlessLayoutLayoutRouteRouteChildren { PathlessLayoutLayoutChildRoute: typeof PathlessLayoutLayoutChildRoute PathlessLayoutLayoutIndexRoute: typeof PathlessLayoutLayoutIndexRoute } const PathlessLayoutLayoutRouteRouteChildren: PathlessLayoutLayoutRouteRouteChildren = { PathlessLayoutLayoutChildRoute: PathlessLayoutLayoutChildRoute, PathlessLayoutLayoutIndexRoute: PathlessLayoutLayoutIndexRoute, } const PathlessLayoutLayoutRouteRouteWithChildren = PathlessLayoutLayoutRouteRoute._addFileChildren( PathlessLayoutLayoutRouteRouteChildren, ) interface PathlessLayoutRouteRouteChildren { PathlessLayoutLayoutRouteRoute: typeof PathlessLayoutLayoutRouteRouteWithChildren } const PathlessLayoutRouteRouteChildren: PathlessLayoutRouteRouteChildren = { PathlessLayoutLayoutRouteRoute: PathlessLayoutLayoutRouteRouteWithChildren, } const PathlessLayoutRouteRouteWithChildren = PathlessLayoutRouteRoute._addFileChildren(PathlessLayoutRouteRouteChildren) interface SearchParamsRouteRouteChildren { SearchParamsDefaultRoute: typeof SearchParamsDefaultRoute SearchParamsIndexRoute: typeof SearchParamsIndexRoute } const SearchParamsRouteRouteChildren: SearchParamsRouteRouteChildren = { SearchParamsDefaultRoute: SearchParamsDefaultRoute, SearchParamsIndexRoute: SearchParamsIndexRoute, } const SearchParamsRouteRouteWithChildren = SearchParamsRouteRoute._addFileChildren(SearchParamsRouteRouteChildren) interface Char45824Char54620Char48124Char44397RouteRouteChildren { Char45824Char54620Char48124Char44397WildcardSplatRoute: typeof Char45824Char54620Char48124Char44397WildcardSplatRoute Char45824Char54620Char48124Char44397Char55357Char56960IdRoute: typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute } const Char45824Char54620Char48124Char44397RouteRouteChildren: Char45824Char54620Char48124Char44397RouteRouteChildren = { Char45824Char54620Char48124Char44397WildcardSplatRoute: Char45824Char54620Char48124Char44397WildcardSplatRoute, Char45824Char54620Char48124Char44397Char55357Char56960IdRoute: Char45824Char54620Char48124Char44397Char55357Char56960IdRoute, } const Char45824Char54620Char48124Char44397RouteRouteWithChildren = Char45824Char54620Char48124Char44397RouteRoute._addFileChildren( Char45824Char54620Char48124Char44397RouteRouteChildren, ) interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface MasksRouteChildren { MasksAdminUserIdRoute: typeof MasksAdminUserIdRoute MasksPublicUsernameRoute: typeof MasksPublicUsernameRoute } const MasksRouteChildren: MasksRouteChildren = { MasksAdminUserIdRoute: MasksAdminUserIdRoute, MasksPublicUsernameRoute: MasksPublicUsernameRoute, } const MasksRouteWithChildren = MasksRoute._addFileChildren(MasksRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) interface ParamsPsNonNestedFooRouteRouteChildren { ParamsPsNonNestedFooBarRoute: typeof ParamsPsNonNestedFooBarRoute } const ParamsPsNonNestedFooRouteRouteChildren: ParamsPsNonNestedFooRouteRouteChildren = { ParamsPsNonNestedFooBarRoute: ParamsPsNonNestedFooBarRoute, } const ParamsPsNonNestedFooRouteRouteWithChildren = ParamsPsNonNestedFooRouteRoute._addFileChildren( ParamsPsNonNestedFooRouteRouteChildren, ) interface ParamsPsNonNestedRouteRouteChildren { ParamsPsNonNestedFooRouteRoute: typeof ParamsPsNonNestedFooRouteRouteWithChildren } const ParamsPsNonNestedRouteRouteChildren: ParamsPsNonNestedRouteRouteChildren = { ParamsPsNonNestedFooRouteRoute: ParamsPsNonNestedFooRouteRouteWithChildren, } const ParamsPsNonNestedRouteRouteWithChildren = ParamsPsNonNestedRouteRoute._addFileChildren( ParamsPsNonNestedRouteRouteChildren, ) interface ParamsPsStrictFalseRouteRouteChildren { ParamsPsStrictFalseVersionRouteRoute: typeof ParamsPsStrictFalseVersionRouteRoute } const ParamsPsStrictFalseRouteRouteChildren: ParamsPsStrictFalseRouteRouteChildren = { ParamsPsStrictFalseVersionRouteRoute: ParamsPsStrictFalseVersionRouteRoute, } const ParamsPsStrictFalseRouteRouteWithChildren = ParamsPsStrictFalseRouteRoute._addFileChildren( ParamsPsStrictFalseRouteRouteChildren, ) interface RelativeLinkRouteRouteChildren { RelativeLinkRelativeLinkARoute: typeof RelativeLinkRelativeLinkARoute RelativeLinkRelativeLinkBRoute: typeof RelativeLinkRelativeLinkBRoute RelativeLinkNestedIndexRoute: typeof RelativeLinkNestedIndexRoute RelativeLinkPathIndexRoute: typeof RelativeLinkPathIndexRoute RelativeLinkWithSearchIndexRoute: typeof RelativeLinkWithSearchIndexRoute RelativeLinkNestedDeepIndexRoute: typeof RelativeLinkNestedDeepIndexRoute RelativeLinkPathPathIndexRoute: typeof RelativeLinkPathPathIndexRoute } const RelativeLinkRouteRouteChildren: RelativeLinkRouteRouteChildren = { RelativeLinkRelativeLinkARoute: RelativeLinkRelativeLinkARoute, RelativeLinkRelativeLinkBRoute: RelativeLinkRelativeLinkBRoute, RelativeLinkNestedIndexRoute: RelativeLinkNestedIndexRoute, RelativeLinkPathIndexRoute: RelativeLinkPathIndexRoute, RelativeLinkWithSearchIndexRoute: RelativeLinkWithSearchIndexRoute, RelativeLinkNestedDeepIndexRoute: RelativeLinkNestedDeepIndexRoute, RelativeLinkPathPathIndexRoute: RelativeLinkPathPathIndexRoute, } const RelativeLinkRouteRouteWithChildren = RelativeLinkRouteRoute._addFileChildren(RelativeLinkRouteRouteChildren) interface RelativeUseNavigateRouteRouteChildren { RelativeUseNavigateRelativeUseNavigateARoute: typeof RelativeUseNavigateRelativeUseNavigateARoute RelativeUseNavigateRelativeUseNavigateBRoute: typeof RelativeUseNavigateRelativeUseNavigateBRoute RelativeUseNavigateNestedIndexRoute: typeof RelativeUseNavigateNestedIndexRoute RelativeUseNavigatePathIndexRoute: typeof RelativeUseNavigatePathIndexRoute RelativeUseNavigateWithSearchIndexRoute: typeof RelativeUseNavigateWithSearchIndexRoute RelativeUseNavigateNestedDeepIndexRoute: typeof RelativeUseNavigateNestedDeepIndexRoute RelativeUseNavigatePathPathIndexRoute: typeof RelativeUseNavigatePathPathIndexRoute } const RelativeUseNavigateRouteRouteChildren: RelativeUseNavigateRouteRouteChildren = { RelativeUseNavigateRelativeUseNavigateARoute: RelativeUseNavigateRelativeUseNavigateARoute, RelativeUseNavigateRelativeUseNavigateBRoute: RelativeUseNavigateRelativeUseNavigateBRoute, RelativeUseNavigateNestedIndexRoute: RelativeUseNavigateNestedIndexRoute, RelativeUseNavigatePathIndexRoute: RelativeUseNavigatePathIndexRoute, RelativeUseNavigateWithSearchIndexRoute: RelativeUseNavigateWithSearchIndexRoute, RelativeUseNavigateNestedDeepIndexRoute: RelativeUseNavigateNestedDeepIndexRoute, RelativeUseNavigatePathPathIndexRoute: RelativeUseNavigatePathPathIndexRoute, } const RelativeUseNavigateRouteRouteWithChildren = RelativeUseNavigateRouteRoute._addFileChildren( RelativeUseNavigateRouteRouteChildren, ) interface groupLayoutRouteChildren { groupLayoutInsidelayoutRoute: typeof groupLayoutInsidelayoutRoute } const groupLayoutRouteChildren: groupLayoutRouteChildren = { groupLayoutInsidelayoutRoute: groupLayoutInsidelayoutRoute, } const groupLayoutRouteWithChildren = groupLayoutRoute._addFileChildren( groupLayoutRouteChildren, ) interface RedirectTargetRouteChildren { RedirectTargetDestinationRoute: typeof RedirectTargetDestinationRoute RedirectTargetViaBeforeLoadRoute: typeof RedirectTargetViaBeforeLoadRoute RedirectTargetViaLoaderRoute: typeof RedirectTargetViaLoaderRoute RedirectTargetViaRouteRedirectBeforeLoadRoute: typeof RedirectTargetViaRouteRedirectBeforeLoadRoute RedirectTargetViaRouteRedirectLoaderRoute: typeof RedirectTargetViaRouteRedirectLoaderRoute RedirectTargetViaRouteApiRedirectBeforeLoadRoute: typeof RedirectTargetViaRouteApiRedirectBeforeLoadRoute RedirectTargetViaRouteApiRedirectLoaderRoute: typeof RedirectTargetViaRouteApiRedirectLoaderRoute RedirectTargetIndexRoute: typeof RedirectTargetIndexRoute } const RedirectTargetRouteChildren: RedirectTargetRouteChildren = { RedirectTargetDestinationRoute: RedirectTargetDestinationRoute, RedirectTargetViaBeforeLoadRoute: RedirectTargetViaBeforeLoadRoute, RedirectTargetViaLoaderRoute: RedirectTargetViaLoaderRoute, RedirectTargetViaRouteRedirectBeforeLoadRoute: RedirectTargetViaRouteRedirectBeforeLoadRoute, RedirectTargetViaRouteRedirectLoaderRoute: RedirectTargetViaRouteRedirectLoaderRoute, RedirectTargetViaRouteApiRedirectBeforeLoadRoute: RedirectTargetViaRouteApiRedirectBeforeLoadRoute, RedirectTargetViaRouteApiRedirectLoaderRoute: RedirectTargetViaRouteApiRedirectLoaderRoute, RedirectTargetIndexRoute: RedirectTargetIndexRoute, } const RedirectTargetRouteWithChildren = RedirectTargetRoute._addFileChildren( RedirectTargetRouteChildren, ) interface ParamsPsNamedFooBarRouteRouteChildren { ParamsPsNamedFooBarBazRoute: typeof ParamsPsNamedFooBarBazRoute } const ParamsPsNamedFooBarRouteRouteChildren: ParamsPsNamedFooBarRouteRouteChildren = { ParamsPsNamedFooBarBazRoute: ParamsPsNamedFooBarBazRoute, } const ParamsPsNamedFooBarRouteRouteWithChildren = ParamsPsNamedFooBarRouteRoute._addFileChildren( ParamsPsNamedFooBarRouteRouteChildren, ) interface ParamsPsNamedFooRouteRouteChildren { ParamsPsNamedFooBarRouteRoute: typeof ParamsPsNamedFooBarRouteRouteWithChildren } const ParamsPsNamedFooRouteRouteChildren: ParamsPsNamedFooRouteRouteChildren = { ParamsPsNamedFooBarRouteRoute: ParamsPsNamedFooBarRouteRouteWithChildren, } const ParamsPsNamedFooRouteRouteWithChildren = ParamsPsNamedFooRouteRoute._addFileChildren( ParamsPsNamedFooRouteRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, FullpathTestRouteRoute: FullpathTestRouteRouteWithChildren, NonNestedRouteRoute: NonNestedRouteRouteWithChildren, PathlessLayoutRouteRoute: PathlessLayoutRouteRouteWithChildren, SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren, Char45824Char54620Char48124Char44397RouteRoute: Char45824Char54620Char48124Char44397RouteRouteWithChildren, LayoutRoute: LayoutRouteWithChildren, AnchorRoute: AnchorRoute, ComponentTypesTestRoute: ComponentTypesTestRoute, EditingARoute: EditingARoute, EditingBRoute: EditingBRoute, HoverPreloadHashRoute: HoverPreloadHashRoute, LazyErrorRoute: LazyErrorRoute, MasksRoute: MasksRouteWithChildren, NotRemountDepsRoute: NotRemountDepsRoute, PostsRoute: PostsRouteWithChildren, RemountDepsRoute: RemountDepsRoute, ParamsPsNonNestedRouteRoute: ParamsPsNonNestedRouteRouteWithChildren, ParamsPsStrictFalseRouteRoute: ParamsPsStrictFalseRouteRouteWithChildren, RelativeLinkRouteRoute: RelativeLinkRouteRouteWithChildren, RelativeUseNavigateRouteRoute: RelativeUseNavigateRouteRouteWithChildren, anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute, groupLayoutRoute: groupLayoutRouteWithChildren, groupInsideRoute: groupInsideRoute, groupLazyinsideRoute: groupLazyinsideRoute, RedirectTargetRoute: RedirectTargetRouteWithChildren, StructuralSharingEnabledRoute: StructuralSharingEnabledRoute, ParamsPsIndexRoute: ParamsPsIndexRoute, RedirectIndexRoute: RedirectIndexRoute, RelativeIndexRoute: RelativeIndexRoute, ParamsPsNamedFooRouteRoute: ParamsPsNamedFooRouteRouteWithChildren, groupSubfolderInsideRoute: groupSubfolderInsideRoute, ParamsPsNamedPrefixChar123fooChar125Route: ParamsPsNamedPrefixChar123fooChar125Route, ParamsPsNamedChar123fooChar125suffixRoute: ParamsPsNamedChar123fooChar125suffixRoute, ParamsPsWildcardSplatRoute: ParamsPsWildcardSplatRoute, ParamsPsWildcardPrefixAtChar45824Char123Char125Route: ParamsPsWildcardPrefixAtChar45824Char123Char125Route, ParamsPsWildcardPrefixChar123Char125Route: ParamsPsWildcardPrefixChar123Char125Route, ParamsPsWildcardChar123Char125suffixRoute: ParamsPsWildcardChar123Char125suffixRoute, ParamsPsWildcardChar123Char125suffixAtChar45824Route: ParamsPsWildcardChar123Char125suffixAtChar45824Route, ParamsSingleValueRoute: ParamsSingleValueRoute, PostsPostIdEditRoute: PostsPostIdEditRoute, RedirectPreloadFirstRoute: RedirectPreloadFirstRoute, RedirectPreloadSecondRoute: RedirectPreloadSecondRoute, RedirectPreloadThirdRoute: RedirectPreloadThirdRoute, ParamsPsNamedIndexRoute: ParamsPsNamedIndexRoute, ParamsPsWildcardIndexRoute: ParamsPsWildcardIndexRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(another-group)/onlyrouteinside.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { getRouteApi, useSearch } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' const routeApi = getRouteApi('/(another-group)/onlyrouteinside') export const Route = createFileRoute('/(another-group)/onlyrouteinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: () => { const searchViaHook = useSearch({ from: '/(another-group)/onlyrouteinside', }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(group)/_layout.insidelayout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { getRouteApi, useSearch } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' const routeApi = getRouteApi('/(group)/_layout/insidelayout') export const Route = createFileRoute('/(group)/_layout/insidelayout')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: () => { const searchViaHook = useSearch({ from: '/(group)/_layout/insidelayout' }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(group)/_layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/(group)/_layout')({ component: () => ( <>
/(group)/_layout!
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(group)/inside.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { getRouteApi, useSearch } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' const routeApi = getRouteApi('/(group)/inside') export const Route = createFileRoute('/(group)/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: () => { const searchViaHook = useSearch({ from: '/(group)/inside' }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(group)/lazyinside.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/react-router' import { getRouteApi, useSearch } from '@tanstack/react-router' const routeApi = getRouteApi('/(group)/lazyinside') export const Route = createLazyFileRoute('/(group)/lazyinside')({ component: () => { const searchViaHook = useSearch({ from: '/(group)/lazyinside' }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(group)/lazyinside.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/lazyinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/(group)/subfolder/inside.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { getRouteApi, useSearch } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' const routeApi = getRouteApi('/(group)/subfolder/inside') export const Route = createFileRoute('/(group)/subfolder/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: () => { const searchViaHook = useSearch({ from: '/(group)/subfolder/inside' }) const searchViaRouteHook = Route.useSearch() const searchViaRouteApi = routeApi.useSearch() return ( <>
{searchViaHook.hello}
{searchViaRouteHook.hello}
{searchViaRouteApi.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, useCanGoBack, useRouter, useRouterState, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { const router = useRouter() const canGoBack = useCanGoBack() // test useRouterState doesn't crash client side navigation const _state = useRouterState() return ( <>
{' '} Home {' '} Posts {' '} Layout {' '} Only Route Inside Group {' '} Inside Group {' '} Inside Subfolder Inside Group {' '} Inside Group Inside Layout {' '} Lazy Inside Group {' '} redirect {' '} relative routing {' '} unicode path {' '} This Route Does Not Exist {' '} Masks {' '} Pathless Layout {' '} FullPath Test

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/_layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/anchor.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { useLayoutEffect, useRef, useState } from 'react' import { Link, useLocation, useNavigate } from '@tanstack/react-router' export const Route = createFileRoute('/anchor')({ component: AnchorComponent, }) const anchors: Array<{ id: string title: string hashScrollIntoView?: boolean | ScrollIntoViewOptions }> = [ { id: 'default-anchor', title: 'Default Anchor', }, { id: 'false-anchor', title: 'No Scroll Into View', hashScrollIntoView: false, }, { id: 'smooth-scroll', title: 'Smooth Scroll', hashScrollIntoView: { behavior: 'smooth' }, }, ] as const function AnchorSection({ id, title }: { id: string; title: string }) { const [hasShown, setHasShown] = useState(false) const elementRef = useRef(null) useLayoutEffect(() => { const observer = new IntersectionObserver( ([entry]) => { if (!hasShown && entry.isIntersecting) { setHasShown(true) } }, { threshold: 0.01 }, ) const currentRef = elementRef.current if (currentRef) { observer.observe(currentRef) } return () => { if (currentRef) { observer.unobserve(currentRef) } } }, [hasShown]) return (

{title} {hasShown ? ' (shown)' : ''}

) } function AnchorComponent() { const navigate = useNavigate() const location = useLocation() const [withScroll, setWithScroll] = useState(true) return (
{ event.preventDefault() event.stopPropagation() const formData = new FormData(event.target as HTMLFormElement) const toHash = formData.get('hash') as string if (!toHash) { return } const hashScrollIntoView = withScroll ? ({ behavior: formData.get('scrollBehavior') as ScrollBehavior, block: formData.get('scrollBlock') as ScrollLogicalPosition, inline: formData.get('scrollInline') as ScrollLogicalPosition, } satisfies ScrollIntoViewOptions) : false navigate({ hash: toHash, hashScrollIntoView }) }} >

Scroll with navigate

{withScroll ? ( <>
) : null}
{anchors.map((anchor) => ( ))}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/component-types-test.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // Test route for type safety of different component types // This test should // - check that `false` can be set on the `errorComponent` // ...without causing build-time TypeScript errors export const Route = createFileRoute('/component-types-test')({ component: () =>
Testing test types
, errorComponent: false, // Should not cause TypeScript error pendingComponent: undefined, // Should not cause TypeScript error notFoundComponent: undefined, // Should not cause TypeScript error }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/editing-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { useBlocker } from '@tanstack/react-router' export const Route = createFileRoute('/editing-a')({ component: RouteComponent, }) function RouteComponent() { const navigate = Route.useNavigate() const [input, setInput] = React.useState('') const { proceed, status } = useBlocker({ shouldBlockFn: ({ next }) => { if (next.fullPath === '/editing-b' && input.length > 0) { return true } return false }, withResolver: true, }) return (

Editing A

{status === 'blocked' && ( )}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/editing-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { useBlocker } from '@tanstack/react-router' export const Route = createFileRoute('/editing-b')({ component: RouteComponent, }) function RouteComponent() { const navigate = Route.useNavigate() const [input, setInput] = React.useState('') const { proceed, status } = useBlocker({ condition: input, }) return (

Editing B

{status === 'blocked' && ( )}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/$id.tsx ================================================ import { createFileRoute, useMatches } from '@tanstack/react-router' export const Route = createFileRoute('/fullpath-test/_layout/$id')({ component: IdComponent, }) function IdComponent() { const matches = useMatches() const { id } = Route.useParams() // Find this route's match by routeId const idMatch = matches.find( (m) => m.routeId === '/fullpath-test/_layout/$id', ) return (
{idMatch?.fullPath ?? 'undefined'}
Param: {id}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/index.tsx ================================================ import { createFileRoute, useMatches } from '@tanstack/react-router' export const Route = createFileRoute('/fullpath-test/_layout/')({ component: IndexComponent, }) function IndexComponent() { const matches = useMatches() // Find this index route's match by routeId const indexMatch = matches.find( (m) => m.routeId === '/fullpath-test/_layout/', ) return (
{indexMatch?.fullPath ?? 'undefined'}
{Route.to}
FullPath Test Index
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/route.tsx ================================================ import { Outlet, createFileRoute, useMatches } from '@tanstack/react-router' export const Route = createFileRoute('/fullpath-test/_layout')({ component: LayoutComponent, }) function LayoutComponent() { const matches = useMatches() // Find this layout's match by routeId const layoutMatch = matches.find( (m) => m.routeId === '/fullpath-test/_layout', ) return (
{layoutMatch?.fullPath ?? 'undefined'}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/fullpath-test/route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/fullpath-test')({ component: () => (

FullPath Test Section

), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/hover-preload-hash.tsx ================================================ import { Link, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/hover-preload-hash')({ component: Page, }) function Page() { return ( <>
Hello from About!
To hash

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Hash is here

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/lazy-error.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/react-router' export const Route = createLazyFileRoute('/lazy-error')({ component: LazyErrorComponent, errorComponent: () => (
Lazy route error component
), }) function LazyErrorComponent() { return
Lazy route component
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/lazy-error.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/lazy-error')({ loader: () => { throw new Error('Lazy route loader error') }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/masks.admin.$userId.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/masks/admin/$userId')({ component: AdminUserRoute, }) function AdminUserRoute() { const params = Route.useParams() return (
{params.userId}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/masks.public.$username.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/masks/public/$username')({ component: PublicUserRoute, }) function PublicUserRoute() { const params = Route.useParams() return (
{params.username}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/masks.tsx ================================================ import { Link, Outlet, createFileRoute, useRouterState, } from '@tanstack/react-router' export const Route = createFileRoute('/masks')({ component: MasksLayout, }) function MasksLayout() { const location = useRouterState({ select: (state) => state.location, }) return (

Route Masks

{location.pathname}
{location.maskedLocation?.pathname ?? ''}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello deeply nested baz index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested named baz route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz_.bar.$foo.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz_/bar/$foo/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello deeply nested baz/bar/foo index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz_.bar.$foo.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz_/bar/$foo')({ component: RouteComponent, }) function RouteComponent() { return (
Hello deeply nested named baz/bar/foo route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz_.bar.$foo_.qux.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz_/bar/$foo_/qux')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello deeply nested baz/bar/foo/qux
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz_.bar.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz_/bar/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello deeply nested baz/bar index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz_.bar.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz_/bar')({ component: RouteComponent, }) function RouteComponent() { return (
Hello deeply nested baz/bar route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/$baz_.bar_.qux.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep/$baz_/bar_/qux')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello deeply nested baz/bar/qux
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/deep/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/deep')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested deep layout
To Baz To named baz/bar To named baz/bar/foo To named baz/bar/foo/qux To named baz/bar/qux
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz.foo.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/named/$baz/foo')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested named baz foo page
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/named/$baz/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested named baz index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/named/$baz')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested named baz route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/named/$baz_/bar')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello non-nested named bar
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/named/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/named')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested named layout
To named index To named foo To named foo 2 To named bar To named bar 2
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/path/baz.foo.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/path/baz/foo')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested path baz foo page
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/path/baz.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/path/baz/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested path baz index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/path/baz.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/path/baz')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested path baz route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/path/baz_.bar.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/path/baz_/bar')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello non-nested path bar
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/path/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/path')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested path layout
To path index To path foo To path foo 2 To path bar To path bar 2
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/prefix/prefix{$baz}.foo.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/prefix/prefix{$baz}/foo')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested prefix foo page
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/prefix/prefix{$baz}.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/prefix/prefix{$baz}/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested prefix index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/prefix/prefix{$baz}.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/prefix/prefix{$baz}')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested prefix route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/prefix/prefix{$baz}_.bar.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/prefix/prefix{$baz}_/bar')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello non-nested prefix bar
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/prefix/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/prefix')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested prefix layout
To prefix index To prefix foo To prefix foo 2 To prefix bar To prefix bar 2
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested')({ component: RouteComponent, }) function RouteComponent() { return (

Non-nested paths

  • To named param tests To prefix param tests To suffix param tests To path tests
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/suffix/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/suffix')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested suffix layout
To suffix index To suffix foo To suffix foo 2 To suffix bar To suffix bar 2
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/suffix/{$baz}suffix.foo.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/suffix/{$baz}suffix/foo')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested suffix foo page
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/suffix/{$baz}suffix.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/suffix/{$baz}suffix/')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello nested suffix index
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/suffix/{$baz}suffix.route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/suffix/{$baz}suffix')({ component: RouteComponent, }) function RouteComponent() { return (
Hello non-nested suffix route layout
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/non-nested/suffix/{$baz}suffix_.bar.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/non-nested/suffix/{$baz}suffix_/bar')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello non-nested suffix bar
{JSON.stringify(params)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/notRemountDeps.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/notRemountDeps')({ validateSearch(search: { searchParam: string }) { return { searchParam: search.searchParam } }, loaderDeps(opts) { return opts.search }, component: Home, remountDeps(opts) { return opts.params }, }) let counter = 0 function Home() { const search = Route.useSearch() const navigate = Route.useNavigate() const [mounts, setMounts] = React.useState(counter) React.useEffect(() => { setMounts(++counter) }, []) return (
Search: {search.searchParam}
Page component mounts: {mounts}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/')({ component: RouteComponent, }) function RouteComponent() { return (

Named path params

  • /params-ps/named/$foo
  • /params-ps/named/$foo - with special characters
  • /params-ps/named/{'prefix{$foo}'}
  • /params-ps/named/{'{$foo}suffix'}

Wildcard path params

  • /params-ps/wildcard/$
  • /params-ps/wildcard/$ with escaped params
  • /params-ps/wildcard/$ with encoded params
  • /params-ps/wildcard/{'prefix{$}'}
  • /params-ps/wildcard/{'prefix@대{$}'}
  • /params-ps/wildcard/{'{$}suffix'}
  • /params-ps/wildcard/{'{$}suffix@대'}

Non-nested path params

  • Non-nested

Parsed params with strict false

  • /params-ps/strict-false/$version (1)
  • /params-ps/strict-false/$version (2)
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/named/$foo/$bar.$baz.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/named/$foo/$bar/$baz')({ component: RouteComponent, }) function RouteComponent() { const { foo, bar, baz } = Route.useParams() return (
Hello "/params-ps/named/{foo}/{bar}/{baz}"!
baz: {baz}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/named/$foo/$bar.route.tsx ================================================ import { Link, Outlet, createFileRoute, useParams, } from '@tanstack/react-router' import { RenderCounter } from '../../../../components/RenderCounter' export const Route = createFileRoute('/params-ps/named/$foo/$bar')({ component: RouteComponent, }) function RouteComponent() { const { foo, bar, baz } = useParams({ strict: false, }) return (
Hello "/params-ps/named/{foo}/{bar}"!
Bar Render Count:{' '}
Bar: {bar}
Baz in Bar:{' '} {baz ?? 'no param'}
To Baz
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/named/$foo/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' import { RenderCounter } from '../../../../components/RenderCounter' export const Route = createFileRoute('/params-ps/named/$foo')({ component: RouteComponent, }) function RouteComponent() { const foo = Route.useParams() return (

ParamsNamedFoo

RenderCount:{' '}
{JSON.stringify(foo)}
Index Bar1 Bar2 Bar with special characters
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/named/index.tsx ================================================ import { createFileRoute, redirect } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/named/')({ beforeLoad: () => { throw redirect({ to: '/params-ps' }) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/named/prefix{$foo}.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/named/prefix{$foo}')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsNamedFooPrefix

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/named/{$foo}suffix.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/named/{$foo}suffix')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsNamedFooSuffix

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx ================================================ import { createFileRoute, useParams } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/non-nested/$foo_/$bar')({ component: RouteComponent, }) function RouteComponent() { const fooParams = useParams({ from: `/params-ps/non-nested/$foo_` }) const routeParams = Route.useParams() return (
{JSON.stringify(fooParams)}
{JSON.stringify(routeParams)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/route.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/non-nested/$foo_')() ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/non-nested')({ component: RouteComponent, }) function RouteComponent() { return (

Non-nested path params

  • /params-ps/non-nested/foo/bar /params-ps/non-nested/foo2/bar2
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/strict-false/$version.route.tsx ================================================ import { createFileRoute, useParams } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/strict-false/$version')({ params: { parse: (params) => ({ ...params, version: parseInt(params.version), }), stringify: (params) => ({ ...params, version: `${params.version}`, }), }, component: RouteComponent, }) function RouteComponent() { const { version } = useParams({ strict: false }) return
{String(version)}
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/strict-false/route.tsx ================================================ import { Link, Outlet, createFileRoute, useParams, } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/strict-false')({ component: RouteComponent, }) function RouteComponent() { const { version } = useParams({ strict: false }) return (

ParamsStrictFalseParse

Type:{' '} {typeof version}
Value:{' '} {String(version)}
Version 1 Version 2
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/wildcard/$.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/wildcard/$')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsWildcardSplat

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/wildcard/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { redirect } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/wildcard/')({ beforeLoad: () => { throw redirect({ to: '/params-ps' }) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/wildcard/prefix@대{$}.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/wildcard/prefix@대{$}')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsWildcardSplatPrefix

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/wildcard/prefix{$}.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/wildcard/prefix{$}')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsWildcardSplatPrefix

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/wildcard/{$}suffix.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/wildcard/{$}suffix')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsWildcardSplatSuffix

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params-ps/wildcard/{$}suffix@대.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/params-ps/wildcard/{$}suffix@대')({ component: RouteComponent, }) function RouteComponent() { const p = Route.useParams() return (

ParamsWildcardSplatSuffix

{JSON.stringify(p)}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/params.single.$value.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link } from '@tanstack/react-router' export const Route = createFileRoute('/params/single/$value')({ component: RouteComponent, }) function RouteComponent() { const value = Route.useParams({ select: (s) => s.value }) return (

What's the value???

Check path param value is printed correctly for a single param.

Value: {value}

Self link to same Self link to amended value {`e2e${value}`}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/pathless-layout/_layout/child.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/pathless-layout/_layout/child')({ component: () => (
Pathless Layout Child Route
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/pathless-layout/_layout/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/pathless-layout/_layout/')({ component: () => (
Pathless Layout Index
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/pathless-layout/_layout/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/pathless-layout/_layout')({ component: () => (
Pathless Layout Wrapper
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/pathless-layout/route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/pathless-layout')({ component: () => (

Pathless Layout Section

), notFoundComponent: () => (
Not Found in Pathless Layout
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/posts.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ head: () => ({ meta: [ { title: 'Posts page', }, ], }), loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsx ================================================ import { createFileRoute, getRouteApi, useParams } from '@tanstack/react-router' export const Route = createFileRoute('/posts_/$postId/edit')({ component: PostEditPage, }) const api = getRouteApi(`/posts_/$postId/edit`) function PostEditPage() { const paramsViaApi = api.useParams() const paramsViaHook = useParams({ from: `/posts_/$postId/edit` }) const paramsViaRouteHook = Route.useParams() return ( <>
{paramsViaHook.postId}
{paramsViaRouteHook.postId}
{paramsViaApi.postId}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/destination.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/$target/destination')({ component: () => (
Redirect destination for {Route.fullPath}
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/$target/')({ component: () => { const preload = Route.useSearch({ select: (s) => s.preload }) return (
via-beforeLoad
via-beforeLoad (reloadDocument=true)
via-loader
via-loader (reloadDocument=true)
{/* Route.redirect() tests - uses relative redirect to ./destination */}
via-route-redirect-beforeLoad
via-route-redirect-loader
{/* getRouteApi().redirect() tests - uses relative redirect to ./destination */}
via-routeApi-redirect-beforeLoad
via-routeApi-redirect-loader
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/via-beforeLoad.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { redirect } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/$target/via-beforeLoad')({ beforeLoad: ({ params: { target }, search: { reloadDocument, externalHost }, }) => { switch (target) { case 'internal': throw redirect({ to: '/posts', reloadDocument }) case 'external': throw redirect({ href: externalHost ?? 'http://example.com' }) } }, component: () =>
{Route.fullPath}
, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/via-loader.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { redirect } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/$target/via-loader')({ loaderDeps: ({ search: { reloadDocument, externalHost } }) => ({ reloadDocument, externalHost, }), loader: ({ params: { target }, deps: { externalHost, reloadDocument } }) => { switch (target) { case 'internal': throw redirect({ to: '/posts', reloadDocument }) case 'external': const href = externalHost ?? 'http://example.com' throw redirect({ href }) } }, component: () =>
{Route.fullPath}
, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/via-route-redirect-beforeLoad.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute( '/redirect/$target/via-route-redirect-beforeLoad', )({ beforeLoad: ({ params: { target }, search: { externalHost } }) => { switch (target) { case 'internal': // Use relative redirect to sibling route - this is the key feature being tested // Note: '../destination' is the correct relative path for sibling navigation throw Route.redirect({ to: '../destination' }) case 'external': throw Route.redirect({ href: externalHost ?? 'http://example.com' }) } }, component: () =>
{Route.fullPath}
, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/via-route-redirect-loader.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute( '/redirect/$target/via-route-redirect-loader', )({ loaderDeps: ({ search: { externalHost } }) => ({ externalHost, }), loader: ({ params: { target }, deps: { externalHost } }) => { switch (target) { case 'internal': // Use relative redirect to sibling route - this is the key feature being tested // Note: '../destination' is the correct relative path for sibling navigation throw Route.redirect({ to: '../destination' }) case 'external': throw Route.redirect({ href: externalHost ?? 'http://example.com' }) } }, component: () =>
{Route.fullPath}
, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/via-routeApi-redirect-beforeLoad.tsx ================================================ import { createFileRoute, getRouteApi } from '@tanstack/react-router' const routeApi = getRouteApi( '/redirect/$target/via-routeApi-redirect-beforeLoad', ) export const Route = createFileRoute( '/redirect/$target/via-routeApi-redirect-beforeLoad', )({ beforeLoad: ({ params: { target }, search: { externalHost } }) => { switch (target) { case 'internal': // Use relative redirect to sibling route - this is the key feature being tested // Note: '../destination' is the correct relative path for sibling navigation throw routeApi.redirect({ to: '../destination' }) case 'external': throw routeApi.redirect({ href: externalHost ?? 'http://example.com' }) } }, component: () =>
{Route.fullPath}
, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target/via-routeApi-redirect-loader.tsx ================================================ import { createFileRoute, getRouteApi } from '@tanstack/react-router' const routeApi = getRouteApi('/redirect/$target/via-routeApi-redirect-loader') export const Route = createFileRoute( '/redirect/$target/via-routeApi-redirect-loader', )({ loaderDeps: ({ search: { externalHost } }) => ({ externalHost, }), loader: ({ params: { target }, deps: { externalHost } }) => { switch (target) { case 'internal': // Use relative redirect to sibling route - this is the key feature being tested // Note: '../destination' is the correct relative path for sibling navigation throw routeApi.redirect({ to: '../destination' }) case 'external': throw routeApi.redirect({ href: externalHost ?? 'http://example.com' }) } }, component: () =>
{Route.fullPath}
, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/$target.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { retainSearchParams } from '@tanstack/react-router' import z from 'zod' export const Route = createFileRoute('/redirect/$target')({ params: { parse: (p) => z .object({ target: z.union([z.literal('internal'), z.literal('external')]), }) .parse(p), }, validateSearch: z.object({ reloadDocument: z.boolean().optional(), preload: z.literal(false).optional(), externalHost: z.string().optional(), }), search: { middlewares: [retainSearchParams(['externalHost'])], }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/')({ component: () => (
internal {' '} external
), }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/preload/first.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/preload/first')({ component: RouteComponent, }) function RouteComponent() { return (
go to second
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/preload/second.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { redirect } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/preload/second')({ loader: async () => { await new Promise((r) => setTimeout(r, 1000)) throw redirect({ from: Route.fullPath, to: '../third' }) }, component: RouteComponent, pendingComponent: () =>

second pending

, }) function RouteComponent() { return
Hello "/redirect/preload/second"!
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/redirect/preload/third.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/redirect/preload/third')({ component: RouteComponent, }) function RouteComponent() { return
Hello "/redirect/preload/third"!
} ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/index.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/')({ component: RouteComponent, }) function RouteComponent() { return (
Relative Routing Tests - Home
Using Links Using useNavigate
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/nested/deep/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/link/nested/deep/')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/link/nested/deep/"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/nested/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/link/nested/')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/link/nested/"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/link/path/$path/')({ component: RouteComponent, loader: ({ params }) => { return params }, }) function RouteComponent() { const { path } = Route.useParams() return (
Hello from "/relative/links/path/{path}"!
To {path === 'a' ? 'b' : 'a'}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/path/index.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/link/path/')({ component: RouteComponent, }) function RouteComponent() { return (
Relative Routing Tests - Path Params
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/relative-link-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/link/relative-link-a')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/link/relative-link-a"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/relative-link-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/link/relative-link-b')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/link/relative-link-b"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/route.tsx ================================================ import { Link, Outlet, createFileRoute, useNavigate, } from '@tanstack/react-router' import { useEffect } from 'react' export const Route = createFileRoute('/relative/link')({ component: RouteComponent, }) function RouteComponent() { const navigate = useNavigate() useEffect(() => { console.log('navigate') }, [navigate]) return (
Relative Routing - Links - Index
Return To Home Return To Index Reload Back To Relative Link A To Relative Link B To Deeply Nested To Path Param A To With Search Params
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/link/with-search/index.tsx ================================================ import { createFileRoute, Link } from '@tanstack/react-router' import { z } from 'zod' export const Route = createFileRoute('/relative/link/with-search/')({ component: RouteComponent, validateSearch: z.object({ searchParam: z.string().default('1'), }), }) function RouteComponent() { const { searchParam } = Route.useSearch() return ( <>
Hello "/relative/link/with-search/" searchParam: {searchParam}!

Update Search ) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/nested/deep/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/useNavigate/nested/deep/')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/useNavigate/nested/deep/"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/nested/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/useNavigate/nested/')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/useNavigate/nested/"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/path/$path/index.tsx ================================================ import { createFileRoute, useNavigate } from '@tanstack/react-router' export const Route = createFileRoute('/relative/useNavigate/path/$path/')({ component: RouteComponent, loader: ({ params }) => { return params }, }) function RouteComponent() { const navigate = useNavigate() const { path } = Route.useParams() return (
Hello from "/relative/useNavigate/path/{path}"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/path/index.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/relative/useNavigate/path/')({ component: RouteComponent, }) function RouteComponent() { return (
Relative Routing Tests - Path Params
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/relative-useNavigate-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute( '/relative/useNavigate/relative-useNavigate-a', )({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/useNavigate/relative-useNavigate-a"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/relative-useNavigate-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute( '/relative/useNavigate/relative-useNavigate-b', )({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/relative/useNavigate/relative-useNavigate-b"!
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/route.tsx ================================================ import { Outlet, createFileRoute, useNavigate } from '@tanstack/react-router' import { useEffect } from 'react' export const Route = createFileRoute('/relative/useNavigate')({ component: RouteComponent, }) function RouteComponent() { const navigate = useNavigate() useEffect(() => { console.log('navigate') }, [navigate]) return (
Relative Routing - Links - Index
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/relative/useNavigate/with-search/index.tsx ================================================ import { createFileRoute, useNavigate } from '@tanstack/react-router' import { z } from 'zod' export const Route = createFileRoute('/relative/useNavigate/with-search/')({ component: RouteComponent, validateSearch: z.object({ searchParam: z.string().default('1'), }), }) function RouteComponent() { const navigate = useNavigate() const { searchParam } = Route.useSearch() return ( <>
Hello "/relative/useNavigate/with-search/" searchParam: {searchParam}!

) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/remountDeps.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/remountDeps')({ validateSearch(search: { searchParam: string }) { return { searchParam: search.searchParam } }, loaderDeps(opts) { return opts.search }, component: Home, remountDeps(opts) { return opts.search }, }) let counter = 0 function Home() { const search = Route.useSearch() const navigate = Route.useNavigate() const [mounts, setMounts] = React.useState(counter) React.useEffect(() => { setMounts(++counter) }, []) return (
Search: {search.searchParam}
Page component mounts: {mounts}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/search-params/default.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' export const Route = createFileRoute('/search-params/default')({ validateSearch: z.object({ default: z.string().default('d1'), }), beforeLoad: ({ context }) => { if (context.hello !== 'world') { throw new Error('Context hello is not "world"') } }, loader: ({ context }) => { if (context.hello !== 'world') { throw new Error('Context hello is not "world"') } }, component: () => { const search = Route.useSearch() const context = Route.useRouteContext() return ( <>
{search.default}
{context.hello}
) }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/search-params/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/search-params/')({ component: RouteComponent, }) function RouteComponent() { return (
go to /search-params/default
go to /search-params/default?default=d2
go to /search-params/default?default=🚀대한민국
go to /search-params/default?default=%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/search-params/route.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/search-params')({ beforeLoad: async () => { await new Promise((resolve) => setTimeout(resolve, 100)) return { hello: 'world' as string } }, }) ================================================ FILE: e2e/react-router/basic-file-based/src/routes/structural-sharing.$enabled.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, getRouteApi, useSearch } from '@tanstack/react-router' import React from 'react' import { z } from 'zod' const enabledSchema = { enabled: z.preprocess((val) => { if (typeof val === 'string') { if (val.toLowerCase() === 'true') return true if (val.toLowerCase() === 'false') return false } return val }, z.boolean()), } export const Route = createFileRoute('/structural-sharing/$enabled')({ component: RouteComponent, params: { parse: (p) => z.object(enabledSchema).parse(p), }, validateSearch: z.object({ foo: z.string(), bar: z.string() }), }) function useRenderCount() { const count = React.useRef(0) count.current++ return count.current } const api = getRouteApi('/structural-sharing/$enabled') function RouteComponent() { console.log('rendering') const renderCount = useRenderCount() const { enabled } = Route.useParams() const searchViaRouteHook = Route.useSearch({ select: ({ foo, bar }) => { return { values: [foo, bar] } }, structuralSharing: enabled, }) const searchViaHook = useSearch({ from: Route.id, select: ({ foo, bar }) => { return { values: [foo, bar] } }, structuralSharing: enabled, }) const searchViaRouteApiHook = api.useSearch({ select: ({ foo, bar }) => { return { values: [foo, bar] } }, structuralSharing: enabled, }) return ( <>
{JSON.stringify(enabled)}
{renderCount}
{JSON.stringify(searchViaHook)}
{JSON.stringify(searchViaRouteHook)}
{JSON.stringify(searchViaRouteApiHook)}

go to f2/b2 ) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/대한민국/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/대한민국')({ component: RouteComponent, }) function RouteComponent() { return (

Hello "/대한민국"!

  • link to latin id
  • link to unicode id
  • link to foo/bar
  • link to foo%\/🚀대

) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/대한민국/wildcard.$.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/대한민국/wildcard/$')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (

Unicode Wildcard Params

Hello /대한민국/wildcard/ {params._splat}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/routes/대한민국/🚀.$id.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/대한민국/🚀/$id')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (

Unicode Named Params

Hello /대한민국/🚀/ {params.id}
) } ================================================ FILE: e2e/react-router/basic-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test('lazy file route errorComponent is rendered when the loader throws', async ({ page, }) => { await page.goto('/lazy-error') await expect(page.getByTestId('lazy-route-error-component')).toContainText( 'Lazy route error component', ) await expect(page.locator('body')).not.toContainText('Something went wrong!') }) test("useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test('useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test("legacy useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('legacy useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('legacy Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('useCanGoBack correctly disables back button', async ({ page }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } expect(await getBackButtonDisabled()).toBe(true) await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goForward() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) test('useCanGoBack correctly disables back button, using router.history and window.history', async ({ page, }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(true) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) const testCases = [ { description: 'Navigating to a route inside a route group', testId: 'link-to-route-inside-group', }, { description: 'Navigating to a route inside a subfolder inside a route group ', testId: 'link-to-route-inside-group-inside-subfolder', }, { description: 'Navigating to a route inside a route group inside a layout', testId: 'link-to-route-inside-group-inside-layout', }, { description: 'Navigating to a lazy route inside a route group', testId: 'link-to-lazy-route-inside-group', }, { description: 'Navigating to the only route inside a route group ', testId: 'link-to-only-route-inside-group', }, ] testCases.forEach(({ description, testId }) => { test(description, async ({ page }) => { await page.getByTestId(testId).click() await expect(page.getByTestId('search-via-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-hook')).toContainText( 'world', ) await expect(page.getByTestId('search-via-route-api')).toContainText( 'world', ) }) }) test('navigating to an unnested route', async ({ page }) => { const postId = 'hello-world' await page.goto(`/posts/${postId}/edit`) await page.waitForURL(`/posts/${postId}/edit`) await expect(page.getByTestId('params-via-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-api')).toContainText(postId) }) async function getRenderCount(page: Page) { const renderCount = parseInt( await page.getByTestId('render-count').innerText(), ) return renderCount } async function structuralSharingTest(page: Page, enabled: boolean) { page.goto(`/structural-sharing/${enabled}/?foo=f1&bar=b1`) await expect(page.getByTestId('enabled')).toHaveText(JSON.stringify(enabled)) async function checkSearch({ foo, bar }: { foo: string; bar: string }) { expect(page.url().endsWith(`?foo=${foo}&bar=${bar}`)).toBe(true) const expectedSearch = JSON.stringify({ values: [foo, bar] }) await expect(page.getByTestId('search-via-hook')).toHaveText(expectedSearch) await expect(page.getByTestId('search-via-route-hook')).toHaveText( expectedSearch, ) await expect(page.getByTestId('search-via-route-api-hook')).toHaveText( expectedSearch, ) } await checkSearch({ bar: 'b1', foo: 'f1' }) await page.getByTestId('link').click() await checkSearch({ bar: 'b2', foo: 'f2' }) } test('structural sharing disabled', async ({ page }) => { await structuralSharingTest(page, false) expect(await getRenderCount(page)).toBe(2) await page.getByTestId('link').click() expect(await getRenderCount(page)).toBeGreaterThan(2) }) test('structural sharing enabled', async ({ page }) => { await structuralSharingTest(page, true) expect(await getRenderCount(page)).toBe(2) await page.getByTestId('link').click() expect(await getRenderCount(page)).toBe(2) }) test('Should change title on client side navigation', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await expect(page).toHaveTitle('Posts page') }) test('Should change post navigating back and forth', async ({ page }) => { await page.goto('/posts/1') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await page.getByRole('link', { name: 'qui est esse' }).click() await expect(page.getByTestId('post-title')).toContainText('qui est esse') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toContainText('sunt aut facere') }) test('Should not remount deps when remountDeps does not change ', async ({ page, }) => { await page.goto('/notRemountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) }) test('Should remount deps when remountDeps does change ', async ({ page }) => { await page.goto('/remountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 2', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 3', ) }) test.describe('Unicode route rendering', () => { test('should render non-latin route correctly', async ({ page, baseURL }) => { await page.goto('/대한민국') await expect(page.locator('body')).toContainText('Hello "/대한민국"!') expect(page.url()).toBe(`${baseURL}/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD`) }) }) test.describe('Pathless layout routes', () => { test('direct navigation to pathless layout route renders correctly', async ({ page, }) => { await page.goto('/pathless-layout') await expect(page.getByTestId('pathless-layout-header')).toContainText( 'Pathless Layout Section', ) await expect(page.getByTestId('pathless-layout-wrapper')).toContainText( 'Pathless Layout Wrapper', ) await expect(page.getByTestId('pathless-layout-index')).toContainText( 'Pathless Layout Index', ) }) test('client-side navigation to pathless layout route', async ({ page }) => { await page.goto('/') await page.getByTestId('link-to-pathless-layout').click() await expect(page.getByTestId('pathless-layout-header')).toContainText( 'Pathless Layout Section', ) await expect(page.getByTestId('pathless-layout-wrapper')).toContainText( 'Pathless Layout Wrapper', ) }) test('navigation within pathless layout preserves layout', async ({ page, }) => { await page.goto('/pathless-layout') await page.getByTestId('link-to-child').click() await expect(page.getByTestId('pathless-layout-header')).toContainText( 'Pathless Layout Section', ) await expect(page.getByTestId('pathless-layout-wrapper')).toContainText( 'Pathless Layout Wrapper', ) await expect(page.getByTestId('pathless-layout-child')).toContainText( 'Pathless Layout Child Route', ) }) test('direct navigation to child of pathless layout', async ({ page }) => { await page.goto('/pathless-layout/child') await expect(page.getByTestId('pathless-layout-header')).toContainText( 'Pathless Layout Section', ) await expect(page.getByTestId('pathless-layout-wrapper')).toContainText( 'Pathless Layout Wrapper', ) await expect(page.getByTestId('pathless-layout-child')).toContainText( 'Pathless Layout Child Route', ) }) test('navigating to non-existent route under pathless layout shows not found', async ({ page, }) => { await page.goto('/pathless-layout/does-not-exist') await expect(page.getByTestId('pathless-layout-not-found')).toContainText( 'Not Found in Pathless Layout', ) await expect(page.locator('body')).toContainText('Not Found') }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/fullpath-types.spec.ts ================================================ import { expect, test } from '@playwright/test' test.describe('FullPath type/runtime match (Issues #4892, #2675, #6403)', () => { test.describe('Pathless layout routes should have parent fullPath, not empty string', () => { test('direct navigation to pathless layout shows correct fullPath', async ({ page, }) => { await page.goto('/fullpath-test') // The pathless layout's fullPath should be '/fullpath-test' (same as parent), not '' await expect(page.getByTestId('pathless-layout-fullpath')).toHaveText( '/fullpath-test', ) }) test('client-side navigation to pathless layout shows correct fullPath', async ({ page, }) => { await page.goto('/') await page.getByTestId('link-to-fullpath-test').click() await expect(page.getByTestId('pathless-layout-fullpath')).toHaveText( '/fullpath-test', ) }) }) test.describe('Index routes should have trailing slash in fullPath', () => { test('direct navigation to index route shows fullPath with trailing slash', async ({ page, }) => { await page.goto('/fullpath-test') // The index route's fullPath should be '/fullpath-test/' (with trailing slash) await expect(page.getByTestId('index-route-fullpath')).toHaveText( '/fullpath-test/', ) }) test('param route under pathless layout shows correct fullPath', async ({ page, }) => { await page.goto('/fullpath-test/123') // The param route's fullPath should be '/fullpath-test/$id' await expect(page.getByTestId('param-route-fullpath')).toHaveText( '/fullpath-test/$id', ) await expect(page.getByTestId('fullpath-test-param')).toHaveText( 'Param: 123', ) }) }) test.describe('Route.to should NOT have trailing slash (Issue #3005)', () => { test('index route Route.to should not have trailing slash', async ({ page, }) => { await page.goto('/fullpath-test') // Route.to should be '/fullpath-test' (without trailing slash) // while Route.fullPath is '/fullpath-test/' (with trailing slash) await expect(page.getByTestId('index-route-to')).toHaveText( '/fullpath-test', ) }) }) test.describe('Existing pathless layout routes', () => { test('existing pathless layout index shows correct fullPath at runtime', async ({ page, }) => { // This tests the existing /pathless-layout route which has a pathless _layout await page.goto('/pathless-layout') await expect(page.getByTestId('pathless-layout-header')).toContainText( 'Pathless Layout Section', ) await expect(page.getByTestId('pathless-layout-wrapper')).toContainText( 'Pathless Layout Wrapper', ) await expect(page.getByTestId('pathless-layout-index')).toContainText( 'Pathless Layout Index', ) }) }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/hover-preload-hash.spec.ts ================================================ import { expect, test } from '@playwright/test' import { toRuntimePath } from '@tanstack/router-e2e-utils' test('clicking hash link then hovering another link does not scroll back to hash', async ({ page, }) => { // Go to the page without hash await page.goto(toRuntimePath('/hover-preload-hash')) // Click the hash link to navigate to position1 await page.getByTestId('link-to-hash').click() await expect(page.locator('#position1')).toBeInViewport() // Scroll up so position1 is no longer in view await page.evaluate(() => window.scrollTo(0, 0)) await expect(page.locator('#position1')).not.toBeInViewport() // Hover the link to trigger intent preload (should NOT scroll back) await page.getByTestId('link-to-only-route-inside-group').hover() await page.waitForTimeout(400) // Ensure we did not jump back to the hash target await expect(page.locator('#position1')).not.toBeInViewport() }) ================================================ FILE: e2e/react-router/basic-file-based/tests/mask.spec.ts ================================================ import { expect, test } from '@playwright/test' test('route masks transform params and expose masked pathname in the browser (react)', async ({ page, }) => { await page.goto('/') await page.getByTestId('link-to-masks').click() await expect(page.getByText('Route Masks')).toBeVisible() const link = page.getByTestId('link-to-admin-mask') await link.click() await page.waitForURL('/masks/public/user-42') await expect(page.getByTestId('admin-user-component')).toBeInViewport() await expect(page.getByTestId('admin-user-id')).toHaveText('42') await expect(page.getByTestId('router-pathname')).toHaveText( '/masks/admin/42', ) await expect(page.getByTestId('router-masked-pathname')).toHaveText( '/masks/public/user-42', ) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts ================================================ import { expect, test } from '@playwright/test' const testCases: Array<{ name: string testPathDesc: string testPathPrefix: string testPathSuffix: string paramValue: Record paramValue2: Record }> = [ { name: 'Named path params', testPathDesc: 'named', testPathPrefix: '', testPathSuffix: '', paramValue: { baz: 'baz' }, paramValue2: { baz: 'baz_' }, }, { name: 'prefix params', testPathPrefix: 'prefix', testPathSuffix: '', testPathDesc: 'prefix', paramValue: { baz: 'baz' }, paramValue2: { baz: 'baz_' }, }, { name: 'suffix params', testPathPrefix: '', testPathSuffix: 'suffix', testPathDesc: 'suffix', paramValue: { baz: 'baz' }, paramValue2: { baz: 'baz2' }, }, { name: 'path', testPathPrefix: '', testPathSuffix: '', testPathDesc: 'path', paramValue: {}, paramValue2: {}, }, ] test.describe('Non-nested paths', () => { testCases.forEach( ({ name, testPathDesc, testPathPrefix, testPathSuffix, paramValue, paramValue2, }) => { test.describe(name, () => { const path = `/non-nested/${testPathDesc}` const paramNameDesc = Object.keys(paramValue)[0] ?? 'baz' test.beforeEach(async ({ page }) => { await page.goto('/non-nested') await page.waitForURL('/non-nested') }) test('Should not un-nest nested paths', async ({ page }) => { const nonNestedPathHeading = page.getByTestId( 'non-nested-path-heading', ) await expect(nonNestedPathHeading).toBeVisible() await page.getByTestId(`l-to-${testPathDesc}`).click() await page.waitForURL(path) const rootRouteHeading = page.getByTestId( `non-nested-${testPathDesc}-root-route-heading`, ) await expect(rootRouteHeading).toBeVisible() const pathRouteHeading = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-route-heading`, ) const indexHeading = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-index-heading`, ) const fooHeading = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-foo-heading`, ) const indexParams = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-index-param`, ) const fooParams = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-foo-param`, ) const indexLink = page.getByTestId(`to-${testPathDesc}-index`) const fooLink = page.getByTestId(`to-${testPathDesc}-foo`) const foo2Link = page.getByTestId(`to-${testPathDesc}-foo-2`) const indexPath = `${path}/${testPathPrefix}${paramValue[paramNameDesc] ?? 'baz'}${testPathSuffix}` const fooPath = `${path}/${testPathPrefix}${paramValue[paramNameDesc] ?? 'baz'}${testPathSuffix}/foo` const foo2Path = `${path}/${testPathPrefix}${paramValue2[paramNameDesc] ?? 'baz'}${testPathSuffix}/foo` console.log(await indexLink.getAttribute('href')) await expect(indexLink).toHaveAttribute('href', indexPath) await expect(fooLink).toHaveAttribute('href', fooPath) await expect(foo2Link).toHaveAttribute('href', foo2Path) await indexLink.click() await page.waitForURL(indexPath) await expect(rootRouteHeading).toBeVisible() await expect(pathRouteHeading).toBeVisible() await expect(indexHeading).toBeVisible() const indexParamValue = await indexParams.innerText() expect(JSON.parse(indexParamValue)).toEqual(paramValue) await fooLink.click() await page.waitForURL(fooPath) await expect(rootRouteHeading).toBeVisible() await expect(pathRouteHeading).toBeVisible() await expect(fooHeading).toBeVisible() const fooParamValue = await fooParams.innerText() expect(JSON.parse(fooParamValue)).toEqual(paramValue) await foo2Link.click() await page.waitForURL(foo2Path) await expect(rootRouteHeading).toBeVisible() await expect(pathRouteHeading).toBeVisible() await expect(fooHeading).toBeVisible() const foo2ParamValue = await fooParams.innerText() expect(JSON.parse(foo2ParamValue)).toEqual(paramValue2) }) test('Should not nest non-nested paths', async ({ page }) => { const nonNestedPathHeading = page.getByTestId( 'non-nested-path-heading', ) await expect(nonNestedPathHeading).toBeVisible() await page.getByTestId(`l-to-${testPathDesc}`).click() await page.waitForURL(path) const rootRouteHeading = page.getByTestId( `non-nested-${testPathDesc}-root-route-heading`, ) await expect(rootRouteHeading).toBeVisible() const pathRouteHeading = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-route-heading`, ) const barHeading = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-bar-heading`, ) const barParams = page.getByTestId( `non-nested-${testPathDesc}-${paramNameDesc}-bar-param`, ) const barLink = page.getByTestId(`to-${testPathDesc}-bar`) const bar2Link = page.getByTestId(`to-${testPathDesc}-bar-2`) const barPath = `${path}/${testPathPrefix}${paramValue[paramNameDesc] ?? 'baz'}${testPathSuffix}/bar` const bar2Path = `${path}/${testPathPrefix}${paramValue2[paramNameDesc] ?? 'baz'}${testPathSuffix}/bar` await expect(barLink).toHaveAttribute('href', barPath) await expect(bar2Link).toHaveAttribute('href', bar2Path) await barLink.click() await page.waitForURL(barPath) await expect(rootRouteHeading).toBeVisible() await expect(pathRouteHeading).not.toBeVisible() await expect(barHeading).toBeVisible() const barParamValue = await barParams.innerText() expect(JSON.parse(barParamValue)).toEqual(paramValue) await bar2Link.click() await page.waitForURL(bar2Path) await expect(rootRouteHeading).toBeVisible() await expect(pathRouteHeading).not.toBeVisible() await expect(barHeading).toBeVisible() const bar2ParamValue = await barParams.innerText() expect(JSON.parse(bar2ParamValue)).toEqual(paramValue2) }) }) }, ) }) test.describe('Deeply nested non-nested paths', () => { test.beforeEach(async ({ page }) => { await page.goto('/non-nested/deep') await page.waitForURL('/non-nested/deep') }) test('It should nest nested paths 1 level deep', async ({ page }) => { const rootRouteHeading = page.getByTestId( `non-nested-deep-root-route-heading`, ) await expect(rootRouteHeading).toBeVisible() const bazLink = page.getByTestId('to-deep-baz') await bazLink.click() await page.waitForURL('/non-nested/deep/baz') const bazRouteHeading = page.getByTestId( 'non-nested-deep-baz-route-heading', ) const bazIndexHeading = page.getByTestId( 'non-nested-deep-baz-index-heading', ) const bazIndexParams = page.getByTestId('non-nested-deep-baz-index-param') await expect(bazRouteHeading).toBeVisible() await expect(bazIndexHeading).toBeVisible() expect(await bazIndexParams.innerText()).toBe( JSON.stringify({ baz: 'baz' }), ) }) test('It should not nest non-nested paths 1 level deep', async ({ page }) => { const rootRouteHeading = page.getByTestId( `non-nested-deep-root-route-heading`, ) await expect(rootRouteHeading).toBeVisible() const bazBarLink = page.getByTestId('to-deep-baz-bar') await bazBarLink.click() await page.waitForURL('/non-nested/deep/baz-bar/bar') const bazRouteHeading = page.getByTestId( 'non-nested-deep-baz-route-heading', ) const bazBarRouteHeading = page.getByTestId( 'non-nested-deep-baz-bar-route-heading', ) const bazBarIndexHeading = page.getByTestId( 'non-nested-deep-baz-bar-index-heading', ) const bazBarIndexParams = page.getByTestId( 'non-nested-deep-baz-bar-index-param', ) await expect(bazRouteHeading).not.toBeVisible() await expect(bazBarRouteHeading).toBeVisible() await expect(bazBarIndexHeading).toBeVisible() expect(await bazBarIndexParams.innerText()).toBe( JSON.stringify({ baz: 'baz-bar' }), ) }) test('It should not nest non-nested paths 2 levels deep', async ({ page, }) => { const rootRouteHeading = page.getByTestId( `non-nested-deep-root-route-heading`, ) await expect(rootRouteHeading).toBeVisible() const bazBarQuxLink = page.getByTestId('to-deep-baz-bar-qux') await bazBarQuxLink.click() await page.waitForURL('/non-nested/deep/baz-bar-qux/bar/qux') const bazRouteHeading = page.getByTestId( 'non-nested-deep-baz-route-heading', ) const bazBarRouteHeading = page.getByTestId( 'non-nested-deep-baz-bar-route-heading', ) const bazBarQuxHeading = page.getByTestId( 'non-nested-deep-baz-bar-qux-heading', ) const bazBarQuxParams = page.getByTestId( 'non-nested-deep-baz-bar-qux-param', ) await expect(bazRouteHeading).not.toBeVisible() await expect(bazBarRouteHeading).not.toBeVisible() await expect(bazBarQuxHeading).toBeVisible() await expect(bazBarQuxParams).toBeVisible() expect(await bazBarQuxParams.innerText()).toBe( JSON.stringify({ baz: 'baz-bar-qux' }), ) }) test('It should nest and un-nest non-nested across paths multiple levels deep', async ({ page, }) => { const rootRouteHeading = page.getByTestId( `non-nested-deep-root-route-heading`, ) await expect(rootRouteHeading).toBeVisible() const bazBarFooLink = page.getByTestId('to-deep-baz-bar-foo') await bazBarFooLink.click() await page.waitForURL('/non-nested/deep/baz-bar/bar/foo') const bazRouteHeading = page.getByTestId( 'non-nested-deep-baz-route-heading', ) const bazBarRouteHeading = page.getByTestId( 'non-nested-deep-baz-bar-route-heading', ) const bazBarFooRouteHeading = page.getByTestId( 'non-nested-deep-baz-bar-foo-route-heading', ) const bazBarFooIndexHeading = page.getByTestId( 'non-nested-deep-baz-bar-foo-index-heading', ) const bazBarFooIndexParams = page.getByTestId( 'non-nested-deep-baz-bar-foo-index-param', ) await expect(bazRouteHeading).not.toBeVisible() await expect(bazBarRouteHeading).toBeVisible() await expect(bazBarFooRouteHeading).toBeVisible() await expect(bazBarFooIndexHeading).toBeVisible() await expect(bazBarFooIndexParams).toBeVisible() expect(await bazBarFooIndexParams.innerText()).toBe( JSON.stringify({ baz: 'baz-bar', foo: 'foo' }), ) const bazBarFooQuxLink = page.getByTestId('to-deep-baz-bar-foo-qux') await bazBarFooQuxLink.click() await page.waitForURL('/non-nested/deep/baz-bar-qux/bar/foo/qux') const bazBarFooQuxHeading = page.getByTestId( 'non-nested-deep-baz-bar-foo-qux-heading', ) const bazBarFooQuxParams = page.getByTestId( 'non-nested-deep-baz-bar-foo-qux-param', ) await expect(bazRouteHeading).not.toBeVisible() await expect(bazBarRouteHeading).toBeVisible() await expect(bazBarFooRouteHeading).not.toBeVisible() await expect(bazBarFooQuxHeading).toBeVisible() await expect(bazBarFooQuxParams).toBeVisible() expect(await bazBarFooQuxParams.innerText()).toBe( JSON.stringify({ baz: 'baz-bar-qux', foo: 'foo' }), ) }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/open-redirect-prevention.spec.ts ================================================ import { expect, test } from '@playwright/test' test.describe('Open redirect prevention', () => { test.describe('CRLF injection attacks', () => { test('should not redirect to external site via CR injection (%0d)', async ({ page, baseURL, }) => { // This URL attempts to exploit CRLF injection to redirect to google.com // %0d = \r (carriage return) // Without the fix, /\r/google.com/ would be interpreted as //google.com/ // which browsers resolve as a protocol-relative URL to http://google.com/ await page.goto('/%0d/google.com/') await page.waitForLoadState('networkidle') // Should stay on the same origin - the path should be treated as a local path // not as an external redirect expect(page.url().startsWith(baseURL!)).toBe(true) // The origin should be localhost, not google.com const url = new URL(page.url()) expect(url.origin).toBe(new URL(baseURL!).origin) }) test('should not redirect to external site via LF injection (%0a)', async ({ page, baseURL, }) => { // %0a = \n (line feed) await page.goto('/%0a/evil.com/') await page.waitForLoadState('networkidle') // Should stay on the same origin expect(page.url().startsWith(baseURL!)).toBe(true) const url = new URL(page.url()) expect(url.origin).toBe(new URL(baseURL!).origin) }) test('should not redirect to external site via CRLF injection (%0d%0a)', async ({ page, baseURL, }) => { // Combined CRLF injection attempt await page.goto('/%0d%0a/attacker.com/') await page.waitForLoadState('networkidle') // Should stay on the same origin expect(page.url().startsWith(baseURL!)).toBe(true) const url = new URL(page.url()) expect(url.origin).toBe(new URL(baseURL!).origin) }) test('should not redirect to external site via multiple control chars', async ({ page, baseURL, }) => { // Multiple CR/LF characters await page.goto('/%0d%0d%0a/malicious.com/path') await page.waitForLoadState('networkidle') // Should stay on the same origin expect(page.url().startsWith(baseURL!)).toBe(true) const url = new URL(page.url()) expect(url.origin).toBe(new URL(baseURL!).origin) }) }) test.describe('Protocol-relative URL prevention', () => { test('should handle paths that could be misinterpreted as protocol-relative URLs', async ({ page, baseURL, }) => { // When control characters are stripped from paths like /%0d/evil.com/ // the result could be //evil.com/ which is a protocol-relative URL // Our fix collapses these to /evil.com/ to prevent external redirects // This is already tested above, but we verify the collapsed path works await page.goto('/%0d/test-path/') await page.waitForLoadState('networkidle') // Should stay on the same origin expect(page.url().startsWith(baseURL!)).toBe(true) const url = new URL(page.url()) expect(url.origin).toBe(new URL(baseURL!).origin) // Path should be collapsed to /test-path (not //test-path/) expect(url.pathname).toMatch(/^\/test-path\/?$/) }) }) test.describe('Normal navigation still works', () => { test('should navigate to normal paths correctly', async ({ page, baseURL, }) => { await page.goto('/posts') await page.waitForLoadState('networkidle') expect(page.url()).toBe(`${baseURL}/posts`) }) test('should handle URL-encoded characters in normal paths', async ({ page, baseURL, }) => { // Normal URL encoding should still work await page.goto('/params/single/hello%20world') await page.waitForLoadState('networkidle') expect(page.url().startsWith(baseURL!)).toBe(true) }) }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/params.spec.ts ================================================ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test.describe('ensure single params have been parsed correctly whilst being stable in the browser', () => { const cases = [ { value: 'hello', expected: 'hello' }, { value: '100%25', expected: '100%', }, { value: '100%2525', expected: '100%25', }, { value: '100%26', expected: '100&', }, ] function getParsedValue(page: Page) { return page.getByTestId('parsed-param-value').textContent() } for (const { value, expected } of cases) { test(`navigating to /params/single/${value}`, async ({ page, baseURL }) => { await page.goto(`/params/single/${value}`) // on the first run, the value should be the same as the expected value const valueOnFirstRun = await getParsedValue(page) expect(valueOnFirstRun).toBe(expected) // the url/pathname should be the same as the expected value const urlOnFirstRun = page.url().replace(baseURL!, '') expect(urlOnFirstRun).toBe(`/params/single/${value}`) // click on the self link to the same value await page.getByTestId('self-link-same').click() const valueOnSecondRun = await getParsedValue(page) expect(valueOnSecondRun).toBe(expected) // click on the self link to the amended value await page.getByTestId('self-link-amended').click() const valueOnThirdRun = await getParsedValue(page) expect(valueOnThirdRun).toBe(`e2e${expected}`) // the url/pathname should be the same as the expected value const urlOnThirdRun = page.url().replace(baseURL!, '') expect(urlOnThirdRun).toBe(`/params/single/e2e${value}`) }) } }) test.describe('params operations + non-nested routes', () => { test.beforeEach(async ({ page }) => { await page.goto('/params-ps/non-nested') }) test('useParams must resolve non-nested path params', async ({ page }) => { await page.waitForURL('/params-ps/non-nested') const fooBarLink = page.getByTestId('l-to-non-nested-foo-bar') const foo2Bar2Link = page.getByTestId('l-to-non-nested-foo2-bar2') await expect(fooBarLink).toHaveAttribute( 'href', '/params-ps/non-nested/foo/bar', ) await fooBarLink.click() await page.waitForURL('/params-ps/non-nested/foo/bar') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe('/params-ps/non-nested/foo/bar') const fooParamsValue = page.getByTestId('foo-params-value') const fooParamsText = await fooParamsValue.innerText() const fooParamsObj = JSON.parse(fooParamsText) expect(fooParamsObj).toEqual({ foo: 'foo' }) const paramsValue = page.getByTestId('foo-bar-params-value') const paramsText = await paramsValue.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual({ foo: 'foo', bar: 'bar' }) await expect(foo2Bar2Link).toHaveAttribute( 'href', '/params-ps/non-nested/foo2/bar2', ) await foo2Bar2Link.click() await page.waitForURL('/params-ps/non-nested/foo2/bar2') const pagePathname2 = new URL(page.url()).pathname expect(pagePathname2).toBe('/params-ps/non-nested/foo2/bar2') const foo2ParamsValue = page.getByTestId('foo-params-value') const foo2ParamsText = await foo2ParamsValue.innerText() const foo2ParamsObj = JSON.parse(foo2ParamsText) expect(foo2ParamsObj).toEqual({ foo: 'foo2' }) const params2Value = page.getByTestId('foo-bar-params-value') const params2Text = await params2Value.innerText() const params2Obj = JSON.parse(params2Text) expect(params2Obj).toEqual({ foo: 'foo2', bar: 'bar2' }) }) }) test.describe('params operations + prefix/suffix', () => { test.beforeEach(async ({ page }) => { await page.goto('/params-ps') }) test.describe('named params', () => { const NAMED_PARAMS_PAIRS = [ // Test ID | Expected href { id: 'l-to-named-foo', pathname: '/params-ps/named/foo', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFoo', }, { id: 'l-to-named-prefixfoo', pathname: '/params-ps/named/prefixfoo', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFooPrefix', }, { id: 'l-to-named-foosuffix', pathname: '/params-ps/named/foosuffix', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFooSuffix', }, { id: 'l-to-named-foo-special-characters', pathname: '/params-ps/named/foo%25%5C%2F%F0%9F%9A%80%EB%8C%80', params: { foo: 'foo%\\/🚀대' }, destHeadingId: 'ParamsNamedFoo', }, ] satisfies Array<{ id: string pathname: string params: Record destHeadingId: string }> test.describe('Link', () => { NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => { test(`interpolation for testid="${id}" has href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await expect(link).toHaveAttribute('href', pathname) }) }) NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => { test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await link.click() await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) }) }) }) NAMED_PARAMS_PAIRS.forEach(({ pathname, params, destHeadingId }) => { test(`on first-load to "${pathname}" has correct params`, async ({ page, }) => { await page.goto(pathname) await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) const headingEl = page.getByRole('heading', { name: destHeadingId }) await expect(headingEl).toBeVisible() const paramsEl = page.getByTestId('params-output') const paramsText = await paramsEl.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual(params) }) }) test(`ensure use params doesn't cause excess renders and is stable across various usage options`, async ({ page, }) => { await page.goto('/params-ps/named/foo') await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe('/params-ps/named/foo') const fooRenderCount = page.getByTestId('foo-render-count') const fooIndexLink = page.getByTestId('params-foo-links-index') const fooBar1Link = page.getByTestId('params-foo-links-bar1') const fooBar2Link = page.getByTestId('params-foo-links-bar2') const fooBarBazLink = page.getByTestId('params-foo-bar-links-baz') const fooValue = page.getByTestId('params-output') const fooBarValue = page.getByTestId('foo-bar-value') const fooBazInBarValue = page.getByTestId('foo-baz-in-bar-value') const fooBarRenderCount = page.getByTestId('foo-bar-render-count') const fooBarBazValue = page.getByTestId('foo-bar-baz-value') await expect(fooRenderCount).toBeInViewport() await expect(fooValue).toBeInViewport() await expect(fooIndexLink).toBeInViewport() await expect(fooBar1Link).toBeInViewport() await expect(fooBar2Link).toBeInViewport() await expect(fooRenderCount).toHaveText('1') await expect(fooValue).toHaveText(JSON.stringify({ foo: 'foo' })) await fooBar1Link.click() await page.waitForLoadState('networkidle') await expect(fooValue).toBeInViewport() await expect(fooRenderCount).toBeInViewport() await expect(fooBarRenderCount).toBeInViewport() await expect(fooBarValue).toBeInViewport() await expect(fooBazInBarValue).toBeInViewport() await expect(fooBarBazLink).toBeInViewport() await expect(fooValue).toHaveText(JSON.stringify({ foo: 'foo' })) await expect(fooRenderCount).toHaveText('1') await expect(fooBarRenderCount).toHaveText('1') await expect(fooBarValue).toHaveText('1') await expect(fooBazInBarValue).toHaveText('no param') await fooBarBazLink.click() await page.waitForLoadState('networkidle') await expect(fooValue).toBeInViewport() await expect(fooRenderCount).toBeInViewport() await expect(fooBarRenderCount).toBeInViewport() await expect(fooBarValue).toBeInViewport() await expect(fooBazInBarValue).toBeInViewport() await expect(fooValue).toHaveText(JSON.stringify({ foo: 'foo' })) await expect(fooRenderCount).toHaveText('1') await expect(fooBarRenderCount).toHaveText('2') await expect(fooBarValue).toHaveText('1') await expect(fooBazInBarValue).toHaveText('1_10') await expect(fooBarBazValue).toHaveText('1_10') await fooBar2Link.click() await page.waitForLoadState('networkidle') await expect(fooValue).toBeInViewport() await expect(fooRenderCount).toBeInViewport() await expect(fooBarValue).toBeInViewport() await expect(fooValue).toHaveText(JSON.stringify({ foo: 'foo' })) await expect(fooRenderCount).toHaveText('1') await expect(fooBarValue).toHaveText('2') await fooIndexLink.click() await page.waitForLoadState('networkidle') await expect(fooValue).toBeInViewport() await expect(fooRenderCount).toBeInViewport() await expect(fooBarValue).not.toBeInViewport() await expect(fooValue).toHaveText(JSON.stringify({ foo: 'foo' })) await expect(fooRenderCount).toHaveText('1') }) }) test.describe('wildcard param', () => { const WILDCARD_PARAM_PAIRS = [ // Test ID | Expected href { id: 'l-to-wildcard-foo', pathname: '/params-ps/wildcard/foo', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplat', }, { id: 'l-to-wildcard-prefixfoo', pathname: '/params-ps/wildcard/prefixfoo', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplatPrefix', }, { id: 'l-to-wildcard-foosuffix', pathname: '/params-ps/wildcard/foosuffix', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplatSuffix', }, { id: 'l-to-wildcard-escaped', pathname: `/params-ps/wildcard/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80]`, params: { _splat: 'test[s\\/.\\/parameter%!🚀]', '*': 'test[s\\/.\\/parameter%!🚀]', }, destHeadingId: 'ParamsWildcardSplat', }, { id: 'l-to-wildcard-prefix-escaped', pathname: `/params-ps/wildcard/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80]`, params: { _splat: 'test[s\\/.\\/parameter%!🚀]', '*': 'test[s\\/.\\/parameter%!🚀]', }, destHeadingId: 'ParamsWildcardSplatPrefix', }, { id: 'l-to-wildcard-suffix-escaped', pathname: `/params-ps/wildcard/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80]suffix@%EB%8C%80`, params: { _splat: 'test[s\\/.\\/parameter%!🚀]', '*': 'test[s\\/.\\/parameter%!🚀]', }, destHeadingId: 'ParamsWildcardSplatSuffix', }, { id: 'l-to-wildcard-encoded', pathname: '/params-ps/wildcard/%25EB%258C%2580%25ED%2595%259C%25EB%25AF%25BC%25EA%25B5%25AD', params: { '*': '%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', _splat: '%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', }, destHeadingId: 'ParamsWildcardSplat', }, ] satisfies Array<{ id: string pathname: string params: Record destHeadingId: string }> test.describe('Link', () => { WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => { test(`interpolation for testid="${id}" has href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await expect(link).toHaveAttribute('href', pathname) }) }) WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => { test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await link.click() await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) }) }) }) WILDCARD_PARAM_PAIRS.forEach(({ pathname, params, destHeadingId }) => { test(`on first-load to "${pathname}" has correct params`, async ({ page, }) => { await page.goto(pathname) await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) const headingEl = page.getByRole('heading', { name: destHeadingId }) await expect(headingEl).toBeVisible() const paramsEl = page.getByTestId('params-output') const paramsText = await paramsEl.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual(params) }) }) }) }) test.describe('Unicode params', () => { test('should render non-latin route correctly across multiple params', async ({ page, baseURL, }) => { await page.goto('/params-ps') await page.waitForURL('/params-ps') const fooLink = page.getByTestId('l-to-named-foo-special-characters') await fooLink.click() await page.waitForURL('/params-ps/named/foo%25%5C%2F%F0%9F%9A%80%EB%8C%80') expect(page.url()).toBe( `${baseURL}/params-ps/named/foo%25%5C%2F%F0%9F%9A%80%EB%8C%80`, ) const headingEl = page.getByRole('heading', { name: 'ParamsNamedFoo' }) await expect(headingEl).toBeVisible() let paramsEl = page.getByTestId('params-output') let paramsText = await paramsEl.innerText() expect(paramsText).toEqual(JSON.stringify({ foo: 'foo%\\/🚀대' })) const barLink = page.getByTestId('params-foo-links-bar-special-characters') await barLink.click() await page.waitForURL( '/params-ps/named/foo%25%5C%2F%F0%9F%9A%80%EB%8C%80/%F0%9F%9A%80%252F%2Fabc%EB%8C%80', ) expect(page.url()).toBe( `${baseURL}/params-ps/named/foo%25%5C%2F%F0%9F%9A%80%EB%8C%80/%F0%9F%9A%80%252F%2Fabc%EB%8C%80`, ) paramsEl = page.getByTestId('foo-bar-value') paramsText = await paramsEl.innerText() expect(paramsText).toEqual('🚀%2F/abc대') }) test.describe('should handle routes with non-latin paths and params correctly', () => { const testCases = [ { name: 'named', childPath: '🚀', latinParams: 'foo', unicodeParams: 'foo%\\/🚀대', }, { name: 'wildcard', childPath: 'wildcard', latinParams: 'foo/bar', unicodeParams: 'foo%\\/🚀대', }, ] testCases.forEach(({ name, childPath, latinParams, unicodeParams }) => { test(`${name} params`, async ({ page, baseURL }) => { const pascalCaseName = name.charAt(0).toUpperCase() + name.slice(1) const routeParentPath = '/대한민국' const encodedRouteParentPath = encodeURI(routeParentPath) const childRoutePath = `${routeParentPath}/${childPath}` const encodedChildRoutePath = encodeURI(childRoutePath) await page.goto(routeParentPath) await page.waitForURL(encodedRouteParentPath) const headingRootEl = page.getByTestId('unicode-heading') expect(await headingRootEl.innerText()).toBe('Hello "/대한민국"!') const latinLink = page.getByTestId(`l-to-${name}-latin`) const unicodeLink = page.getByTestId(`l-to-${name}-unicode`) await expect(latinLink).not.toContainClass('font-bold') await expect(unicodeLink).not.toContainClass('font-bold') await latinLink.click() await page.waitForURL(`${encodedChildRoutePath}/${latinParams}`) expect(page.url()).toBe( `${baseURL}${encodedChildRoutePath}/${latinParams}`, ) await expect(latinLink).toContainClass('font-bold') await expect(unicodeLink).not.toContainClass('font-bold') const headingEl = page.getByTestId(`unicode-${name}-heading`) const paramsEl = page.getByTestId(`unicode-${name}-params`) expect(await headingEl.innerText()).toBe( `Unicode ${pascalCaseName} Params`, ) expect(await paramsEl.innerText()).toBe(latinParams) await unicodeLink.click() const encodedParams = name === 'wildcard' ? encodeURI(unicodeParams) : encodeURIComponent(unicodeParams) await page.waitForURL(`${encodedChildRoutePath}/${encodedParams}`) expect(page.url()).toBe( `${baseURL}${encodedChildRoutePath}/${encodedParams}`, ) await expect(latinLink).not.toContainClass('font-bold') await expect(unicodeLink).toContainClass('font-bold') expect(await headingEl.innerText()).toBe( `Unicode ${pascalCaseName} Params`, ) expect(await paramsEl.innerText()).toBe(unicodeParams) }) }) }) }) test.describe('useParams strict false uses parsed child params', () => { test.beforeEach(async ({ page }) => { await page.goto('/params-ps') }) test('parent receives parsed values after child navigation', async ({ page, }) => { await page.getByTestId('strict-false-version-1').click() await page.waitForURL('/params-ps/strict-false/1') await expect(page.getByTestId('strict-false-version-type')).toHaveText( 'number', ) await expect(page.getByTestId('strict-false-version-value')).toHaveText('1') await page.getByTestId('strict-false-version-2').click() await page.waitForURL('/params-ps/strict-false/2') await expect(page.getByTestId('strict-false-version-type')).toHaveText( 'number', ) await expect(page.getByTestId('strict-false-version-value')).toHaveText('2') }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/redirect.spec.ts ================================================ import queryString from 'node:querystring' import { expect, test } from '@playwright/test' import combinateImport from 'combinate' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } // somehow playwright does not correctly import default exports const combinate = (combinateImport as any).default as typeof combinateImport const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_HOST_PORT = await getDummyServerPort(packageJson.name) test.describe('redirects', () => { const internalNavigationTestMatrix = combinate({ thrower: ['beforeLoad', 'loader'] as const, reloadDocument: [false, true] as const, preload: [false, true] as const, }) internalNavigationTestMatrix.forEach( ({ thrower, reloadDocument, preload }) => { test(`internal target, navigation: thrower: ${thrower}, reloadDocument: ${reloadDocument}, preload: ${preload}`, async ({ page, }) => { await page.waitForLoadState('networkidle') await page.goto( `/redirect/internal${preload === false ? '?preload=false' : ''}`, ) const link = page.getByTestId( `via-${thrower}${reloadDocument ? '-reloadDocument' : ''}`, ) await page.waitForLoadState('networkidle') let requestHappened = false const requestPromise = new Promise((resolve) => { page.on('request', (request) => { if ( request.url() === `http://localhost:${EXTERNAL_HOST_PORT}/posts` ) { requestHappened = true resolve() } }) }) await link.focus() const expectRequestHappened = preload && !reloadDocument const timeoutPromise = new Promise((resolve) => setTimeout(resolve, expectRequestHappened ? 5000 : 500), ) await Promise.race([requestPromise, timeoutPromise]) await page.waitForLoadState('networkidle') expect(requestHappened).toBe(expectRequestHappened) await link.click() let fullPageLoad = false page.on('domcontentloaded', () => { fullPageLoad = true }) const url = `http://localhost:${PORT}/posts` await page.waitForURL(url) if (reloadDocument) { await page.waitForLoadState('domcontentloaded') } expect(page.url()).toBe(url) await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() expect(fullPageLoad).toBe(reloadDocument) }) }, ) const internalDirectVisitTestMatrix = combinate({ thrower: ['beforeLoad', 'loader'] as const, reloadDocument: [false, true] as const, }) internalDirectVisitTestMatrix.forEach(({ thrower, reloadDocument }) => { test(`internal target, direct visit: thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({ page, }) => { await page.waitForLoadState('networkidle') await page.goto(`/redirect/internal/via-${thrower}`) const url = `http://localhost:${PORT}/posts` await page.waitForURL(url) expect(page.url()).toBe(url) await page.waitForLoadState('networkidle') await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() }) }) const externalTestMatrix = combinate({ scenario: ['navigate', 'direct_visit'] as const, thrower: ['beforeLoad', 'loader'] as const, }) externalTestMatrix.forEach(({ scenario, thrower }) => { test(`external target: scenario: ${scenario}, thrower: ${thrower}`, async ({ page, }) => { await page.waitForLoadState('networkidle') const q = queryString.stringify({ externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, }) if (scenario === 'navigate') { await page.goto(`/redirect/external?${q}`) await page.getByTestId(`via-${thrower}`).click() } else { await page.goto(`/redirect/external/via-${thrower}?${q}`) } const url = `http://localhost:${EXTERNAL_HOST_PORT}/` await page.waitForURL(url) expect(page.url()).toBe(url) }) }) test('regression test for #3097', async ({ page }) => { await page.goto(`/redirect/preload/first`) const link = page.getByTestId(`link`) await link.focus() await link.click() await page.waitForURL('/redirect/preload/third') await expect(page.getByTestId(`third`)).toBeInViewport() }) // Tests for Route.redirect() method - tests relative redirects test.describe('Route.redirect()', () => { const routeRedirectInternalTestMatrix = combinate({ thrower: ['beforeLoad', 'loader'] as const, }) // Test internal relative redirects (the key feature being tested) routeRedirectInternalTestMatrix.forEach(({ thrower }) => { test(`internal target (relative redirect), navigation: thrower: ${thrower}`, async ({ page, }) => { await page.goto('/redirect/internal') await page.waitForLoadState('networkidle') const link = page.getByTestId(`via-route-redirect-${thrower}`) await link.click() // Should redirect to the relative ./destination route const url = `http://localhost:${PORT}/redirect/internal/destination` await page.waitForURL(url) expect(page.url()).toBe(url) await expect(page.getByTestId('redirect-destination')).toBeInViewport() }) }) routeRedirectInternalTestMatrix.forEach(({ thrower }) => { test(`internal target (relative redirect), direct visit: thrower: ${thrower}`, async ({ page, }) => { await page.goto(`/redirect/internal/via-route-redirect-${thrower}`) // Should redirect to the relative ./destination route const url = `http://localhost:${PORT}/redirect/internal/destination` await page.waitForURL(url) expect(page.url()).toBe(url) await expect(page.getByTestId('redirect-destination')).toBeInViewport() }) }) // Test external redirects still work with Route.redirect() const externalRouteRedirectTestMatrix = combinate({ scenario: ['navigate', 'direct_visit'] as const, thrower: ['beforeLoad', 'loader'] as const, }) externalRouteRedirectTestMatrix.forEach(({ scenario, thrower }) => { test(`external target: scenario: ${scenario}, thrower: ${thrower}`, async ({ page, }) => { const q = queryString.stringify({ externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, }) if (scenario === 'navigate') { await page.goto(`/redirect/external?${q}`) await page.waitForLoadState('networkidle') await page.getByTestId(`via-route-redirect-${thrower}`).click() } else { await page.goto( `/redirect/external/via-route-redirect-${thrower}?${q}`, ) } const url = `http://localhost:${EXTERNAL_HOST_PORT}/` await page.waitForURL(url) expect(page.url()).toBe(url) }) }) }) // Tests for getRouteApi().redirect() method - tests relative redirects test.describe('getRouteApi().redirect()', () => { const routeApiRedirectInternalTestMatrix = combinate({ thrower: ['beforeLoad', 'loader'] as const, }) // Test internal relative redirects (the key feature being tested) routeApiRedirectInternalTestMatrix.forEach(({ thrower }) => { test(`internal target (relative redirect), navigation: thrower: ${thrower}`, async ({ page, }) => { await page.goto('/redirect/internal') await page.waitForLoadState('networkidle') const link = page.getByTestId(`via-routeApi-redirect-${thrower}`) await link.click() // Should redirect to the relative ./destination route const url = `http://localhost:${PORT}/redirect/internal/destination` await page.waitForURL(url) expect(page.url()).toBe(url) await expect(page.getByTestId('redirect-destination')).toBeInViewport() }) }) routeApiRedirectInternalTestMatrix.forEach(({ thrower }) => { test(`internal target (relative redirect), direct visit: thrower: ${thrower}`, async ({ page, }) => { await page.goto(`/redirect/internal/via-routeApi-redirect-${thrower}`) // Should redirect to the relative ./destination route const url = `http://localhost:${PORT}/redirect/internal/destination` await page.waitForURL(url) expect(page.url()).toBe(url) await expect(page.getByTestId('redirect-destination')).toBeInViewport() }) }) // Test external redirects still work with getRouteApi().redirect() const externalRouteApiRedirectTestMatrix = combinate({ scenario: ['navigate', 'direct_visit'] as const, thrower: ['beforeLoad', 'loader'] as const, }) externalRouteApiRedirectTestMatrix.forEach(({ scenario, thrower }) => { test(`external target: scenario: ${scenario}, thrower: ${thrower}`, async ({ page, }) => { const q = queryString.stringify({ externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, }) if (scenario === 'navigate') { await page.goto(`/redirect/external?${q}`) await page.waitForLoadState('networkidle') await page.getByTestId(`via-routeApi-redirect-${thrower}`).click() } else { await page.goto( `/redirect/external/via-routeApi-redirect-${thrower}?${q}`, ) } const url = `http://localhost:${EXTERNAL_HOST_PORT}/` await page.waitForURL(url) expect(page.url()).toBe(url) }) }) }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/relative.spec.ts ================================================ import { expect, test } from '@playwright/test' import combinateImport from 'combinate' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } // somehow playwright does not correctly import default exports const combinate = (combinateImport as any).default as typeof combinateImport const PORT = await getTestServerPort(packageJson.name) test.describe('relative_routing', () => { const internalNavigationTestMatrix = combinate({ navigation: ['link', 'useNavigate'] as const, }) internalNavigationTestMatrix.forEach(({ navigation }) => { test(`simple relative navigation. navigation: ${navigation}`, async ({ page, }) => { await page.waitForLoadState('networkidle') await page.goto(`/relative/${navigation}`) await page.waitForURL(`http://localhost:${PORT}/relative/${navigation}`) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() const indexLink = page.getByTestId(`relative-${navigation}-index`) const backLink = page.getByTestId(`relative-${navigation}-back`) const relativeRouteA = page.getByTestId(`relative-${navigation}-a`) const relativeRouteB = page.getByTestId(`relative-${navigation}-b`) await relativeRouteA.click() await page.waitForURL( `http://localhost:${PORT}/relative/${navigation}/relative-${navigation}-a`, ) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() await expect( page.getByTestId(`relative-${navigation}-a-header`), ).toBeInViewport() await relativeRouteB.click() await page.waitForURL( `http://localhost:${PORT}/relative/${navigation}/relative-${navigation}-b`, ) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() await expect( page.getByTestId(`relative-${navigation}-b-header`), ).toBeInViewport() await indexLink.click() await page.waitForURL(`http://localhost:${PORT}/relative/${navigation}`) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() await expect( page.getByTestId(`relative-${navigation}-b-header`), ).not.toBeInViewport() await backLink.click() await page.waitForURL(`http://localhost:${PORT}/relative`) await expect(page.getByTestId(`relative-routing-home`)).toBeInViewport() }) test(`nested chilren. navigation: ${navigation}`, async ({ page }) => { await page.waitForLoadState('networkidle') await page.goto(`/relative/${navigation}`) await page.waitForURL(`http://localhost:${PORT}/relative/${navigation}`) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() const backLink = page.getByTestId(`relative-${navigation}-back`) const deeplyNestedChildRoute = page.getByTestId( `relative-${navigation}-deeply-nested`, ) await deeplyNestedChildRoute.click() await page.waitForURL( `http://localhost:${PORT}/relative/${navigation}/nested/deep`, ) await expect( page.getByTestId(`relative-${navigation}-nested-deep-header`), ).toBeInViewport() await backLink.click() await page.waitForURL( `http://localhost:${PORT}/relative/${navigation}/nested`, ) await expect( page.getByTestId(`relative-${navigation}-nested-header`), ).toBeInViewport() }) test(`with path params. navigation: ${navigation}`, async ({ page }) => { await page.waitForLoadState('networkidle') await page.goto(`/relative/${navigation}`) await page.waitForURL(`http://localhost:${PORT}/relative/${navigation}`) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() const pathParamRoute = page.getByTestId(`relative-${navigation}-path`) await pathParamRoute.click() await page.waitForURL( `http://localhost:${PORT}/relative/${navigation}/path/a`, ) expect(page.url().endsWith('/path/a')).toBe(true) await expect( page.getByTestId(`relative-${navigation}-path-param-header`), ).toBeInViewport() const switchParamLink = page.getByTestId( `relative-${navigation}-path-param-switchAB`, ) await switchParamLink.click() await page.waitForURL( `http://localhost:${PORT}/relative/${navigation}/path/b`, ) expect(page.url().endsWith('/path/b')).toBe(true) await expect( page.getByTestId(`relative-${navigation}-path-param-header`), ).toBeInViewport() }) test(`with search params. navigation: ${navigation}`, async ({ page }) => { await page.waitForLoadState('networkidle') await page.goto(`/relative/${navigation}`) await page.waitForURL(`http://localhost:${PORT}/relative/${navigation}`) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() const searchParamRoute = page.getByTestId( `relative-${navigation}-withSearch`, ) await searchParamRoute.click() let expectedUrl = new URL( `http://localhost:${PORT}/relative/${navigation}/with-search?searchParam="1"`, ) await page.waitForLoadState('domcontentloaded') expect(page.url().toString()).toBe(expectedUrl.toString()) await expect( page.getByTestId(`relative-${navigation}-withSearch-header`), ).toBeInViewport() await expect( page.getByTestId(`relative-${navigation}-withSearch-header`), ).toContainText('searchParam: 1') const updateSearchLink = page.getByTestId( `relative-${navigation}-withSearch-update-param`, ) await updateSearchLink.click() await page.waitForLoadState('domcontentloaded') expectedUrl = new URL( `http://localhost:${PORT}/relative/${navigation}/with-search?searchParam="2"`, ) expect(page.url().toString()).toBe(expectedUrl.toString()) await expect( page.getByTestId(`relative-${navigation}-withSearch-header`), ).toContainText('searchParam: 2') await expect( page.getByTestId(`relative-${navigation}-withSearch-header`), ).toBeInViewport() }) test(`shouldn't cause excessive rendering. navigation: ${navigation}`, async ({ page, }) => { let navigateMsgs = 0 page.on('console', (msg) => { if (msg.text() === 'navigate') { navigateMsgs++ } }) await page.waitForLoadState('networkidle') await page.goto(`/relative/${navigation}`) await page.waitForURL(`http://localhost:${PORT}/relative/${navigation}`) await expect( page.getByTestId(`relative-${navigation}-header`), ).toBeInViewport() expect(navigateMsgs).toBe(1) const indexLink = page.getByTestId(`relative-${navigation}-index`) const backLink = page.getByTestId(`relative-${navigation}-back`) const relativeRouteA = page.getByTestId(`relative-${navigation}-a`) const relativeRouteB = page.getByTestId(`relative-${navigation}-b`) const deeplyNestedChildRoute = page.getByTestId( `relative-${navigation}-deeply-nested`, ) const pathParamRoute = page.getByTestId(`relative-${navigation}-path`) const searchParamRoute = page.getByTestId( `relative-${navigation}-withSearch`, ) await relativeRouteA.click() await page.waitForLoadState('domcontentloaded') await relativeRouteB.click() await page.waitForLoadState('domcontentloaded') await deeplyNestedChildRoute.click() await page.waitForLoadState('domcontentloaded') await backLink.click() await page.waitForLoadState('domcontentloaded') await pathParamRoute.click() await page.waitForLoadState('domcontentloaded') const switchParamLink = page.getByTestId( `relative-${navigation}-path-param-switchAB`, ) await switchParamLink.click() await page.waitForLoadState('domcontentloaded') await searchParamRoute.click() await page.waitForLoadState('domcontentloaded') const updateSearchLink = page.getByTestId( `relative-${navigation}-withSearch-update-param`, ) await updateSearchLink.click() await page.waitForLoadState('domcontentloaded') await indexLink.click() await page.waitForLoadState('domcontentloaded') expect(navigateMsgs).toBe(1) }) }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/scroll-into-view.spec.ts ================================================ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' const anchors = { defaultAnchor: 'default-anchor', noScrollIntoView: 'false-anchor', smoothScroll: 'smooth-scroll', } as const const formTestIds = { targetAnchor: 'hash-select', scrollIntoView: 'with-scroll', behaviorSelect: 'behavior-select', blockSelect: 'block-select', inlineSelect: 'inline-select', navigateButton: 'navigate-button', } const shownSuffix = '(shown)' const activeClass = 'font-bold active' function getAnchorTarget(page: Page, anchor: string) { return page.getByTestId(`heading-${anchor}`) } function getAnchorLink(page: Page, anchor: string) { return page.getByTestId(`link-${anchor}`) } test.beforeEach(async ({ page }) => { await page.goto('/anchor') }) // Testing the `Link` component with the `hashScrollIntoView` prop test('Navigating via anchor `Link` with default hash scrolling behavior', async ({ page, }) => { await expect(getAnchorTarget(page, anchors.defaultAnchor)).not.toContainText( shownSuffix, ) await getAnchorLink(page, anchors.defaultAnchor).click() await expect(getAnchorTarget(page, anchors.defaultAnchor)).toBeVisible() await expect(getAnchorTarget(page, anchors.defaultAnchor)).toContainText( shownSuffix, ) await expect(getAnchorLink(page, anchors.defaultAnchor)).toHaveClass( activeClass, ) }) test('Navigating via anchor `Link` with hash scrolling disabled', async ({ page, }) => { const initialScrollPosition = await page.evaluate(() => window.scrollY) await expect( getAnchorTarget(page, anchors.noScrollIntoView), ).not.toContainText(shownSuffix) await getAnchorLink(page, anchors.noScrollIntoView).click() // The active anchor should have updated await expect(getAnchorLink(page, anchors.noScrollIntoView)).toHaveClass( activeClass, ) // The anchor should not have been visible, because the scroll should not have been activated await expect(getAnchorTarget(page, anchors.defaultAnchor)).not.toContainText( shownSuffix, ) // Expect the same scroll position as before expect(await page.evaluate(() => window.scrollY)).toBe(initialScrollPosition) }) test('Navigating via anchor `Link` with smooth hash scrolling behavior', async ({ page, }) => { await expect(getAnchorTarget(page, anchors.smoothScroll)).not.toContainText( shownSuffix, ) await getAnchorLink(page, anchors.smoothScroll).click() await expect(getAnchorTarget(page, anchors.smoothScroll)).toBeVisible() // Smooth scrolling should activate the IntersectionObserver on all headings, making them all render "(shown)" await expect(getAnchorTarget(page, anchors.defaultAnchor)).toContainText( shownSuffix, ) await expect(getAnchorTarget(page, anchors.noScrollIntoView)).toContainText( shownSuffix, ) await expect(getAnchorTarget(page, anchors.smoothScroll)).toContainText( shownSuffix, ) await expect(getAnchorLink(page, anchors.smoothScroll)).toHaveClass( activeClass, ) }) // Testing the `useNavigate` hook with the `hashScrollIntoView` option test('Navigating via `useNavigate` with instant scroll behavior', async ({ page, }) => { await expect(getAnchorTarget(page, anchors.smoothScroll)).not.toContainText( shownSuffix, ) // Scroll to the last anchor instantly, should not activate Intersection Observers for the other anchors await page.getByTestId(formTestIds.targetAnchor).selectOption('Smooth Scroll') await page.getByTestId(formTestIds.scrollIntoView).check() await page.getByTestId(formTestIds.behaviorSelect).selectOption('instant') await page.getByTestId(formTestIds.blockSelect).selectOption('start') await page.getByTestId(formTestIds.inlineSelect).selectOption('nearest') await page.getByTestId(formTestIds.navigateButton).click() await expect(getAnchorTarget(page, anchors.defaultAnchor)).not.toContainText( shownSuffix, ) await expect( getAnchorTarget(page, anchors.noScrollIntoView), ).not.toContainText(shownSuffix) await expect(getAnchorTarget(page, anchors.smoothScroll)).toContainText( shownSuffix, ) await expect(getAnchorLink(page, anchors.smoothScroll)).toBeVisible() await expect(getAnchorLink(page, anchors.smoothScroll)).toHaveClass( activeClass, ) }) test('Navigating via `useNavigate` with scrollIntoView disabled', async ({ page, }) => { const initialScrollPosition = await page.evaluate(() => window.scrollY) await expect(getAnchorTarget(page, anchors.defaultAnchor)).not.toContainText( shownSuffix, ) await expect( getAnchorTarget(page, anchors.noScrollIntoView), ).not.toContainText(shownSuffix) await expect(getAnchorTarget(page, anchors.smoothScroll)).not.toContainText( shownSuffix, ) // Navigate to the last anchor, but with scrollIntoView disabled should not activate Intersection Observers for any anchors await page.getByTestId(formTestIds.targetAnchor).selectOption('Smooth Scroll') await page.getByTestId(formTestIds.scrollIntoView).uncheck() await page.getByTestId(formTestIds.navigateButton).click() await expect(getAnchorTarget(page, anchors.defaultAnchor)).not.toContainText( shownSuffix, ) await expect( getAnchorTarget(page, anchors.noScrollIntoView), ).not.toContainText(shownSuffix) await expect(getAnchorTarget(page, anchors.smoothScroll)).not.toContainText( shownSuffix, ) await expect(getAnchorLink(page, anchors.smoothScroll)).toHaveClass( activeClass, ) // Expect the same scroll position as before expect(await page.evaluate(() => window.scrollY)).toBe(initialScrollPosition) }) test('Navigating via `useNavigate` with smooth scroll behavior', async ({ page, }) => { await expect(getAnchorTarget(page, anchors.smoothScroll)).not.toContainText( shownSuffix, ) // Scroll to the last anchor smoothly, should activate Intersection Observers for the other anchors, making them all render "(shown)" await page.getByTestId(formTestIds.targetAnchor).selectOption('Smooth Scroll') await page.getByTestId(formTestIds.scrollIntoView).check() await page.getByTestId(formTestIds.behaviorSelect).selectOption('smooth') await page.getByTestId(formTestIds.blockSelect).selectOption('start') await page.getByTestId(formTestIds.inlineSelect).selectOption('nearest') await page.getByTestId(formTestIds.navigateButton).click() await expect(getAnchorTarget(page, anchors.defaultAnchor)).toContainText( shownSuffix, ) await expect(getAnchorTarget(page, anchors.noScrollIntoView)).toContainText( shownSuffix, ) await expect(getAnchorTarget(page, anchors.smoothScroll)).toContainText( shownSuffix, ) await expect(getAnchorLink(page, anchors.smoothScroll)).toHaveClass( activeClass, ) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/search-params.spec.ts ================================================ import { expect, test } from '@playwright/test' test.describe('/search-params/default', () => { test('Directly visiting the route without search param set', async ({ page, }) => { await page.goto('/search-params/default') await page.waitForURL('/search-params/default?default=d1') await expect(page.getByTestId('search-default')).toContainText('d1') await expect(page.getByTestId('context-hello')).toContainText('world') expect( page.url().endsWith('/search-params/default?default=d1'), ).toBeTruthy() }) test('Directly visiting the route with search param set', async ({ page, }) => { await page.goto('/search-params/default/?default=d2') await page.waitForURL('/search-params/default?default=d2') await expect(page.getByTestId('search-default')).toContainText('d2') await expect(page.getByTestId('context-hello')).toContainText('world') expect( page.url().endsWith('/search-params/default?default=d2'), ).toBeTruthy() }) test('Directly visiting the route with special character search param set', async ({ page, }) => { await page.goto('/search-params/default/?default=🚀대한민국') await page.waitForURL( '/search-params/default?default=%F0%9F%9A%80%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', ) await expect(page.getByTestId('search-default')).toContainText('🚀대한민국') await expect(page.getByTestId('context-hello')).toContainText('world') expect( page .url() .endsWith( '/search-params/default?default=%F0%9F%9A%80%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', ), ).toBeTruthy() }) test('Directly visiting the route with encoded character search param set', async ({ page, }) => { await page.goto( '/search-params/default/?default=%25EB%258C%2580%25ED%2595%259C%25EB%25AF%25BC%25EA%25B5%25AD', ) await page.waitForURL( '/search-params/default?default=%25EB%258C%2580%25ED%2595%259C%25EB%25AF%25BC%25EA%25B5%25AD', ) await expect(page.getByTestId('search-default')).toContainText( '%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', ) await expect(page.getByTestId('context-hello')).toContainText('world') expect( page .url() .endsWith( '/search-params/default?default=%25EB%258C%2580%25ED%2595%259C%25EB%25AF%25BC%25EA%25B5%25AD', ), ).toBeTruthy() }) test('navigating to the route without search param set', async ({ page }) => { await page.goto('/search-params/') await page.getByTestId('link-to-default-without-search').click() await page.waitForURL('/search-params/default?default=d1') await expect(page.getByTestId('search-default')).toContainText('d1') await expect(page.getByTestId('context-hello')).toContainText('world') expect( page.url().endsWith('/search-params/default?default=d1'), ).toBeTruthy() }) test('navigating to the route with search param set', async ({ page }) => { await page.goto('/search-params/') await page.getByTestId('link-to-default-with-search').click() await page.waitForURL('/search-params/default?default=d2') await expect(page.getByTestId('search-default')).toContainText('d2') await expect(page.getByTestId('context-hello')).toContainText('world') expect( page.url().endsWith('/search-params/default?default=d2'), ).toBeTruthy() }) test('navigating to the route with special character search param set', async ({ page, }) => { await page.goto('/search-params/') await page .getByTestId('link-to-default-with-search-special-characters') .click() await page.waitForURL( '/search-params/default?default=%F0%9F%9A%80%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', ) await expect(page.getByTestId('search-default')).toContainText('🚀대한민국') await expect(page.getByTestId('context-hello')).toContainText('world') expect( page .url() .endsWith( '/search-params/default?default=%F0%9F%9A%80%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', ), ).toBeTruthy() }) test('navigating to the route with encoded character search param set', async ({ page, }) => { await page.goto('/search-params/') await page .getByTestId('link-to-default-with-search-encoded-characters') .click() await page.waitForURL( '/search-params/default?default=%25EB%258C%2580%25ED%2595%259C%25EB%25AF%25BC%25EA%25B5%25AD', ) await expect(page.getByTestId('search-default')).toContainText( '%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD', ) await expect(page.getByTestId('context-hello')).toContainText('world') expect( page .url() .endsWith( '/search-params/default?default=%25EB%258C%2580%25ED%2595%259C%25EB%25AF%25BC%25EA%25B5%25AD', ), ).toBeTruthy() }) }) ================================================ FILE: e2e/react-router/basic-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', }), react(), ], }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-file-based-code-splitting/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-file-based-code-splitting", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e:verbose-routes:true": "rm -rf port*.txt; VERBOSE_FILE_ROUTES=1 playwright test --project=chromium", "test:e2e:verbose-routes:false": "rm -rf port*.txt; VERBOSE_FILE_ROUTES=0 playwright test --project=chromium", "test:e2e": "rm -rf port*.txt; pnpm run test:e2e:verbose-routes:true && pnpm run test:e2e:verbose-routes:false" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' import { Route as WithoutLoaderRouteImport } from './routes/without-loader' import { Route as ViewportTestRouteImport } from './routes/viewport-test' import { Route as SharedSingletonRouteImport } from './routes/shared-singleton' import { Route as PostsRouteImport } from './routes/posts' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' const WithoutLoaderRoute = WithoutLoaderRouteImport.update({ id: '/without-loader', path: '/without-loader', getParentRoute: () => rootRouteImport, } as any) const ViewportTestRoute = ViewportTestRouteImport.update({ id: '/viewport-test', path: '/viewport-test', getParentRoute: () => rootRouteImport, } as any) const SharedSingletonRoute = SharedSingletonRouteImport.update({ id: '/shared-singleton', path: '/shared-singleton', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/shared-singleton': typeof SharedSingletonRoute '/viewport-test': typeof ViewportTestRoute '/without-loader': typeof WithoutLoaderRoute '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/shared-singleton': typeof SharedSingletonRoute '/viewport-test': typeof ViewportTestRoute '/without-loader': typeof WithoutLoaderRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/posts': typeof PostsRouteWithChildren '/shared-singleton': typeof SharedSingletonRoute '/viewport-test': typeof ViewportTestRoute '/without-loader': typeof WithoutLoaderRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/shared-singleton' | '/viewport-test' | '/without-loader' | '/posts/$postId' | '/posts/' | '/layout-a' | '/layout-b' fileRoutesByTo: FileRoutesByTo to: | '/' | '/shared-singleton' | '/viewport-test' | '/without-loader' | '/posts/$postId' | '/posts' | '/layout-a' | '/layout-b' id: | '__root__' | '/' | '/_layout' | '/posts' | '/shared-singleton' | '/viewport-test' | '/without-loader' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren PostsRoute: typeof PostsRouteWithChildren SharedSingletonRoute: typeof SharedSingletonRoute ViewportTestRoute: typeof ViewportTestRoute WithoutLoaderRoute: typeof WithoutLoaderRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/without-loader': { id: '/without-loader' path: '/without-loader' fullPath: '/without-loader' preLoaderRoute: typeof WithoutLoaderRouteImport parentRoute: typeof rootRouteImport } '/viewport-test': { id: '/viewport-test' path: '/viewport-test' fullPath: '/viewport-test' preLoaderRoute: typeof ViewportTestRouteImport parentRoute: typeof rootRouteImport } '/shared-singleton': { id: '/shared-singleton' path: '/shared-singleton' fullPath: '/shared-singleton' preLoaderRoute: typeof SharedSingletonRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } } } declare module './routes/index' { const createFileRoute: CreateFileRoute< '/', FileRoutesByPath['/']['parentRoute'], FileRoutesByPath['/']['id'], FileRoutesByPath['/']['path'], FileRoutesByPath['/']['fullPath'] > } declare module './routes/_layout' { const createFileRoute: CreateFileRoute< '/_layout', FileRoutesByPath['/_layout']['parentRoute'], FileRoutesByPath['/_layout']['id'], FileRoutesByPath['/_layout']['path'], FileRoutesByPath['/_layout']['fullPath'] > } declare module './routes/posts' { const createFileRoute: CreateFileRoute< '/posts', FileRoutesByPath['/posts']['parentRoute'], FileRoutesByPath['/posts']['id'], FileRoutesByPath['/posts']['path'], FileRoutesByPath['/posts']['fullPath'] > } declare module './routes/shared-singleton' { const createFileRoute: CreateFileRoute< '/shared-singleton', FileRoutesByPath['/shared-singleton']['parentRoute'], FileRoutesByPath['/shared-singleton']['id'], FileRoutesByPath['/shared-singleton']['path'], FileRoutesByPath['/shared-singleton']['fullPath'] > } declare module './routes/viewport-test' { const createFileRoute: CreateFileRoute< '/viewport-test', FileRoutesByPath['/viewport-test']['parentRoute'], FileRoutesByPath['/viewport-test']['id'], FileRoutesByPath['/viewport-test']['path'], FileRoutesByPath['/viewport-test']['fullPath'] > } declare module './routes/without-loader' { const createFileRoute: CreateFileRoute< '/without-loader', FileRoutesByPath['/without-loader']['parentRoute'], FileRoutesByPath['/without-loader']['id'], FileRoutesByPath['/without-loader']['path'], FileRoutesByPath['/without-loader']['fullPath'] > } declare module './routes/_layout/_layout-2' { const createFileRoute: CreateFileRoute< '/_layout/_layout-2', FileRoutesByPath['/_layout/_layout-2']['parentRoute'], FileRoutesByPath['/_layout/_layout-2']['id'], FileRoutesByPath['/_layout/_layout-2']['path'], FileRoutesByPath['/_layout/_layout-2']['fullPath'] > } declare module './routes/posts.$postId' { const createFileRoute: CreateFileRoute< '/posts/$postId', FileRoutesByPath['/posts/$postId']['parentRoute'], FileRoutesByPath['/posts/$postId']['id'], FileRoutesByPath['/posts/$postId']['path'], FileRoutesByPath['/posts/$postId']['fullPath'] > } declare module './routes/posts.index' { const createFileRoute: CreateFileRoute< '/posts/', FileRoutesByPath['/posts/']['parentRoute'], FileRoutesByPath['/posts/']['id'], FileRoutesByPath['/posts/']['path'], FileRoutesByPath['/posts/']['fullPath'] > } declare module './routes/_layout/_layout-2/layout-a' { const createFileRoute: CreateFileRoute< '/_layout/_layout-2/layout-a', FileRoutesByPath['/_layout/_layout-2/layout-a']['parentRoute'], FileRoutesByPath['/_layout/_layout-2/layout-a']['id'], FileRoutesByPath['/_layout/_layout-2/layout-a']['path'], FileRoutesByPath['/_layout/_layout-2/layout-a']['fullPath'] > } declare module './routes/_layout/_layout-2/layout-b' { const createFileRoute: CreateFileRoute< '/_layout/_layout-2/layout-b', FileRoutesByPath['/_layout/_layout-2/layout-b']['parentRoute'], FileRoutesByPath['/_layout/_layout-2/layout-b']['id'], FileRoutesByPath['/_layout/_layout-2/layout-b']['path'], FileRoutesByPath['/_layout/_layout-2/layout-b']['fullPath'] > } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, PostsRoute: PostsRouteWithChildren, SharedSingletonRoute: SharedSingletonRoute, ViewportTestRoute: ViewportTestRoute, WithoutLoaderRoute: WithoutLoaderRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} without-loader {' '} shared-singleton {' '} This Route Does Not Exist

viewport-test {/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/_layout/_layout-2/layout-a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/_layout/_layout-2/layout-b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/_layout.tsx ================================================ import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/index.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/posts.$postId.tsx ================================================ import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, codeSplitGroupings: [ ['component'], ['pendingComponent', 'errorComponent', 'notFoundComponent'], ], }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/posts.index.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/posts.tsx ================================================ import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/shared-singleton.tsx ================================================ import * as React from 'react' // All shared state lives in declarations — no bare expression statements. // The singleton tracks how many times this module scope executes. const singleton = (() => { const g = globalThis as any g.__tsrSharedSingleton ??= { initCount: 0 } g.__tsrSharedSingleton.initCount++ return { initCountAtCreate: g.__tsrSharedSingleton.initCount as number } })() function getInitCount() { return (globalThis as any).__tsrSharedSingleton?.initCount as number } export const Route = createFileRoute({ loader: () => { return { loaderSawInitCount: getInitCount(), initCountAtCreateFromLoader: singleton.initCountAtCreate, } }, component: SharedSingletonComponent, }) function SharedSingletonComponent() { const data = Route.useLoaderData() return (

Shared Singleton

initCount (global):{' '} {getInitCount()}
loaderSawInitCount:{' '} {data.loaderSawInitCount}
initCountAtCreate (loader module):{' '} {data.initCountAtCreateFromLoader}
initCountAtCreate (component module):{' '} {singleton.initCountAtCreate}
) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/viewport-test.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /viewport-test!
, }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/routes/without-loader.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /without-loader!
, }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/tests/preload.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('hovering a link with preload=intent to a route without a loader should preload route', async ({ page, }) => { await page.waitForLoadState('networkidle') const requestPromise = new Promise((resolve) => { page.on('request', (request) => { resolve(request.url()) }) }) await page.getByRole('link', { name: 'without-loader' }).hover() const url = await requestPromise const expectedString = process.env.NODE_ENV === 'development' ? 'without-loader.tsx?tsr-split' : '/assets/without-loader' expect(url).toContain(expectedString) }) test('scrolling into viewport a link with preload=viewport to a route should preload route', async ({ page, }) => { await page.waitForLoadState('networkidle') const [request] = await Promise.all([ page.waitForRequest(() => true), page.getByRole('link', { name: 'viewport-test' }).scrollIntoViewIfNeeded(), ]) const expectedString = process.env.NODE_ENV === 'development' ? 'viewport-test.tsx?tsr-split' : '/assets/viewport-test' expect(request.url()).toEqual(expect.stringContaining(expectedString)) }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/tests/shared-bindings.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('shared module bindings should init once', async ({ page }) => { await page.getByTestId('shared-singleton-link').click() // If module scope is duplicated between reference + split chunk, this becomes "2". await expect(page.getByTestId('shared-init-count')).toHaveText('1') // Extra signals (useful when debugging failures) await expect(page.getByTestId('shared-loader-saw')).toHaveText('1') await expect(page.getByTestId('shared-created-at-loader')).toHaveText('1') await expect(page.getByTestId('shared-created-at-module')).toHaveText('1') }) ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-file-based-code-splitting/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' const isVerboseFileRoutes = process.env.VERBOSE_FILE_ROUTES === '1' || false console.info(`Verbose file routes is set to: ${isVerboseFileRoutes}.`) // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', autoCodeSplitting: true, verboseFileRoutes: isVerboseFileRoutes, codeSplittingOptions: { splitBehavior: ({ routeId }) => { if ( // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition routeId === undefined || routeId.startsWith('$$TSR_') || // @ts-expect-error routeId === '' ) { console.error( 'The routeId is empty or undefined. This should not happen.', ) process.exit(1) } if (routeId === '/posts') { return [ ['loader'], ['component'], ['pendingComponent', 'notFoundComponent', 'errorComponent'], ] } }, }, }), react(), ], }) ================================================ FILE: e2e/react-router/basic-react-query/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-react-query/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-react-query/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-react-query/package.json ================================================ { "name": "tanstack-router-e2e-react-react-query", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "workspace:^", "@tanstack/router-devtools": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-react-query/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-react-query/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { ErrorComponent, Link, Outlet, RouterProvider, createRootRouteWithContext, createRoute, createRouter, useRouter, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/router-devtools' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { QueryClient, QueryClientProvider, useQueryErrorResetBoundary, useSuspenseQuery, } from '@tanstack/react-query' import { NotFoundError, postQueryOptions, postsQueryOptions } from './posts' import type { ErrorComponentProps } from '@tanstack/react-router' import './styles.css' const rootRoute = createRootRouteWithContext<{ queryClient: QueryClient }>()({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: IndexRouteComponent, }) function IndexRouteComponent() { return (

Welcome Home!

) } const postsRoute = createRoute({ getParentRoute: () => rootRoute, path: 'posts', loader: ({ context: { queryClient } }) => queryClient.ensureQueryData(postsQueryOptions), }).lazy(() => import('./posts.lazy').then((d) => d.Route)) const postsIndexRoute = createRoute({ getParentRoute: () => postsRoute, path: '/', component: PostsIndexRouteComponent, }) function PostsIndexRouteComponent() { return
Select a post.
} const postRoute = createRoute({ getParentRoute: () => postsRoute, path: '$postId', errorComponent: PostErrorComponent, loader: ({ context: { queryClient }, params: { postId } }) => queryClient.ensureQueryData(postQueryOptions(postId)), component: PostRouteComponent, }) function PostErrorComponent({ error, reset }: ErrorComponentProps) { const router = useRouter() if (error instanceof NotFoundError) { return
{error.message}
} const queryErrorResetBoundary = useQueryErrorResetBoundary() React.useEffect(() => { queryErrorResetBoundary.reset() }, [queryErrorResetBoundary]) return (
) } function PostRouteComponent() { const { postId } = postRoute.useParams() const postQuery = useSuspenseQuery(postQueryOptions(postId)) const post = postQuery.data return (

{post.title}

{post.body}
) } const layoutRoute = createRoute({ getParentRoute: () => rootRoute, id: '_layout', component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } const layout2Route = createRoute({ getParentRoute: () => layoutRoute, id: '_layout-2', component: Layout2Component, }) function Layout2Component() { return (
I'm a nested layout
Layout A Layout B
) } const layoutARoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-a', component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} const layoutBRoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-b', component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} const routeTree = rootRoute.addChildren([ postsRoute.addChildren([postRoute, postsIndexRoute]), layoutRoute.addChildren([ layout2Route.addChildren([layoutARoute, layoutBRoute]), ]), indexRoute, ]) const queryClient = new QueryClient() // Set up a Router instance const router = createRouter({ routeTree, scrollRestoration: true, defaultPreload: 'intent', // Since we're using React Query, we don't want loader calls to ever be stale // This will ensure that the loader is always called when the route is preloaded or visited defaultPreloadStaleTime: 0, context: { queryClient, }, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( , ) } ================================================ FILE: e2e/react-router/basic-react-query/src/posts.lazy.tsx ================================================ import * as React from 'react' import { Link, Outlet, createLazyRoute } from '@tanstack/react-router' import { useSuspenseQuery } from '@tanstack/react-query' import { postsQueryOptions } from './posts' export const Route = createLazyRoute('/posts')({ component: PostsComponent, }) function PostsComponent() { const postsQuery = useSuspenseQuery(postsQueryOptions) const posts = postsQuery.data return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}
) } ================================================ FILE: e2e/react-router/basic-react-query/src/posts.ts ================================================ import axios from 'redaxios' import { queryOptions } from '@tanstack/react-query' export class NotFoundError extends Error {} type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } export const postQueryOptions = (postId: string) => queryOptions({ queryKey: ['posts', { postId }], queryFn: () => fetchPost(postId), }) export const postsQueryOptions = queryOptions({ queryKey: ['posts'], queryFn: () => fetchPosts(), }) ================================================ FILE: e2e/react-router/basic-react-query/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-react-query/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/basic-react-query/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-react-query/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-react-query/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-react-query/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), react()], }) ================================================ FILE: e2e/react-router/basic-react-query-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-react-query-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-react-query-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-react-query-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-react-query-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-react-query-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { routeTree } from './routeTree.gen' import './styles.css' const queryClient = new QueryClient() // Set up a Router instance const router = createRouter({ routeTree, context: { queryClient, }, scrollRestoration: true, defaultPreload: 'intent', // Since we're using React Query, we don't want loader calls to ever be stale // This will ensure that the loader is always called when the route is preloaded or visited defaultPreloadStaleTime: 0, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( , ) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/postQueryOptions.tsx ================================================ import { queryOptions } from '@tanstack/react-query' import { fetchPost } from './posts' export const postQueryOptions = (postId: string) => queryOptions({ queryKey: ['posts', { postId }], queryFn: () => fetchPost(postId), }) ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/posts.tsx ================================================ import axios from 'redaxios' export type PostType = { id: string title: string body: string } export class PostNotFoundError extends Error {} let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw new PostNotFoundError(`Post with id "${postId}" not found!`) } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/postsQueryOptions.tsx ================================================ import { queryOptions } from '@tanstack/react-query' import { fetchPosts } from './posts' export const postsQueryOptions = queryOptions({ queryKey: ['posts'], queryFn: () => fetchPosts(), }) ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PostsRouteImport } from './routes/posts' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/posts': typeof PostsRouteWithChildren '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/posts/$postId' | '/posts/' | '/layout-a' | '/layout-b' fileRoutesByTo: FileRoutesByTo to: '/' | '/posts/$postId' | '/posts' | '/layout-a' | '/layout-b' id: | '__root__' | '/' | '/_layout' | '/posts' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren PostsRoute: typeof PostsRouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } } } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, PostsRoute: PostsRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRouteWithContext, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import type { QueryClient } from '@tanstack/react-query' export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/_layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ErrorComponent, useRouter } from '@tanstack/react-router' import { useQueryErrorResetBoundary, useSuspenseQuery, } from '@tanstack/react-query' import { PostNotFoundError } from '../posts' import { postQueryOptions } from '../postQueryOptions' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: ({ context: { queryClient }, params: { postId } }) => { return queryClient.ensureQueryData(postQueryOptions(postId)) }, errorComponent: PostErrorComponent, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { const router = useRouter() if (error instanceof PostNotFoundError) { return
{error.message}
} const queryErrorResetBoundary = useQueryErrorResetBoundary() React.useEffect(() => { queryErrorResetBoundary.reset() }, [queryErrorResetBoundary]) return (
) } function PostComponent() { const postId = Route.useParams().postId const { data: post } = useSuspenseQuery(postQueryOptions(postId)) return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/routes/posts.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { useSuspenseQuery } from '@tanstack/react-query' import { postsQueryOptions } from '../postsQueryOptions' export const Route = createFileRoute('/posts')({ loader: ({ context: { queryClient } }) => queryClient.ensureQueryData(postsQueryOptions), component: PostsComponent, }) function PostsComponent() { const postsQuery = useSuspenseQuery(postsQueryOptions) const posts = postsQuery.data return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-react-query-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import { getDummyServerPort } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } test.beforeEach(async ({ page }) => { await page.goto('/') }) test('GetPosts', async () => { const port = await getDummyServerPort(packageJson.name) const res = await fetch(`http://localhost:${port}/posts`) expect(res.status).toBe(200) const posts = await res.json() expect(posts.length).toBeGreaterThan(0) const postRes = await fetch(`http://localhost:${port}/posts/1`) expect(postRes.status).toBe(200) const post = await postRes.json() expect(post).toEqual(posts[0]) }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/basic-react-query-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-react-query-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-react-query-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', autoCodeSplitting: true }), react(), ], }) ================================================ FILE: e2e/react-router/basic-scroll-restoration/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-scroll-restoration/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-scroll-restoration/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-scroll-restoration/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-scroll-restoration", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/react-virtual": "^3.13.0", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-scroll-restoration/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_SERVER_PORT=${PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-scroll-restoration/src/main.tsx ================================================ import React, { useLayoutEffect } from 'react' import ReactDOM from 'react-dom/client' import { Link, Outlet, RouterProvider, createRootRoute, createRoute, createRouter, useElementScrollRestoration, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' import { useVirtualizer } from '@tanstack/react-virtual' import './styles.css' const rootRoute = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( <>
Home {' '} About About (No Reset) By-Element
) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', loader: () => new Promise((r) => setTimeout(r, 500)), component: IndexComponent, }) function IndexComponent() { useLayoutEffect(() => { window.invokeOrders.push('index-useLayoutEffect') }, []) return (

Welcome Home!

{Array.from({ length: 50 }).map((_, i) => (
Home Item {i + 1}
))}
) } const aboutRoute = createRoute({ getParentRoute: () => rootRoute, path: '/about', loader: () => new Promise((r) => setTimeout(r, 500)), component: AboutComponent, }) function AboutComponent() { useLayoutEffect(() => { window.invokeOrders.push('about-useLayoutEffect') }, []) return (

Hello from About!

{Array.from({ length: 50 }).map((_, i) => (
About Item {i + 1}
))}
) } const byElementRoute = createRoute({ getParentRoute: () => rootRoute, path: '/by-element', loader: () => new Promise((r) => setTimeout(r, 500)), component: ByElementComponent, }) function ByElementComponent() { // We need a unique ID for manual scroll restoration on a specific element // It should be as unique as possible for this element across your app const scrollRestorationId = 'myVirtualizedContent' // We use that ID to get the scroll entry for this element const scrollEntry = useElementScrollRestoration({ id: scrollRestorationId, }) // Let's use TanStack Virtual to virtualize some content! const virtualizerParentRef = React.useRef(null) const virtualizer = useVirtualizer({ count: 10000, getScrollElement: () => virtualizerParentRef.current, estimateSize: () => 100, // We pass the scrollY from the scroll restoration entry to the virtualizer // as the initial offset initialOffset: scrollEntry?.scrollY, }) return (
Hello from By-Element!
First Regular List Item
{Array.from({ length: 50 }).map((_, i) => (
Regular List Item {i + 1}
))}
{Array.from({ length: 2 }).map((_, i) => (
{Array.from({ length: 50 }).map((_, i) => (
About Item {i + 1}
))}
))}
Virtualized
{virtualizer.getVirtualItems().map((item) => (
Virtualized Item {item.index + 1}
))}
) } const fooRoute = createRoute({ getParentRoute: () => rootRoute, path: '/foo', loader: () => new Promise((r) => setTimeout(r, 500)), component: FooComponent, }) function FooComponent() { return (

Hello from Foo!

Go to Bar
{Array.from({ length: 50 }).map((_, i) => (
Foo Item {i + 1}
))}
) } const barRoute = createRoute({ getParentRoute: () => rootRoute, path: '/bar', loader: () => new Promise((r) => setTimeout(r, 500)), component: BarComponent, }) function BarComponent() { return (

Hello from Bar!

{Array.from({ length: 50 }).map((_, i) => (
Bar Item {i + 1}
))}
) } const routeTree = rootRoute.addChildren([ indexRoute, aboutRoute, byElementRoute, fooRoute, barRoute, ]) const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, getScrollRestorationKey: (location) => location.pathname, }) declare global { interface Window { invokeOrders: Array } } window.invokeOrders = [] router.subscribe('onBeforeRouteMount', (event) => { window.invokeOrders.push(event.type) }) router.subscribe('onResolved', (event) => { window.invokeOrders.push(event.type) }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic-scroll-restoration/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-scroll-restoration/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test('restore scroll positions by page, home pages top message should not display on navigating back', async ({ page, }) => { // Step 1: Navigate to the home page await page.goto('/') await page.waitForURL('/') await expect(page.locator('#greeting')).toContainText('Welcome Home!') await expect(page.locator('#top-message')).toBeInViewport() // Step 2: Scroll to a position that hides the top const targetScrollPosition = 1000 await page.evaluate( (scrollPos: number) => window.scrollTo(0, scrollPos), targetScrollPosition, ) // Verify initial scroll position const scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).toBe(targetScrollPosition) await expect(page.locator('#top-message')).not.toBeInViewport() await page.waitForTimeout(1000) // Step 3: Navigate to the about page await page.getByRole('link', { name: 'About', exact: true }).click() await expect(page.locator('#greeting')).toContainText('Hello from About!') // Step 4: Go back to the home page and immediately check the message await page.goBack() // Wait for the home page to have rendered await page.waitForSelector('#greeting') await page.waitForTimeout(1000) await expect(page.locator('#top-message')).not.toBeInViewport() // Confirm the scroll position was restored correctly const restoredScrollPosition = await page.evaluate(() => window.scrollY) expect(restoredScrollPosition).toBe(targetScrollPosition) }) test('restore scroll positions by element, first regular list item should not display on navigating back', async ({ page, }) => { // Step 1: Navigate to the by-element page await page.goto('/by-element') await page.waitForURL('/by-element') // Step 2: Scroll to a position that hides the first list item in regular list const targetScrollPosition = 1000 await page.waitForSelector('#RegularList') await expect(page.locator('#first-regular-list-item')).toBeInViewport() await page.evaluate( (scrollPos: number) => document.querySelector('#RegularList')!.scrollTo(0, scrollPos), targetScrollPosition, ) // Verify initial scroll position const scrollPosition = await page.evaluate( () => document.querySelector('#RegularList')!.scrollTop, ) expect(scrollPosition).toBe(targetScrollPosition) await expect(page.locator('#first-regular-list-item')).not.toBeInViewport() // Step 3: Navigate to the about page await page.getByRole('link', { name: 'About', exact: true }).click() await expect(page.locator('#greeting')).toContainText('Hello from About!') // Step 4: Go back to the by-element page and immediately check the message await page.goBack() // TODO: For some reason, this only works in headed mode. // When someone can explain that to me, I'll fix this test. // Confirm the scroll position was restored correctly // const restoredScrollPosition = await page.evaluate( // () => document.querySelector('#RegularList')!.scrollTop, // ) // expect(restoredScrollPosition).toBe(targetScrollPosition) }) test('scroll to top when not scrolled, regression test for #4782', async ({ page, }) => { await page.goto('/foo') await page.waitForURL('/foo') await expect(page.getByTestId('foo-route-component')).toBeVisible() let scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).toBe(0) // do not scroll, just navigate to /bar await page.getByTestId('go-to-bar-link').click() await expect(page.getByTestId('bar-route-component')).toBeVisible() const targetScrollPosition = 1000 await page.evaluate( (scrollPos: number) => window.scrollTo(0, scrollPos), targetScrollPosition, ) scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).toBe(targetScrollPosition) // navigate back to /foo await page.goBack() await expect(page.getByTestId('foo-route-component')).toBeVisible() // check if scroll position is restored to 0 since we did not scroll on /foo const restoredScrollPosition = await page.evaluate(() => window.scrollY) expect(restoredScrollPosition).toBe(0) }) ================================================ FILE: e2e/react-router/basic-scroll-restoration/tests/router-events.spec.ts ================================================ import { expect, test } from '@playwright/test' test('after a navigation, should have emitted "onBeforeRouteMount","onResolved" and useLayoutEffect setup in the correct order', async ({ page, }) => { // Navigate to the Home page await page.goto('/') await expect(page.locator('#greeting')).toContainText('Welcome Home!') let orders = await page.evaluate(() => window.invokeOrders) expectItemOrder(orders, 'onBeforeRouteMount', 'onResolved') expectItemOrder(orders, 'onBeforeRouteMount', 'index-useLayoutEffect') // Clear the invokeOrders array orders = await page.evaluate(() => { window.invokeOrders = [] return window.invokeOrders }) // Navigate to the About page await page.getByRole('link', { name: 'About', exact: true }).click() await expect(page.locator('#greeting')).toContainText('Hello from About!') orders = await page.evaluate(() => window.invokeOrders) expectItemOrder(orders, 'onBeforeRouteMount', 'onResolved') expectItemOrder(orders, 'onBeforeRouteMount', 'about-useLayoutEffect') }) function expectItemOrder( array: Array, firstItem: TItem, secondItem: TItem, ) { const firstIndex = array.findIndex((item) => item === firstItem) const secondIndex = array.findIndex((item) => item === secondItem) if (firstIndex === -1 || secondIndex === -1) { throw new Error('One or both items were not found in the array ' + array) } expect(firstIndex).toBeLessThan(secondIndex) } ================================================ FILE: e2e/react-router/basic-scroll-restoration/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-scroll-restoration/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), react()], }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-virtual-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/basic-virtual-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-virtual-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-virtual-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-virtual-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('second', 'layout/second-layout.tsx', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/root' import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' import { Route as bRouteImport } from './routes/b' import { Route as aRouteImport } from './routes/a' const postsPostsRoute = postsPostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) const homeRoute = homeRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => postsPostsRoute, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second', getParentRoute: () => layoutFirstLayoutRoute, } as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ id: '/classic/hello', path: '/classic/hello', getParentRoute: () => rootRouteImport, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ id: '/world', path: '/world', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) const bRoute = bRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => layoutSecondLayoutRoute, } as any) const aRoute = aRouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof homeRoute '/posts': typeof postsPostsRouteWithChildren '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/layout-a': typeof aRoute '/layout-b': typeof bRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute } export interface FileRoutesByTo { '/': typeof homeRoute '/posts': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/layout-a': typeof aRoute '/layout-b': typeof bRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello': typeof ClassicHelloIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof homeRoute '/_first': typeof layoutFirstLayoutRouteWithChildren '/posts': typeof postsPostsRouteWithChildren '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/_first/_second': typeof layoutSecondLayoutRouteWithChildren '/posts/$postId': typeof postsPostsDetailRoute '/_first/_second/layout-a': typeof aRoute '/_first/_second/layout-b': typeof bRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/classic/hello' | '/posts/' | '/posts/$postId' | '/layout-a' | '/layout-b' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' fileRoutesByTo: FileRoutesByTo to: | '/' | '/posts' | '/posts/$postId' | '/layout-a' | '/layout-b' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello' id: | '__root__' | '/' | '/_first' | '/posts' | '/classic/hello' | '/posts/' | '/_first/_second' | '/posts/$postId' | '/_first/_second/layout-a' | '/_first/_second/layout-b' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { homeRoute: typeof homeRoute layoutFirstLayoutRoute: typeof layoutFirstLayoutRouteWithChildren postsPostsRoute: typeof postsPostsRouteWithChildren ClassicHelloRouteRoute: typeof ClassicHelloRouteRouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } '/_first': { id: '/_first' path: '' fullPath: '/' preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof postsPostsDetailRouteImport parentRoute: typeof postsPostsRoute } '/_first/_second': { id: '/_first/_second' path: '' fullPath: '/' preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } '/classic/hello': { id: '/classic/hello' path: '/classic/hello' fullPath: '/classic/hello' preLoaderRoute: typeof ClassicHelloRouteRouteImport parentRoute: typeof rootRouteImport } '/classic/hello/': { id: '/classic/hello/' path: '/' fullPath: '/classic/hello/' preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/world': { id: '/classic/hello/world' path: '/world' fullPath: '/classic/hello/world' preLoaderRoute: typeof ClassicHelloWorldRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' fullPath: '/classic/hello/universe' preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second/layout-b': { id: '/_first/_second/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof bRouteImport parentRoute: typeof layoutSecondLayoutRoute } '/_first/_second/layout-a': { id: '/_first/_second/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } } } declare module './routes/home' { const createFileRoute: CreateFileRoute< '/', FileRoutesByPath['/']['parentRoute'], FileRoutesByPath['/']['id'], FileRoutesByPath['/']['path'], FileRoutesByPath['/']['fullPath'] > } declare module './routes/layout/first-layout' { const createFileRoute: CreateFileRoute< '/_first', FileRoutesByPath['/_first']['parentRoute'], FileRoutesByPath['/_first']['id'], FileRoutesByPath['/_first']['path'], FileRoutesByPath['/_first']['fullPath'] > } declare module './routes/posts/posts' { const createFileRoute: CreateFileRoute< '/posts', FileRoutesByPath['/posts']['parentRoute'], FileRoutesByPath['/posts']['id'], FileRoutesByPath['/posts']['path'], FileRoutesByPath['/posts']['fullPath'] > } declare module './routes/file-based-subtree/hello/route' { const createFileRoute: CreateFileRoute< '/classic/hello', FileRoutesByPath['/classic/hello']['parentRoute'], FileRoutesByPath['/classic/hello']['id'], FileRoutesByPath['/classic/hello']['path'], FileRoutesByPath['/classic/hello']['fullPath'] > } declare module './routes/posts/posts-home' { const createFileRoute: CreateFileRoute< '/posts/', FileRoutesByPath['/posts/']['parentRoute'], FileRoutesByPath['/posts/']['id'], FileRoutesByPath['/posts/']['path'], FileRoutesByPath['/posts/']['fullPath'] > } declare module './routes/layout/second-layout' { const createFileRoute: CreateFileRoute< '/_first/_second', FileRoutesByPath['/_first/_second']['parentRoute'], FileRoutesByPath['/_first/_second']['id'], FileRoutesByPath['/_first/_second']['path'], FileRoutesByPath['/_first/_second']['fullPath'] > } declare module './routes/posts/posts-detail' { const createFileRoute: CreateFileRoute< '/posts/$postId', FileRoutesByPath['/posts/$postId']['parentRoute'], FileRoutesByPath['/posts/$postId']['id'], FileRoutesByPath['/posts/$postId']['path'], FileRoutesByPath['/posts/$postId']['fullPath'] > } declare module './routes/a' { const createFileRoute: CreateFileRoute< '/_first/_second/layout-a', FileRoutesByPath['/_first/_second/layout-a']['parentRoute'], FileRoutesByPath['/_first/_second/layout-a']['id'], FileRoutesByPath['/_first/_second/layout-a']['path'], FileRoutesByPath['/_first/_second/layout-a']['fullPath'] > } declare module './routes/b' { const createFileRoute: CreateFileRoute< '/_first/_second/layout-b', FileRoutesByPath['/_first/_second/layout-b']['parentRoute'], FileRoutesByPath['/_first/_second/layout-b']['id'], FileRoutesByPath['/_first/_second/layout-b']['path'], FileRoutesByPath['/_first/_second/layout-b']['fullPath'] > } declare module './routes/file-based-subtree/hello/universe' { const createFileRoute: CreateFileRoute< '/classic/hello/universe', FileRoutesByPath['/classic/hello/universe']['parentRoute'], FileRoutesByPath['/classic/hello/universe']['id'], FileRoutesByPath['/classic/hello/universe']['path'], FileRoutesByPath['/classic/hello/universe']['fullPath'] > } declare module './routes/file-based-subtree/hello/world' { const createFileRoute: CreateFileRoute< '/classic/hello/world', FileRoutesByPath['/classic/hello/world']['parentRoute'], FileRoutesByPath['/classic/hello/world']['id'], FileRoutesByPath['/classic/hello/world']['path'], FileRoutesByPath['/classic/hello/world']['fullPath'] > } declare module './routes/file-based-subtree/hello/index' { const createFileRoute: CreateFileRoute< '/classic/hello/', FileRoutesByPath['/classic/hello/']['parentRoute'], FileRoutesByPath['/classic/hello/']['id'], FileRoutesByPath['/classic/hello/']['path'], FileRoutesByPath['/classic/hello/']['fullPath'] > } interface layoutSecondLayoutRouteChildren { aRoute: typeof aRoute bRoute: typeof bRoute } const layoutSecondLayoutRouteChildren: layoutSecondLayoutRouteChildren = { aRoute: aRoute, bRoute: bRoute, } const layoutSecondLayoutRouteWithChildren = layoutSecondLayoutRoute._addFileChildren(layoutSecondLayoutRouteChildren) interface layoutFirstLayoutRouteChildren { layoutSecondLayoutRoute: typeof layoutSecondLayoutRouteWithChildren } const layoutFirstLayoutRouteChildren: layoutFirstLayoutRouteChildren = { layoutSecondLayoutRoute: layoutSecondLayoutRouteWithChildren, } const layoutFirstLayoutRouteWithChildren = layoutFirstLayoutRoute._addFileChildren(layoutFirstLayoutRouteChildren) interface postsPostsRouteChildren { postsPostsHomeRoute: typeof postsPostsHomeRoute postsPostsDetailRoute: typeof postsPostsDetailRoute } const postsPostsRouteChildren: postsPostsRouteChildren = { postsPostsHomeRoute: postsPostsHomeRoute, postsPostsDetailRoute: postsPostsDetailRoute, } const postsPostsRouteWithChildren = postsPostsRoute._addFileChildren( postsPostsRouteChildren, ) interface ClassicHelloRouteRouteChildren { ClassicHelloUniverseRoute: typeof ClassicHelloUniverseRoute ClassicHelloWorldRoute: typeof ClassicHelloWorldRoute ClassicHelloIndexRoute: typeof ClassicHelloIndexRoute } const ClassicHelloRouteRouteChildren: ClassicHelloRouteRouteChildren = { ClassicHelloUniverseRoute: ClassicHelloUniverseRoute, ClassicHelloWorldRoute: ClassicHelloWorldRoute, ClassicHelloIndexRoute: ClassicHelloIndexRoute, } const ClassicHelloRouteRouteWithChildren = ClassicHelloRouteRoute._addFileChildren(ClassicHelloRouteRouteChildren) const rootRouteChildren: RootRouteChildren = { homeRoute: homeRoute, layoutFirstLayoutRoute: layoutFirstLayoutRouteWithChildren, postsPostsRoute: postsPostsRouteWithChildren, ClassicHelloRouteRoute: ClassicHelloRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ export const Route = createFileRoute({ component: () =>
This is the index
, }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/layout/first-layout.tsx ================================================ import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/posts/posts-detail.tsx ================================================ import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/posts/posts-home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/posts/posts.tsx ================================================ import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/routes/root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-virtual-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/basic-virtual-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-virtual-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-virtual-file-based/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import { routes } from './routes' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', autoCodeSplitting: true, verboseFileRoutes: false, virtualRouteConfig: routes, }), react(), ], }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ src/routeTree.gen.ts ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-basic-virtual-named-export-config-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('second', 'layout/second-layout.tsx', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ export const Route = createFileRoute({ component: () =>
This is the index
, }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx ================================================ import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx ================================================ import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx ================================================ import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/routes/root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source './**/*.{js,jsx,ts,tsx}'; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.waitForURL('/') await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.waitForURL('/') await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.waitForURL('/') await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/basic-virtual-named-export-config-file-based/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', autoCodeSplitting: true, verboseFileRoutes: false, virtualRouteConfig: './routes.ts', }), react(), ], }) ================================================ FILE: e2e/react-router/escaped-special-strings/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/escaped-special-strings/.gitignore ================================================ node_modules dist port*.txt test-results playwright-report ================================================ FILE: e2e/react-router/escaped-special-strings/index.html ================================================ Escaped Special Strings E2E Test
================================================ FILE: e2e/react-router/escaped-special-strings/package.json ================================================ { "name": "tanstack-router-e2e-react-escaped-special-strings", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "pnpm run test:e2e:default", "test:e2e:default": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/router-plugin": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/escaped-special-strings/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` const command = `pnpm build && pnpm preview --port ${PORT}` console.info('Running with mode: ', process.env.MODE || 'default') /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', env: { MODE: process.env.MODE || '', VITE_MODE: process.env.MODE || '', VITE_NODE_ENV: 'test', VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), VITE_SERVER_PORT: String(PORT), PORT: String(PORT), }, }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/escaped-special-strings/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as Char91indexChar93RouteImport } from './routes/[index]' import { Route as Char91routeChar93RouteImport } from './routes/[route]' import { Route as Char91lazyChar93RouteImport } from './routes/[lazy]' import { Route as BlogRouteImport } from './routes/blog[_]' import { Route as LayoutRouteImport } from './routes/[_]layout' import { Route as IndexRouteImport } from './routes/index' const Char91indexChar93Route = Char91indexChar93RouteImport.update({ id: '/index', path: '/index', getParentRoute: () => rootRouteImport, } as any) const Char91routeChar93Route = Char91routeChar93RouteImport.update({ id: '/route', path: '/route', getParentRoute: () => rootRouteImport, } as any) const Char91lazyChar93Route = Char91lazyChar93RouteImport.update({ id: '/lazy', path: '/lazy', getParentRoute: () => rootRouteImport, } as any) const BlogRoute = BlogRouteImport.update({ id: '/blog_', path: '/blog_', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', path: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/_layout': typeof LayoutRoute '/blog_': typeof BlogRoute '/index': typeof Char91indexChar93Route '/lazy': typeof Char91lazyChar93Route '/route': typeof Char91routeChar93Route } export interface FileRoutesByTo { '/': typeof IndexRoute '/_layout': typeof LayoutRoute '/blog_': typeof BlogRoute '/index': typeof Char91indexChar93Route '/lazy': typeof Char91lazyChar93Route '/route': typeof Char91routeChar93Route } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRoute '/blog_': typeof BlogRoute '/index': typeof Char91indexChar93Route '/lazy': typeof Char91lazyChar93Route '/route': typeof Char91routeChar93Route } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/_layout' | '/blog_' | '/index' | '/lazy' | '/route' fileRoutesByTo: FileRoutesByTo to: '/' | '/_layout' | '/blog_' | '/index' | '/lazy' | '/route' id: '__root__' | '/' | '/_layout' | '/blog_' | '/index' | '/lazy' | '/route' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRoute BlogRoute: typeof BlogRoute Char91indexChar93Route: typeof Char91indexChar93Route Char91lazyChar93Route: typeof Char91lazyChar93Route Char91routeChar93Route: typeof Char91routeChar93Route } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/index': { id: '/index' path: '/index' fullPath: '/index' preLoaderRoute: typeof Char91indexChar93RouteImport parentRoute: typeof rootRouteImport } '/route': { id: '/route' path: '/route' fullPath: '/route' preLoaderRoute: typeof Char91routeChar93RouteImport parentRoute: typeof rootRouteImport } '/lazy': { id: '/lazy' path: '/lazy' fullPath: '/lazy' preLoaderRoute: typeof Char91lazyChar93RouteImport parentRoute: typeof rootRouteImport } '/blog_': { id: '/blog_' path: '/blog_' fullPath: '/blog_' preLoaderRoute: typeof BlogRouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '/_layout' fullPath: '/_layout' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRoute, BlogRoute: BlogRoute, Char91indexChar93Route: Char91indexChar93Route, Char91lazyChar93Route: Char91lazyChar93Route, Char91routeChar93Route: Char91routeChar93Route, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/[_]layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // This file uses [_]layout escaping to create a literal /_layout path // instead of being treated as a pathless layout route export const Route = createFileRoute('/_layout')({ component: EscapedUnderscoreLayoutComponent, }) function EscapedUnderscoreLayoutComponent() { return (

Escaped Underscore Layout Page

/_layout

This route was created using [_]layout.tsx to escape the leading underscore. It renders at the literal path /_layout instead of being a pathless layout.

) } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/[index].tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // This file uses [index] escaping to create a literal /index route // instead of being treated as an index route for the parent export const Route = createFileRoute('/index')({ component: EscapedIndexComponent, }) function EscapedIndexComponent() { return (

Escaped Index Page

/index

This route was created using [index].tsx to escape the special "index" token. It renders at the literal path /index instead of being the index route.

) } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/[lazy].tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // This file uses [lazy] escaping to create a literal /lazy path // instead of being treated as a lazy-loaded route export const Route = createFileRoute('/lazy')({ component: EscapedLazyComponent, }) function EscapedLazyComponent() { return (

Escaped Lazy Page

/lazy

This route was created using [lazy].tsx to escape the special "lazy" token. It renders at the literal path /lazy instead of being a lazy-loaded route.

) } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/[route].tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // This file uses [route] escaping to create a literal /route path // instead of being treated as a layout configuration file export const Route = createFileRoute('/route')({ component: EscapedRouteComponent, }) function EscapedRouteComponent() { return (

Escaped Route Page

/route

This route was created using [route].tsx to escape the special "route" token. It renders at the literal path /route instead of being a layout configuration.

) } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/__root.tsx ================================================ import { Link, Outlet, createRootRoute } from '@tanstack/react-router' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

Page not found

Go to /index
) }, }) function RootComponent() { return (

Escaped Special Strings Test

) } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/blog[_].tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // This file uses blog[_] escaping to create a literal /blog_ path // with the trailing underscore preserved in the URL export const Route = createFileRoute('/blog_')({ component: EscapedBlogUnderscoreComponent, }) function EscapedBlogUnderscoreComponent() { return (

Escaped Blog Underscore Page

/blog_

This route was created using blog[_].tsx to escape the trailing underscore. It renders at the literal path /blog_ with the underscore preserved in the URL.

) } ================================================ FILE: e2e/react-router/escaped-special-strings/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' // This is the standard index route for the root path / // It coexists with [index].tsx which creates a literal /index route export const Route = createFileRoute('/')({ component: HomeComponent, }) function HomeComponent() { return (

Home Page

/

This is the home/index route at /. It coexists with the escaped [index] route at /index.

) } ================================================ FILE: e2e/react-router/escaped-special-strings/tests/escaped-routes.spec.ts ================================================ import { expect, test } from '@playwright/test' test.describe('Escaped special strings routing', () => { test('home index route renders at / path', async ({ page }) => { await page.goto('/') await expect(page.getByTestId('page-title')).toContainText('Home Page') await expect(page.getByTestId('page-path')).toContainText('/') await expect(page.getByTestId('page-description')).toContainText( 'home/index route at /', ) }) test('escaped [index] route renders at /index path', async ({ page }) => { await page.goto('/index') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Index Page', ) await expect(page.getByTestId('page-path')).toContainText('/index') await expect(page.getByTestId('page-description')).toContainText( 'escape the special "index" token', ) }) test('escaped [route] route renders at /route path', async ({ page }) => { await page.goto('/route') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Route Page', ) await expect(page.getByTestId('page-path')).toContainText('/route') await expect(page.getByTestId('page-description')).toContainText( 'escape the special "route" token', ) }) test('escaped [lazy] route renders at /lazy path', async ({ page }) => { await page.goto('/lazy') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Lazy Page', ) await expect(page.getByTestId('page-path')).toContainText('/lazy') await expect(page.getByTestId('page-description')).toContainText( 'escape the special "lazy" token', ) }) test('escaped [_]layout route renders at /_layout path', async ({ page }) => { await page.goto('/_layout') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Underscore Layout Page', ) await expect(page.getByTestId('page-path')).toContainText('/_layout') await expect(page.getByTestId('page-description')).toContainText( 'escape the leading underscore', ) }) test('escaped blog[_] route renders at /blog_ path', async ({ page }) => { await page.goto('/blog_') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Blog Underscore Page', ) await expect(page.getByTestId('page-path')).toContainText('/blog_') await expect(page.getByTestId('page-description')).toContainText( 'escape the trailing underscore', ) }) test('client-side navigation to home / route', async ({ page }) => { await page.goto('/index') await page.getByTestId('link-home').click() await page.waitForURL('/') await expect(page.getByTestId('page-title')).toContainText('Home Page') }) test('client-side navigation to escaped /index route', async ({ page }) => { await page.goto('/route') await page.getByTestId('link-index').click() await page.waitForURL('/index') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Index Page', ) }) test('client-side navigation to escaped /route route', async ({ page }) => { await page.goto('/index') await page.getByTestId('link-route').click() await page.waitForURL('/route') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Route Page', ) }) test('client-side navigation to escaped /lazy route', async ({ page }) => { await page.goto('/index') await page.getByTestId('link-lazy').click() await page.waitForURL('/lazy') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Lazy Page', ) }) test('client-side navigation to escaped /_layout route', async ({ page }) => { await page.goto('/index') await page.getByTestId('link-underscore-layout').click() await page.waitForURL('/_layout') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Underscore Layout Page', ) }) test('client-side navigation to escaped /blog_ route', async ({ page }) => { await page.goto('/index') await page.getByTestId('link-blog-underscore').click() await page.waitForURL('/blog_') await expect(page.getByTestId('page-title')).toContainText( 'Escaped Blog Underscore Page', ) }) test('URL is correct for escaped routes with underscores', async ({ page, baseURL, }) => { await page.goto('/_layout') expect(page.url()).toBe(`${baseURL}/_layout`) await page.goto('/blog_') expect(page.url()).toBe(`${baseURL}/blog_`) }) }) ================================================ FILE: e2e/react-router/escaped-special-strings/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/escaped-special-strings/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/escaped-special-strings/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/escaped-special-strings/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tanstackRouter({ target: 'react', }), react(), ], }) ================================================ FILE: e2e/react-router/generator-cli-only/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/generator-cli-only/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/generator-cli-only/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/generator-cli-only/package.json ================================================ { "name": "tanstack-router-e2e-react-generator-cli-only", "private": true, "type": "module", "scripts": { "dev": "tsr generate && vite --port 3000", "dev:e2e": "tsr generate && vite", "build": "tsr generate && vite build && tsc --noEmit", "preview": "vite preview", "start": "tsr generate && vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-cli": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/generator-cli-only/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/generator-cli-only/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( , ) } ================================================ FILE: e2e/react-router/generator-cli-only/src/posts.ts ================================================ import axios from 'redaxios' export class NotFoundError extends Error {} type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } ================================================ FILE: e2e/react-router/generator-cli-only/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRouteRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutRouteImport.update({ id: '/_nested-layout', getParentRoute: () => PathlessLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/posts': typeof PostsRouteRouteWithChildren '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/posts/$postId' | '/posts/' | '/route-a' | '/route-b' fileRoutesByTo: FileRoutesByTo to: '/' | '/posts/$postId' | '/posts' | '/route-a' | '/route-b' id: | '__root__' | '/' | '/posts' | '/_pathlessLayout' | '/_pathlessLayout/_nested-layout' | '/posts/$postId' | '/posts/' | '/_pathlessLayout/_nested-layout/route-a' | '/_pathlessLayout/_nested-layout/route-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute PostsRouteRoute: typeof PostsRouteRouteWithChildren PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/_pathlessLayout': { id: '/_pathlessLayout' path: '' fullPath: '/' preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRouteRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } '/_pathlessLayout/_nested-layout': { id: '/_pathlessLayout/_nested-layout' path: '' fullPath: '/' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport parentRoute: typeof PathlessLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' path: '/route-b' fullPath: '/route-b' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-a': { id: '/_pathlessLayout/_nested-layout/route-a' path: '/route-a' fullPath: '/route-a' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } } } interface PostsRouteRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteRouteChildren: PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) interface PathlessLayoutNestedLayoutRouteChildren { PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute } const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = { PathlessLayoutNestedLayoutRouteARoute: PathlessLayoutNestedLayoutRouteARoute, PathlessLayoutNestedLayoutRouteBRoute: PathlessLayoutNestedLayoutRouteBRoute, } const PathlessLayoutNestedLayoutRouteWithChildren = PathlessLayoutNestedLayoutRoute._addFileChildren( PathlessLayoutNestedLayoutRouteChildren, ) interface PathlessLayoutRouteChildren { PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren } const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, } const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( PathlessLayoutRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, PostsRouteRoute: PostsRouteRouteWithChildren, PathlessLayoutRoute: PathlessLayoutRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Pathless Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( { component: LayoutAComponent, }, ) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( { component: LayoutBComponent, }, ) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested pathless layout
Go to route A Go to route B
) } ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/_pathlessLayout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a pathless layout
) } ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/generator-cli-only/src/routes/posts.route.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsLayoutComponent, }) function PostsLayoutComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/generator-cli-only/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/generator-cli-only/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested pathless layouts', async ({ page }) => { await page.getByRole('link', { name: 'Pathless Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a pathless layout") await expect(page.locator('#app')).toContainText( "I'm a nested pathless layout", ) await page.getByRole('link', { name: 'Go to route A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Go to route B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/generator-cli-only/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/generator-cli-only/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/generator-cli-only/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "resolveJsonModule": true, "allowJs": true, "skipLibCheck": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/generator-cli-only/tsr.config.json ================================================ { "routesDirectory": "./src/routes", "generatedRouteTree": "./src/routeTree.gen.ts" } ================================================ FILE: e2e/react-router/generator-cli-only/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), react()], }) ================================================ FILE: e2e/react-router/i18n-paraglide/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/i18n-paraglide/.gitignore ================================================ node_modules dist src/routeTree.gen.ts src/paraglide *.local port*.txt test-results ================================================ FILE: e2e/react-router/i18n-paraglide/index.html ================================================ TanStack Router i18n-paraglide e2e
================================================ FILE: e2e/react-router/i18n-paraglide/messages/de.json ================================================ { "$schema": "https://inlang.com/schema/inlang-message-format", "example_message": "Guten Tag {username}", "hello_about": "Hallo /ueber!", "home_page": "Startseite", "about_page": "Über uns" } ================================================ FILE: e2e/react-router/i18n-paraglide/messages/en.json ================================================ { "$schema": "https://inlang.com/schema/inlang-message-format", "example_message": "Hello world {username}", "hello_about": "Hello /about!", "home_page": "Home page", "about_page": "About page" } ================================================ FILE: e2e/react-router/i18n-paraglide/package.json ================================================ { "name": "tanstack-router-e2e-react-i18n-paraglide", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwindcss": "^4.2.2" }, "devDependencies": { "@inlang/paraglide-js": "^2.4.0", "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "typescript": "^5.7.2", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/i18n-paraglide/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_SERVER_PORT=${PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/i18n-paraglide/project.inlang/.gitignore ================================================ cache ================================================ FILE: e2e/react-router/i18n-paraglide/project.inlang/project_id ================================================ apLjNFHNH2yAHYTN5d ================================================ FILE: e2e/react-router/i18n-paraglide/project.inlang/settings.json ================================================ { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", "locales": ["en", "de"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" ], "plugin.inlang.messageFormat": { "pathPattern": "./messages/{locale}.json" } } ================================================ FILE: e2e/react-router/i18n-paraglide/src/main.tsx ================================================ import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import './styles.css' // Import the generated route tree import { routeTree } from './routeTree.gen' import { deLocalizeUrl, localizeUrl } from './paraglide/runtime.js' // Create a new router instance const router = createRouter({ routeTree, context: {}, defaultPreload: 'intent', scrollRestoration: true, defaultStructuralSharing: true, defaultPreloadStaleTime: 0, rewrite: { input: ({ url }) => deLocalizeUrl(url), output: ({ url }) => localizeUrl(url), }, }) // Register the router instance for type safety declare module '@tanstack/react-router' { interface Register { router: typeof router } } // Render the app const rootElement = document.getElementById('app') if (rootElement && !rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( , ) } ================================================ FILE: e2e/react-router/i18n-paraglide/src/routes/__root.tsx ================================================ import { Link, Outlet, createRootRoute, redirect } from '@tanstack/react-router' import { getLocale, locales, setLocale, shouldRedirect, } from '@/paraglide/runtime' import { m } from '@/paraglide/messages' export const Route = createRootRoute({ beforeLoad: async () => { document.documentElement.setAttribute('lang', getLocale()) const decision = await shouldRedirect({ url: window.location.href }) if (decision.redirectUrl) { throw redirect({ href: decision.redirectUrl.href }) } }, component: () => ( <>
{m.home_page()} {m.about_page()}
{locales.map((locale) => ( ))}

), }) ================================================ FILE: e2e/react-router/i18n-paraglide/src/routes/about.tsx ================================================ import { m } from '@/paraglide/messages' import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/about')({ component: RouteComponent, }) function RouteComponent() { return
{m.hello_about()}
} ================================================ FILE: e2e/react-router/i18n-paraglide/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { m } from '@/paraglide/messages' export const Route = createFileRoute('/')({ component: App, }) function App() { return (

{m.example_message({ username: 'TanStack Router!', })}

) } ================================================ FILE: e2e/react-router/i18n-paraglide/src/styles.css ================================================ @import 'tailwindcss' source('../'); ================================================ FILE: e2e/react-router/i18n-paraglide/tests/navigation.spec.ts ================================================ import { expect, test } from '@playwright/test' // These tests verify that client-side i18n rewrites work correctly in a pure SPA // (no server-side rendering). The router uses the rewrite API to: // - input: de-localize URLs for route matching (e.g., /de/ueber -> /about) // - output: localize URLs for display (e.g., /about -> /de/ueber) test.describe('Client-side i18n navigation', () => { test('should load the home page without redirect loops', async ({ page }) => { // Navigate to root - should work without any redirect loop issues await page.goto('/') await page.waitForLoadState('networkidle') // Verify the page loaded successfully await expect(page.getByTestId('home-content')).toBeVisible() await expect(page.getByTestId('home-content')).toContainText('Hello world') }) test('should load the German home page (/de)', async ({ page }) => { await page.goto('/de') await page.waitForLoadState('networkidle') // Verify we're on the German page await expect(page.getByTestId('home-content')).toBeVisible() await expect(page.getByTestId('home-content')).toContainText('Guten Tag') // URL should remain /de expect(page.url()).toContain('/de') }) test('should navigate to about page and update URL correctly', async ({ page, }) => { await page.goto('/') await page.waitForLoadState('networkidle') // Click on about link await page.getByTestId('about-link').click() await page.waitForLoadState('networkidle') // Verify we're on the about page await expect(page.getByTestId('about-content')).toBeVisible() await expect(page.getByTestId('about-content')).toContainText( 'Hello /about!', ) // URL should be /about (English) expect(page.url()).toContain('/about') }) test('should navigate to German about page with translated URL', async ({ page, }) => { await page.goto('/de') await page.waitForLoadState('networkidle') // Click on about link (should navigate to /de/ueber) await page.getByTestId('about-link').click() await page.waitForLoadState('networkidle') // Verify we're on the about page with German content await expect(page.getByTestId('about-content')).toBeVisible() await expect(page.getByTestId('about-content')).toContainText( 'Hallo /ueber!', ) // URL should be /de/ueber (German translated path) expect(page.url()).toContain('/de/ueber') }) test('should directly access German about page (/de/ueber)', async ({ page, }) => { // Direct navigation to translated URL await page.goto('/de/ueber') await page.waitForLoadState('networkidle') // Verify the page loaded correctly await expect(page.getByTestId('about-content')).toBeVisible() await expect(page.getByTestId('about-content')).toContainText( 'Hallo /ueber!', ) // URL should stay /de/ueber expect(page.url()).toContain('/de/ueber') }) test('should switch locale and update URLs accordingly', async ({ page }) => { // Start at English home page await page.goto('/') await page.waitForLoadState('networkidle') // Verify English content await expect(page.getByTestId('home-content')).toContainText('Hello world') // Switch to German await page.getByTestId('locale-de').click() await page.waitForLoadState('networkidle') // Verify German content await expect(page.getByTestId('home-content')).toContainText('Guten Tag') // URL should now include /de expect(page.url()).toContain('/de') }) test('should switch locale on about page and update translated URL', async ({ page, }) => { // Start at English about page await page.goto('/about') await page.waitForLoadState('networkidle') // Verify English content await expect(page.getByTestId('about-content')).toContainText( 'Hello /about!', ) // Switch to German await page.getByTestId('locale-de').click() await page.waitForLoadState('networkidle') // Verify German content await expect(page.getByTestId('about-content')).toContainText( 'Hallo /ueber!', ) // URL should now be /de/ueber (translated path) expect(page.url()).toContain('/de/ueber') }) test('should maintain correct links after locale switch', async ({ page, }) => { // Start at German home page await page.goto('/de') await page.waitForLoadState('networkidle') // Verify about link has German translated href const aboutLink = page.getByTestId('about-link') await expect(aboutLink).toHaveAttribute('href', '/de/ueber') // Switch to English await page.getByTestId('locale-en').click() await page.waitForLoadState('networkidle') // Verify about link now has English href await expect(aboutLink).toHaveAttribute('href', '/about') }) }) test.describe('Client-side navigation without redirect loops', () => { test('navigating back and forth should not cause issues', async ({ page, }) => { await page.goto('/') await page.waitForLoadState('networkidle') // Navigate to about await page.getByTestId('about-link').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('about-content')).toBeVisible() // Navigate back to home await page.getByTestId('home-link').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('home-content')).toBeVisible() // Navigate to about again await page.getByTestId('about-link').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('about-content')).toBeVisible() }) test('switching locales multiple times should work correctly', async ({ page, }) => { await page.goto('/') await page.waitForLoadState('networkidle') // Switch to German await page.getByTestId('locale-de').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('home-content')).toContainText('Guten Tag') // Switch back to English await page.getByTestId('locale-en').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('home-content')).toContainText('Hello world') // Switch to German again await page.getByTestId('locale-de').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('home-content')).toContainText('Guten Tag') }) test('browser back/forward should work with locale changes', async ({ page, }) => { await page.goto('/') await page.waitForLoadState('networkidle') // Navigate to about await page.getByTestId('about-link').click() await page.waitForLoadState('networkidle') // Switch to German await page.getByTestId('locale-de').click() await page.waitForLoadState('networkidle') expect(page.url()).toContain('/de/ueber') // Go back await page.goBack() await page.waitForLoadState('networkidle') // Should be on about page (English or previous state) await expect(page.getByTestId('about-content')).toBeVisible() }) }) test.describe('URL rewrite consistency', () => { test('internal navigation should use rewritten URLs in links', async ({ page, }) => { await page.goto('/de') await page.waitForLoadState('networkidle') // Check that links are properly localized const homeLink = page.getByTestId('home-link') const aboutLink = page.getByTestId('about-link') await expect(homeLink).toHaveAttribute('href', '/de') await expect(aboutLink).toHaveAttribute('href', '/de/ueber') }) test('English links should not have locale prefix', async ({ page }) => { await page.goto('/') await page.waitForLoadState('networkidle') const homeLink = page.getByTestId('home-link') const aboutLink = page.getByTestId('about-link') await expect(homeLink).toHaveAttribute('href', '/') await expect(aboutLink).toHaveAttribute('href', '/about') }) }) ================================================ FILE: e2e/react-router/i18n-paraglide/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "noImplicitAny": false, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, "allowJs": true, "paths": { "@/*": ["./src/*"] } }, "include": ["src", "tests"] } ================================================ FILE: e2e/react-router/i18n-paraglide/vite.config.ts ================================================ import { defineConfig } from 'vite' import viteReact from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import { resolve } from 'node:path' import { paraglideVitePlugin } from '@inlang/paraglide-js' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ tailwindcss(), paraglideVitePlugin({ project: './project.inlang', outdir: './src/paraglide', outputStructure: 'message-modules', cookieName: 'PARAGLIDE_LOCALE', strategy: ['url', 'cookie', 'preferredLanguage', 'baseLocale'], urlPatterns: [ { pattern: '/', localized: [ ['en', '/'], ['de', '/de'], ], }, { pattern: '/about', localized: [ ['en', '/about'], ['de', '/de/ueber'], ], }, { pattern: '/:path(.*)?', localized: [ ['en', '/:path(.*)?'], ['de', '/de/:path(.*)?'], ], }, ], }), tanstackRouter({ autoCodeSplitting: true }), viteReact(), ], resolve: { alias: { '@': resolve(__dirname, './src'), }, }, }) ================================================ FILE: e2e/react-router/js-only-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/js-only-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/js-only-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/js-only-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-js-only-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/js-only-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/js-only-file-based/src/main.jsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) const rootElement = document.getElementById('app') if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( , ) } ================================================ FILE: e2e/react-router/js-only-file-based/src/posts.js ================================================ import axios from 'redaxios' export class NotFoundError extends Error {} let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPosts = async () => { console.info('Fetching posts...') return axios.get(`${queryURL}/posts`).then((r) => r.data.slice(0, 10)) } export const fetchPost = async (postId) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } ================================================ FILE: e2e/react-router/js-only-file-based/src/routeTree.gen.js ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, }) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, }) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, }) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRouteRoute, }) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRouteRoute, }) const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutRouteImport.update({ id: '/_nested-layout', getParentRoute: () => PathlessLayoutRoute, }) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) const PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) const PathlessLayoutNestedLayoutRouteChildren = { PathlessLayoutNestedLayoutRouteARoute: PathlessLayoutNestedLayoutRouteARoute, PathlessLayoutNestedLayoutRouteBRoute: PathlessLayoutNestedLayoutRouteBRoute, } const PathlessLayoutNestedLayoutRouteWithChildren = PathlessLayoutNestedLayoutRoute._addFileChildren( PathlessLayoutNestedLayoutRouteChildren, ) const PathlessLayoutRouteChildren = { PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, } const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( PathlessLayoutRouteChildren, ) const rootRouteChildren = { IndexRoute: IndexRoute, PostsRouteRoute: PostsRouteRouteWithChildren, PathlessLayoutRoute: PathlessLayoutRouteWithChildren, } export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren) ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/__root.jsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Pathless Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/_pathlessLayout/_nested-layout/route-a.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( { component: LayoutAComponent, }, ) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/_pathlessLayout/_nested-layout/route-b.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( { component: LayoutBComponent, }, ) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/_pathlessLayout/_nested-layout.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested pathless layout
Go to route A Go to route B
) } ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/_pathlessLayout.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_pathlessLayout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a pathless layout
) } ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/index.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/posts.$postId.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../posts' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/posts.index.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/js-only-file-based/src/routes/posts.route.jsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsLayoutComponent, }) function PostsLayoutComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/js-only-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/js-only-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested pathless layouts', async ({ page }) => { await page.getByRole('link', { name: 'Pathless Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a pathless layout") await expect(page.locator('#app')).toContainText( "I'm a nested pathless layout", ) await page.getByRole('link', { name: 'Go to route A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Go to route B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/js-only-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/js-only-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/js-only-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "resolveJsonModule": true, "allowJs": true, "skipLibCheck": true }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/js-only-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', disableTypes: true, }), react(), ], }) ================================================ FILE: e2e/react-router/rspack-basic-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/rspack-basic-file-based/.gitignore ================================================ # Local .DS_Store *.local *.log* # Dist node_modules dist/ dist-hash/ # IDE .vscode/* !.vscode/extensions.json .idea # E2E src/routeTree.gen.ts test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/rspack-basic-file-based/README.md ================================================ # Example To run this example: - `pnpm install` - `pnpm dev` ================================================ FILE: e2e/react-router/rspack-basic-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-rspack-basic-file-based", "private": true, "type": "module", "scripts": { "dev": "rsbuild dev --port 3000", "build": "rsbuild build && tsc --noEmit", "preview": "rsbuild preview", "start": "rsbuild preview", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" }, "devDependencies": { "@playwright/test": "^1.50.1", "@rsbuild/core": "^1.2.4", "@rsbuild/plugin-react": "^1.1.0", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "postcss": "^8.5.1", "tailwindcss": "^4.2.2", "typescript": "^5.7.2" } } ================================================ FILE: e2e/react-router/rspack-basic-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `PUBLIC_NODE_ENV="test" PUBLIC_EXTERNAL_PORT=${EXTERNAL_PORT} PUBLIC_SERVER_PORT=${PORT} pnpm build && PUBLIC_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/rspack-basic-file-based/postcss.config.mjs ================================================ export default { plugins: { '@tailwindcss/postcss': {}, }, } ================================================ FILE: e2e/react-router/rspack-basic-file-based/rsbuild.config.ts ================================================ import { defineConfig } from '@rsbuild/core' import { pluginReact } from '@rsbuild/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/rspack' export default defineConfig({ plugins: [pluginReact()], tools: { rspack: { plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true, }), ], }, }, }) ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/app.tsx ================================================ import * as React from 'react' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const App = () => { return } export default App ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/env.d.ts ================================================ /// ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/index.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import App from './app' const rootEl = document.getElementById('root') if (rootEl) { const root = ReactDOM.createRoot(rootEl) root.render( , ) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.PUBLIC_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.PUBLIC_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/_layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/routes/posts.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source './**/*.{js,jsx,ts,tsx}'; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/rspack-basic-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#root')).toContainText("I'm a layout") await expect(page.locator('#root')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#root')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#root')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/rspack-basic-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/rspack-basic-file-based/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2020", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "module": "ESNext", "jsx": "react-jsx", "strict": true, "skipLibCheck": true, "isolatedModules": true, "resolveJsonModule": true, "useDefineForClassFields": true, "allowJs": true }, "include": ["src", "playwright.config.ts", "tests"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/.gitignore ================================================ # Local .DS_Store *.local *.log* # Dist node_modules dist/ dist-hash/ # IDE .vscode/* !.vscode/extensions.json .idea # E2E src/routeTree.gen.ts test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/README.md ================================================ # Example To run this example: - `pnpm install` - `pnpm dev` ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json ================================================ { "name": "tanstack-router-e2e-react-rspack-basic-virtual-named-export-config-file-based", "private": true, "type": "module", "scripts": { "dev": "rsbuild dev --port 3000", "build": "rsbuild build && tsc --noEmit", "preview": "rsbuild preview", "start": "rsbuild preview", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" }, "devDependencies": { "@playwright/test": "^1.50.1", "@rsbuild/core": "^1.2.4", "@rsbuild/plugin-react": "^1.1.0", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "postcss": "^8.5.1", "tailwindcss": "^4.2.2", "typescript": "^5.7.2" } } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `PUBLIC_NODE_ENV="test" PUBLIC_EXTERNAL_PORT=${EXTERNAL_PORT} PUBLIC_SERVER_PORT=${PORT} pnpm build && PUBLIC_SERVER_PORT=${PORT} pnpm start --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/postcss.config.mjs ================================================ export default { plugins: { '@tailwindcss/postcss': {}, }, } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('second', 'layout/second-layout.tsx', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/rsbuild.config.ts ================================================ import { defineConfig } from '@rsbuild/core' import { pluginReact } from '@rsbuild/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/rspack' export default defineConfig({ plugins: [pluginReact()], tools: { rspack: { plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true, virtualRouteConfig: './routes.ts', }), ], }, }, }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/app.tsx ================================================ import * as React from 'react' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const App = () => { return } export default App ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/env.d.ts ================================================ /// ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/index.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import App from './app' const rootEl = document.getElementById('root') if (rootEl) { const root = ReactDOM.createRoot(rootEl) root.render( , ) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.PUBLIC_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.PUBLIC_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/a.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_first/_second/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/b.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_first/_second/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/classic/hello/')({ component: () =>
This is the index
, }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/classic/hello')({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/classic/hello/universe')({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/classic/hello/world')({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/home.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_first')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute('/_first/_second')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source './**/*.{js,jsx,ts,tsx}'; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.waitForURL('/') await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.waitForURL('/') await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#root')).toContainText("I'm a layout") await expect(page.locator('#root')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#root')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#root')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.waitForURL('/') await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2020", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "module": "ESNext", "jsx": "react-jsx", "strict": true, "skipLibCheck": true, "isolatedModules": true, "resolveJsonModule": true, "useDefineForClassFields": true, "allowJs": true }, "include": ["src", "playwright.config.ts", "tests", "./routes.ts"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsr.config.json ================================================ { "routesDirectory": "./src/routes", "generatedRouteTree": "./src/routeTree.gen.ts", "virtualRouteConfig": "./routes.ts" } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/.gitignore ================================================ node_modules .DS_Store dist dist-ssr dist-hash *.local test-results ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/README.md ================================================ # Scroll Restoration Testing Sandbox with Vite To run this example: - `npm install` - `npm start` This sandbox is for testing the scroll restoration behavior. ## Setup - Create your files in `src/routes` directory. - Make sure you update the arrays in the following files with the expected routes - `tests/app.spec.ts` > routes array - `src/routes/__root.tsx` > Nav component, routes array - `src/routes/index.tsx` > Navigation test suite, routes array ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/index.html ================================================ Vite App
================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/package.json ================================================ { "name": "tanstack-router-e2e-react-scroll-restoration-sandbox-vite", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:hash": "VITE_APP_HISTORY=hash vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e:browser": "VITE_APP_HISTORY=browser playwright test --config=playwright.browser.config.ts --project=chromium", "test:e2e:hash": "VITE_APP_HISTORY=hash playwright test --config=playwright.hash.config.ts --project=chromium", "test:e2e": "rm -rf port*.txt; pnpm run test:e2e:browser && pnpm run test:e2e:hash" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "workspace:^", "@tanstack/react-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.0" } } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/playwright.browser.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, resolveRuntimeSuffix, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort( packageJson.name + `-${resolveRuntimeSuffix('browser')}`, ) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=browser pnpm build && VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=browser pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], quiet: false, }) ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/playwright.hash.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, resolveRuntimeSuffix, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort( packageJson.name + `-${resolveRuntimeSuffix('hash')}`, ) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=hash vite build --outDir dist-hash && VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=hash pnpm preview --port ${PORT} --outDir dist-hash`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createHashHistory, createRouter, } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import type { RouterHistory } from '@tanstack/react-router' import './styles.css' let history: RouterHistory | undefined if (import.meta.env.VITE_APP_HISTORY === 'hash') { history = createHashHistory() } // Set up a Router instance const router = createRouter({ routeTree, history, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/posts.tsx ================================================ import axios from 'redaxios' export function sleep(ms: number) { return new Promise((r) => setTimeout(r, ms)) } export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export class PostNotFoundError extends Error {} export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw new PostNotFoundError(`Post with id "${postId}" not found!`) } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { createFileRoute } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' import { Route as testsLazyPageRouteImport } from './routes/(tests)/lazy-page' const testsVirtualPageLazyRouteImport = createFileRoute( '/(tests)/virtual-page', )() const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport .update({ id: '/(tests)/virtual-page', path: '/virtual-page', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ id: '/(tests)/page-with-search', path: '/page-with-search', getParentRoute: () => rootRouteImport, } as any) const testsNormalPageRoute = testsNormalPageRouteImport.update({ id: '/(tests)/normal-page', path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .update({ id: '/(tests)/lazy-with-loader-page', path: '/lazy-with-loader-page', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(tests)/lazy-with-loader-page.lazy').then((d) => d.Route), ) const testsLazyPageRoute = testsLazyPageRouteImport .update({ id: '/(tests)/lazy-page', path: '/lazy-page', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/lazy-page': typeof testsLazyPageRoute '/lazy-with-loader-page': typeof testsLazyWithLoaderPageRoute '/normal-page': typeof testsNormalPageRoute '/page-with-search': typeof testsPageWithSearchRoute '/virtual-page': typeof testsVirtualPageLazyRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/lazy-page': typeof testsLazyPageRoute '/lazy-with-loader-page': typeof testsLazyWithLoaderPageRoute '/normal-page': typeof testsNormalPageRoute '/page-with-search': typeof testsPageWithSearchRoute '/virtual-page': typeof testsVirtualPageLazyRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/(tests)/lazy-page': typeof testsLazyPageRoute '/(tests)/lazy-with-loader-page': typeof testsLazyWithLoaderPageRoute '/(tests)/normal-page': typeof testsNormalPageRoute '/(tests)/page-with-search': typeof testsPageWithSearchRoute '/(tests)/virtual-page': typeof testsVirtualPageLazyRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/lazy-page' | '/lazy-with-loader-page' | '/normal-page' | '/page-with-search' | '/virtual-page' fileRoutesByTo: FileRoutesByTo to: | '/' | '/lazy-page' | '/lazy-with-loader-page' | '/normal-page' | '/page-with-search' | '/virtual-page' id: | '__root__' | '/' | '/(tests)/lazy-page' | '/(tests)/lazy-with-loader-page' | '/(tests)/normal-page' | '/(tests)/page-with-search' | '/(tests)/virtual-page' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute testsLazyPageRoute: typeof testsLazyPageRoute testsLazyWithLoaderPageRoute: typeof testsLazyWithLoaderPageRoute testsNormalPageRoute: typeof testsNormalPageRoute testsPageWithSearchRoute: typeof testsPageWithSearchRoute testsVirtualPageLazyRoute: typeof testsVirtualPageLazyRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/(tests)/virtual-page': { id: '/(tests)/virtual-page' path: '/virtual-page' fullPath: '/virtual-page' preLoaderRoute: typeof testsVirtualPageLazyRouteImport parentRoute: typeof rootRouteImport } '/(tests)/page-with-search': { id: '/(tests)/page-with-search' path: '/page-with-search' fullPath: '/page-with-search' preLoaderRoute: typeof testsPageWithSearchRouteImport parentRoute: typeof rootRouteImport } '/(tests)/normal-page': { id: '/(tests)/normal-page' path: '/normal-page' fullPath: '/normal-page' preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/lazy-with-loader-page': { id: '/(tests)/lazy-with-loader-page' path: '/lazy-with-loader-page' fullPath: '/lazy-with-loader-page' preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/lazy-page': { id: '/(tests)/lazy-page' path: '/lazy-page' fullPath: '/lazy-page' preLoaderRoute: typeof testsLazyPageRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, testsLazyPageRoute: testsLazyPageRoute, testsLazyWithLoaderPageRoute: testsLazyWithLoaderPageRoute, testsNormalPageRoute: testsNormalPageRoute, testsPageWithSearchRoute: testsPageWithSearchRoute, testsVirtualPageLazyRoute: testsVirtualPageLazyRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-page.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ScrollBlock } from '../-components/scroll-block' export const Route = createLazyFileRoute('/(tests)/lazy-page')({ component: Component, }) function Component() { return (

lazy-page


) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-page.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/(tests)/lazy-page')({}) ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-with-loader-page.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/react-router' import { ScrollBlock } from '../-components/scroll-block' export const Route = createLazyFileRoute('/(tests)/lazy-with-loader-page')({ component: Component, }) function Component() { return (

lazy-with-loader-page


) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-with-loader-page.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { sleep } from '../../posts' export const Route = createFileRoute('/(tests)/lazy-with-loader-page')({ loader: async () => { await sleep(1000) return { foo: 'bar' } }, }) ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/normal-page.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/normal-page')({ component: Component, }) function Component() { return (

normal-page


) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/page-with-search.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/page-with-search')({ validateSearch: zodValidator(z.object({ where: z.string() })), component: Component, }) function Component() { return (

page-with-search


) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/(tests)/virtual-page.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/react-router' import * as React from 'react' import { ScrollBlock } from '../-components/scroll-block' export const Route = createLazyFileRoute('/(tests)/virtual-page')({ component: Component, }) function Component() { return (

virtual-page


) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/-components/scroll-block.tsx ================================================ export const atTheTopId = 'at-the-top' export const atTheBottomId = 'at-the-bottom' export function ScrollBlock({ number = 100 }: { number?: number }) { return ( <>
{Array.from({ length: number }).map((_, i) => (
{i}
))}
At the bottom
) } ================================================ FILE: e2e/react-router/scroll-restoration-sandbox-vite/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute, linkOptions, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( <>
) } ================================================ FILE: e2e/solid-start/basic-solid-query/src/routes/transition/count/query.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' import { Suspense } from 'solid-js' import { queryOptions, useQuery } from '@tanstack/solid-query' import { z } from 'zod' const searchSchema = z.object({ n: z.number().default(1), }) const doubleQueryOptions = (n: number) => queryOptions({ queryKey: ['transition-double', n], queryFn: async () => { await new Promise((resolve) => setTimeout(resolve, 1000)) return { n, double: n * 2 } }, placeholderData: (oldData) => oldData, }) export const Route = createFileRoute('/transition/count/query')({ validateSearch: searchSchema, loader: ({ context: { queryClient }, location }) => { const { n } = searchSchema.parse(location.search) return queryClient.ensureQueryData(doubleQueryOptions(n)) }, component: TransitionPage, }) function TransitionPage() { const search = Route.useSearch() const doubleQuery = useQuery(() => doubleQueryOptions(search().n)) return (
({ n: s.n + 1 })} > Increase
n: {doubleQuery.data?.n}
double: {doubleQuery.data?.double}
) } ================================================ FILE: e2e/solid-start/basic-solid-query/src/routes/users.$userId.tsx ================================================ import { useQuery } from '@tanstack/solid-query' import { ErrorComponent, createFileRoute } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' import { NotFound } from '~/components/NotFound' import { userQueryOptions } from '~/utils/users' export const Route = createFileRoute('/users/$userId')({ loader: async ({ context, params: { userId } }) => { await context.queryClient.ensureQueryData(userQueryOptions(userId)) }, errorComponent: UserErrorComponent, component: UserComponent, notFoundComponent: () => { return User not found }, }) function UserErrorComponent(props: ErrorComponentProps) { return } function UserComponent() { const params = Route.useParams() const userQuery = useQuery(() => userQueryOptions(params().userId)) const user = () => userQuery.data return (

{user()?.name ?? 'loading...'}

{user()?.email ?? ''}
) } ================================================ FILE: e2e/solid-start/basic-solid-query/src/routes/users.index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/users/')({ component: UsersIndexComponent, }) function UsersIndexComponent() { return
Select a user.
} ================================================ FILE: e2e/solid-start/basic-solid-query/src/routes/users.tsx ================================================ import { useQuery } from '@tanstack/solid-query' import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' import { For } from 'solid-js' import { usersQueryOptions } from '~/utils/users' export const Route = createFileRoute('/users')({ loader: async ({ context }) => { await context.queryClient.ensureQueryData(usersQueryOptions()) }, component: UsersComponent, }) function UsersComponent() { const usersQuery = useQuery(() => usersQueryOptions()) return (
    {(user) => (
  • {user.name}
  • )}

) } ================================================ FILE: e2e/solid-start/basic-solid-query/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } body { @apply text-base; font-family: system-ui, sans-serif; } ================================================ FILE: e2e/solid-start/basic-solid-query/src/utils/posts.tsx ================================================ import { queryOptions } from '@tanstack/solid-query' import axios from 'redaxios' export type PostType = { id: string title: string body: string } export const postsQueryOptions = () => queryOptions({ queryKey: ['posts'], queryFn: async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>('https://jsonplaceholder.typicode.com/posts') .then((r) => r.data.slice(0, 10)) }, }) export const postQueryOptions = (postId: string) => queryOptions({ queryKey: ['posts', postId], queryFn: async () => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) return axios .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) .then((r) => r.data) }, }) ================================================ FILE: e2e/solid-start/basic-solid-query/src/utils/seo.ts ================================================ export function seo(params: { title: string description?: string keywords?: string image?: string }) { const { title, description, keywords, image } = params const tags = [ { title }, { name: 'description', content: description }, { name: 'keywords', content: keywords }, { name: 'twitter:title', content: title }, { name: 'twitter:description', content: description }, { name: 'twitter:creator', content: '@tannerlinsley' }, { name: 'twitter:site', content: '@tannerlinsley' }, { name: 'og:type', content: 'website' }, { name: 'og:title', content: title }, { name: 'og:description', content: description }, ...(image ? [ { name: 'twitter:image', content: image }, { name: 'twitter:card', content: 'summary_large_image' }, { name: 'og:image', content: image }, ] : []), ] return tags } ================================================ FILE: e2e/solid-start/basic-solid-query/src/utils/users.tsx ================================================ import { queryOptions } from '@tanstack/solid-query' import axios from 'redaxios' export type User = { id: number name: string email: string } const PORT = import.meta.env.VITE_SERVER_PORT || process.env.VITE_SERVER_PORT || 3000 export const DEPLOY_URL = `http://localhost:${PORT}` export const usersQueryOptions = () => queryOptions({ queryKey: ['users'], queryFn: () => axios .get>(DEPLOY_URL + '/api/users') .then((r) => r.data) .catch(() => { throw new Error('Failed to fetch users') }), }) export const userQueryOptions = (id: string) => queryOptions({ queryKey: ['users', id], queryFn: () => axios .get(DEPLOY_URL + '/api/users/' + id) .then((r) => r.data) .catch(() => { throw new Error('Failed to fetch user') }), }) ================================================ FILE: e2e/solid-start/basic-solid-query/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test('Navigating to post', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await page.getByRole('link', { name: 'Deep View' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating to user', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Users' }).click() await page.getByRole('link', { name: 'Leanne Graham' }).click() await expect(page.getByRole('heading')).toContainText('Leanne Graham') }) test('Navigating nested layouts', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Layout', exact: true }).click() await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('body')).toContainText("I'm A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('body')).toContainText("I'm B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test('Manual Suspense boundaries should transition on navigation', async ({ page, }) => { // Navigate to the suspense transition test page await page.goto('/suspense-transition') // Wait for initial content to load await expect(page.getByTestId('n-value')).toHaveText('1') await expect(page.getByTestId('double-value')).toHaveText('2') // Click the increase button to trigger navigation with search params change await page.getByTestId('increase-button').click() // During the transition, the old content should remain visible // and the fallback should NOT be shown await expect(page.getByTestId('suspense-fallback')).not.toBeVisible({ timeout: 100, }) // The old content should still be visible during transition await expect(page.getByTestId('suspense-content')).toBeVisible() // After transition completes, new content should be visible await expect(page.getByTestId('n-value')).toHaveText('2') await expect(page.getByTestId('double-value')).toHaveText('4') }) ================================================ FILE: e2e/solid-start/basic-solid-query/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/basic-solid-query/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/basic-solid-query/tests/transition.spec.ts ================================================ import { expect, test } from '@playwright/test' test('transitions/count/query should keep old values visible during navigation', async ({ page, }) => { await page.goto('/transition/count/query') await expect(page.getByTestId('n-value')).toContainText('n: 1') await expect(page.getByTestId('double-value')).toContainText('double: 2') const bodyTexts: Array = [] const pollInterval = setInterval(async () => { const text = await page .locator('body') .textContent() .catch(() => '') if (text) bodyTexts.push(text) }, 50) // 1 click page.getByTestId('increase-button').click() await expect(page.getByTestId('n-value')).toContainText('n: 1', { timeout: 2_000, }) await expect(page.getByTestId('double-value')).toContainText('double: 2', { timeout: 2_000, }) await page.waitForTimeout(200) await expect(page.getByTestId('n-value')).toContainText('n: 2', { timeout: 2000, }) await expect(page.getByTestId('double-value')).toContainText('double: 4', { timeout: 2000, }) // 2 clicks // page.getByTestId('increase-button').click() // page.getByTestId('increase-button').click() // await expect(page.getByTestId('n-value')).toContainText('n: 2', { // timeout: 2000, // }) // await expect(page.getByTestId('double-value')).toContainText('double: 4', { // timeout: 2000, // }) // await page.waitForTimeout(200) // await expect(page.getByTestId('n-value')).toContainText('n: 4', { // timeout: 2000, // }) // await expect(page.getByTestId('double-value')).toContainText('double: 8', { // timeout: 2000, // }) // // 3 clicks // page.getByTestId('increase-button').click() // page.getByTestId('increase-button').click() // page.getByTestId('increase-button').click() // await expect(page.getByTestId('n-value')).toContainText('n: 4', { // timeout: 2000, // }) // await expect(page.getByTestId('double-value')).toContainText('double: 8', { // timeout: 2000, // }) // await page.waitForTimeout(200) // await expect(page.getByTestId('n-value')).toContainText('n: 7', { // timeout: 2000, // }) // await expect(page.getByTestId('double-value')).toContainText('double: 14', { // timeout: 2000, // }) clearInterval(pollInterval) // With proper transitions, old values should remain visible until new ones arrive const hasLoadingText = bodyTexts.some((text) => text.includes('Loading...')) if (hasLoadingText) { throw new Error( 'FAILED: "Loading..." appeared during navigation. ' + 'Solid Router should use transitions to keep old values visible.', ) } }) ================================================ FILE: e2e/solid-start/basic-solid-query/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true, "types": ["vite/client"] } } ================================================ FILE: e2e/solid-start/basic-solid-query/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import solid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, plugins: [tailwindcss(), tanstackStart(), solid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/basic-tsr-config/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/basic-tsr-config/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ count.txt ================================================ FILE: e2e/solid-start/basic-tsr-config/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/basic-tsr-config/README.md ================================================ # Welcome to TanStack.com! This site is built with TanStack Router! - [TanStack Router Docs](https://tanstack.com/router) It's deployed automagically with Netlify! - [Netlify](https://netlify.com/) ## Development From your terminal: ```sh pnpm install pnpm dev ``` This starts your app in development mode, rebuilding assets on file changes. ## Editing and previewing the docs of TanStack projects locally The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app. In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system. Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally : 1. Create a new directory called `tanstack`. ```sh mkdir tanstack ``` 2. Enter the directory and clone this repo and the repo of the project there. ```sh cd tanstack git clone git@github.com:TanStack/tanstack.com.git git clone git@github.com:TanStack/form.git ``` > [!NOTE] > Your `tanstack` directory should look like this: > > ``` > tanstack/ > | > +-- form/ > | > +-- tanstack.com/ > ``` > [!WARNING] > Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found. 3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode: ```sh cd tanstack.com pnpm i # The app will run on https://localhost:3000 by default pnpm dev ``` 4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`. > [!NOTE] > The updated pages need to be manually reloaded in the browser. > [!WARNING] > You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page! ================================================ FILE: e2e/solid-start/basic-tsr-config/package.json ================================================ { "name": "tanstack-solid-start-e2e-basic-tsr-config", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "rimraf ./count.txt && vite build && tsc --noEmit", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-start": "workspace:^", "solid-js": "^1.9.10", "vite": "^8.0.0" }, "devDependencies": { "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "srvx": "^0.11.9", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/basic-tsr-config/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `pnpm build && VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/basic-tsr-config/src/app/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' fileRoutesByTo: FileRoutesByTo to: '/' id: '__root__' | '/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/basic-tsr-config/src/app/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, }) return router } ================================================ FILE: e2e/solid-start/basic-tsr-config/src/app/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' export const Route = createRootRoute({ head: () => ({ meta: [ { charset: 'utf-8', }, { name: 'viewport', content: 'width=device-width, initial-scale=1', }, { title: 'TanStack Start Starter', }, ], }), component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: e2e/solid-start/basic-tsr-config/src/app/routes/index.tsx ================================================ import fs from 'node:fs' import { useRouter, createFileRoute } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' const filePath = 'count.txt' const getCount = createServerFn({ method: 'GET', }).handler(async () => { const number = await fs.promises.readFile(filePath, 'utf-8').catch(() => '0') return parseInt(number || '0') }) const updateCount = createServerFn({ method: 'POST' }) .inputValidator((d: number) => d) .handler(async ({ data }) => { const count = await getCount() await fs.promises.writeFile(filePath, `${count + data}`) }) export const Route = createFileRoute('/')({ component: Home, loader: async () => await getCount(), }) function Home() { const router = useRouter() const state = Route.useLoaderData() return ( ) } ================================================ FILE: e2e/solid-start/basic-tsr-config/src/app/vite-env.d.ts ================================================ declare module '*?url' { const url: string export default url } ================================================ FILE: e2e/solid-start/basic-tsr-config/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test('opening the app', async ({ page }) => { await page.goto('/') await page.waitForLoadState('networkidle') await expect(page.getByTestId('add-button')).toContainText('Add 1 to 0?') await page.getByTestId('add-button').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('add-button')).toContainText('Add 1 to 1?') }) ================================================ FILE: e2e/solid-start/basic-tsr-config/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/app/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/basic-tsr-config/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [ tanstackStart({ srcDirectory: './src/app', }), viteSolid({ ssr: true }), ], }) ================================================ FILE: e2e/solid-start/csp/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/csp/.gitignore ================================================ node_modules dist .output test-results playwright-report ================================================ FILE: e2e/solid-start/csp/package.json ================================================ { "name": "tanstack-solid-start-e2e-csp", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-start": "workspace:^", "solid-js": "^1.9.10", "vite": "^8.0.0" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "srvx": "^0.11.9", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/csp/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_SERVER_PORT=${PORT} pnpm build && PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/csp/public/external.css ================================================ .external-styled { color: blue; font-weight: bold; } ================================================ FILE: e2e/solid-start/csp/public/external.js ================================================ // This script sets a window global when loaded // Using a global avoids race conditions with Solid and DOM ownership issues window.__EXTERNAL_SCRIPT_LOADED__ = true ================================================ FILE: e2e/solid-start/csp/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' fileRoutesByTo: FileRoutesByTo to: '/' id: '__root__' | '/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/csp/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { createIsomorphicFn } from '@tanstack/solid-start' import { routeTree } from './routeTree.gen' const getSSROptions = createIsomorphicFn().server(() => { const array = new Uint8Array(16) crypto.getRandomValues(array) const nonce = Array.from(array, (b) => b.toString(16).padStart(2, '0')).join( '', ) return { nonce } }) export function getRouter() { return createRouter({ routeTree, scrollRestoration: true, ssr: getSSROptions(), }) } ================================================ FILE: e2e/solid-start/csp/src/routes/__root.tsx ================================================ import { createRootRoute, HeadContent, Outlet, Scripts, } from '@tanstack/solid-router' import { HydrationScript } from 'solid-js/web' export const Route = createRootRoute({ headers: ({ ssr }) => { const nonce = ssr?.nonce if (!nonce) return return { 'Content-Security-Policy': [ "default-src 'self'", `script-src 'self' 'nonce-${nonce}'`, `style-src 'self' 'nonce-${nonce}'`, ].join('; '), } }, head: () => ({ meta: [ { charSet: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { title: 'CSP Nonce Test' }, ], links: [{ rel: 'stylesheet', href: '/external.css' }], scripts: [{ src: '/external.js' }], styles: [ { children: '.inline-styled { color: green; font-weight: bold; }' }, ], }), component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: e2e/solid-start/csp/src/routes/index.tsx ================================================ import { createSignal } from 'solid-js' import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { const [count, setCount] = createSignal(0) return (

CSP Nonce Test

This should be green if inline styles work

This should be blue if external styles work

) } ================================================ FILE: e2e/solid-start/csp/tests/csp.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' test('CSP header is set with nonce', async ({ page }) => { const response = await page.goto('/') const csp = response?.headers()['content-security-policy'] expect(csp).toContain("script-src 'self' 'nonce-") expect(csp).toContain("style-src 'self' 'nonce-") }) test('Inline scripts have nonce attribute', async ({ page }) => { await page.goto('/') const scripts = await page.locator('script[nonce]').all() expect(scripts.length).toBeGreaterThan(0) }) test('Inline styles have nonce attribute', async ({ page }) => { await page.goto('/') const styles = await page.locator('style[nonce]').all() expect(styles.length).toBeGreaterThan(0) }) test('External script has nonce attribute', async ({ page }) => { await page.goto('/') const externalScript = page.locator('script[src="/external.js"]') await expect(externalScript).toHaveAttribute('nonce') }) test('External stylesheet has nonce attribute', async ({ page }) => { await page.goto('/') const externalStylesheet = page.locator('link[href="/external.css"]') await expect(externalStylesheet).toHaveAttribute('nonce') }) test('Nonces match between header and elements', async ({ page }) => { // Intercept the HTML response to get raw content before browser strips nonces let rawHtml = '' await page.route('/', async (route) => { const response = await route.fetch() rawHtml = await response.text() await route.fulfill({ response }) }) const response = await page.goto('/') await page.unrouteAll({ behavior: 'ignoreErrors' }) const csp = response?.headers()['content-security-policy'] || '' // Extract nonce from CSP header const nonceMatch = csp.match(/nonce-([a-f0-9]+)/) expect(nonceMatch).toBeTruthy() const headerNonce = nonceMatch![1] // Check script nonces match - look for nonce attribute anywhere in the script tag const scriptNonces = [ ...rawHtml.matchAll(/]*\bnonce="([^"]+)"[^>]*>/g), ].map((m) => m[1]) expect(scriptNonces.length).toBeGreaterThan(0) for (const nonce of scriptNonces) { expect(nonce).toBe(headerNonce) } // Check style nonces match const styleNonces = [ ...rawHtml.matchAll(/]*\bnonce="([^"]+)"[^>]*>/g), ].map((m) => m[1]) expect(styleNonces.length).toBeGreaterThan(0) for (const nonce of styleNonces) { expect(nonce).toBe(headerNonce) } // Check external script nonce matches (nonce can be before or after src) const externalScriptMatch = rawHtml.match( /]*\bsrc="\/external\.js"[^>]*\bnonce="([^"]+)"[^>]*>|]*\bnonce="([^"]+)"[^>]*\bsrc="\/external\.js"[^>]*>/, ) expect(externalScriptMatch).toBeTruthy() expect(externalScriptMatch![1] || externalScriptMatch![2]).toBe(headerNonce) // Check external stylesheet nonce matches (nonce can be before or after href) const externalStyleMatch = rawHtml.match( /]*\bhref="\/external\.css"[^>]*\bnonce="([^"]+)"[^>]*>|]*\bnonce="([^"]+)"[^>]*\bhref="\/external\.css"[^>]*>/, ) expect(externalStyleMatch).toBeTruthy() expect(externalStyleMatch![1] || externalStyleMatch![2]).toBe(headerNonce) }) test('Hydration works - counter increments', async ({ page }) => { await page.goto('/') await expect(page.getByTestId('counter-value')).toContainText('0') // Keep clicking until Solid hydrates and the counter increments // This handles the race between test execution and hydration await expect .poll( async () => { await page.getByTestId('counter-btn').click() return page.getByTestId('counter-value').textContent() }, { timeout: 10000, intervals: [100, 200, 500, 1000] }, ) .not.toBe('0') // Now that hydration is confirmed, verify further increments work const currentValue = parseInt( (await page.getByTestId('counter-value').textContent()) || '0', ) await page.getByTestId('counter-btn').click() await expect(page.getByTestId('counter-value')).toContainText( String(currentValue + 1), ) }) test('Inline styles work with CSP', async ({ page }) => { await page.goto('/') const el = page.getByTestId('inline-styled') await expect(el).toBeVisible() // Verify the style was applied (green color) const color = await el.evaluate((e) => getComputedStyle(e).color) expect(color).toBe('rgb(0, 128, 0)') // green }) test('External styles work with CSP', async ({ page }) => { await page.goto('/') const el = page.getByTestId('external-styled') await expect(el).toBeVisible() // Verify the style was applied (blue color) const color = await el.evaluate((e) => getComputedStyle(e).color) expect(color).toBe('rgb(0, 0, 255)') // blue }) test('External script executes with CSP', async ({ page }) => { await page.goto('/') // Check that the external script set its window global await expect .poll(() => page.evaluate(() => (window as any).__EXTERNAL_SCRIPT_LOADED__)) .toBe(true) }) test('No CSP violations in console', async ({ page }) => { const violations: string[] = [] page.on('console', (msg) => { if (msg.text().toLowerCase().includes('content security policy')) { violations.push(msg.text()) } }) page.on('pageerror', (err) => { if (err.message.toLowerCase().includes('content security policy')) { violations.push(err.message) } }) await page.goto('/') await page.getByTestId('counter-btn').click() // Small wait to ensure any async violations are caught await page.waitForTimeout(100) expect(violations).toEqual([]) }) test('Each request gets a unique nonce', async ({ page }) => { const response1 = await page.goto('/') const csp1 = response1?.headers()['content-security-policy'] || '' const nonce1 = csp1.match(/nonce-([a-f0-9]+)/)?.[1] const response2 = await page.goto('/') const csp2 = response2?.headers()['content-security-policy'] || '' const nonce2 = csp2.match(/nonce-([a-f0-9]+)/)?.[1] expect(nonce1).toBeTruthy() expect(nonce2).toBeTruthy() expect(nonce1).not.toBe(nonce2) }) ================================================ FILE: e2e/solid-start/csp/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/csp/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' export default defineConfig({ server: { port: 3000, }, plugins: [tanstackStart(), viteSolid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/css-modules/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/css-modules/.gitignore ================================================ node_modules dist .routeTree.gen.ts src/routeTree.gen.ts test-results playwright-report port*.txt ================================================ FILE: e2e/solid-start/css-modules/.prettierignore ================================================ src/routeTree.gen.ts ================================================ FILE: e2e/solid-start/css-modules/package.json ================================================ { "name": "tanstack-solid-start-e2e-css-modules", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev --port $PORT", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e:dev": "MODE=dev playwright test --project=chromium", "test:e2e:prod": "playwright test --project=chromium", "test:e2e": "rm -rf port*.txt; pnpm run test:e2e:dev" }, "dependencies": { "@tanstack/solid-router": "workspace:*", "@tanstack/solid-start": "workspace:*", "solid-js": "^1.9.10" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:*", "@types/node": "^22.10.2", "sass": "^1.97.2", "srvx": "^0.11.9", "typescript": "^5.7.2", "vite": "^8.0.0", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/css-modules/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const mode = process.env.MODE ?? 'prod' const isDev = mode === 'dev' const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { baseURL, }, webServer: { command: isDev ? `pnpm dev:e2e` : `pnpm build && PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', env: { VITE_NODE_ENV: 'test', PORT: String(PORT), }, }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'], }, }, ], }) ================================================ FILE: e2e/solid-start/css-modules/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, defaultPreload: false, }) return router } ================================================ FILE: e2e/solid-start/css-modules/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { HydrationScript } from 'solid-js/web' export const Route = createRootRoute({ head: () => ({ meta: [ { charSet: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, ], }), component: RootComponent, }) function RootComponent() { return (
) } ================================================ FILE: e2e/solid-start/css-modules/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import '~/styles/global.css' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

CSS Collection Test - Global CSS

This page tests that global CSS is collected and served during SSR.

Global CSS Applied
This container should have a blue background, white text, and rounded corners even with JavaScript disabled.
) } ================================================ FILE: e2e/solid-start/css-modules/src/routes/modules.tsx ================================================ /// import { createFileRoute } from '@tanstack/solid-router' import styles from '~/styles/card.module.css' export const Route = createFileRoute('/modules')({ component: Modules, }) function Modules() { return (

CSS Collection Test - CSS Modules

This page tests that CSS modules are collected and served during SSR.

CSS Module Applied
This card should have a green theme with scoped class names even with JavaScript disabled.
) } ================================================ FILE: e2e/solid-start/css-modules/src/routes/sass-mixin.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import '~/styles/app.scss' export const Route = createFileRoute('/sass-mixin')({ component: SassMixin, }) function SassMixin() { return (

CSS Collection Test - Sass Mixin

This page tests that Sass mixins imported in a parent file are available to subsequent imports during dev mode SSR.

Sass Mixin Applied - Should be centered with purple background
) } ================================================ FILE: e2e/solid-start/css-modules/src/styles/app.scss ================================================ // Import the mixin, which should make them available to future imports @import './center-mixin.scss'; @import './mixin-consumer.scss'; ================================================ FILE: e2e/solid-start/css-modules/src/styles/card.module.css ================================================ /* CSS Module for testing scoped styles in dev mode */ .card { background-color: #f0fdf4; /* green-50 */ padding: 16px; border-radius: 8px; border: 1px solid #22c55e; /* green-500 */ } .title { font-size: 18px; font-weight: 600; color: #166534; /* green-800 */ margin-bottom: 8px; } .content { font-size: 14px; color: #15803d; /* green-700 */ } ================================================ FILE: e2e/solid-start/css-modules/src/styles/center-mixin.scss ================================================ @mixin center-mixin { display: flex; justify-content: center; align-items: center; } ================================================ FILE: e2e/solid-start/css-modules/src/styles/global.css ================================================ /* Global styles for testing CSS collection in dev mode */ .global-container { background-color: #3b82f6; /* blue-500 */ padding: 24px; border-radius: 12px; color: white; } .global-title { font-size: 24px; font-weight: bold; margin-bottom: 16px; } .global-description { font-size: 16px; opacity: 0.9; } ================================================ FILE: e2e/solid-start/css-modules/src/styles/mixin-consumer.scss ================================================ .mixin-container { // Use the center mixin, which is included in app.scss // This works for the client build, but not for the dev server styles.css build @include center-mixin; background-color: #a855f7; /* purple-500 */ padding: 24px; border-radius: 12px; color: white; min-height: 100px; } ================================================ FILE: e2e/solid-start/css-modules/tests/css.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' // Whitelist errors that can occur in CI: // - net::ERR_NAME_NOT_RESOLVED: transient network issues // - 504 (Outdated Optimize Dep): Vite dependency optimization reload const whitelistErrors = [ 'Failed to load resource: net::ERR_NAME_NOT_RESOLVED', 'Failed to load resource: the server responded with a status of 504', ] test.describe('CSS styles in SSR (dev mode)', () => { test.use({ whitelistErrors }) // Helper to build full URL from baseURL and path // Playwright's goto with absolute paths (like '/modules') ignores baseURL's path portion // So we need to manually construct the full URL const buildUrl = (baseURL: string, path: string) => { return baseURL.replace(/\/$/, '') + path } test.describe('with JavaScript disabled', () => { test.use({ javaScriptEnabled: false, whitelistErrors }) test('global CSS is applied on initial page load', async ({ page, baseURL, }) => { await page.goto(buildUrl(baseURL!, '/')) const element = page.getByTestId('global-styled') await expect(element).toBeVisible() // Verify the CSS is applied by checking computed styles // #3b82f6 (blue-500) in RGB is rgb(59, 130, 246) const backgroundColor = await element.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(59, 130, 246)') const padding = await element.evaluate( (el) => getComputedStyle(el).padding, ) expect(padding).toBe('24px') const borderRadius = await element.evaluate( (el) => getComputedStyle(el).borderRadius, ) expect(borderRadius).toBe('12px') }) test('CSS modules are applied on initial page load', async ({ page, baseURL, }) => { await page.goto(buildUrl(baseURL!, '/modules')) const card = page.getByTestId('module-card') await expect(card).toBeVisible() // Verify class is scoped (hashed) const className = await card.getAttribute('class') expect(className).toBeTruthy() expect(className).not.toBe('card') // The class should contain some hash characters (CSS modules add a hash) expect(className!.length).toBeGreaterThan(5) // Verify computed styles from card.module.css // #f0fdf4 (green-50) in RGB is rgb(240, 253, 244) const backgroundColor = await card.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(240, 253, 244)') const padding = await card.evaluate((el) => getComputedStyle(el).padding) expect(padding).toBe('16px') const borderRadius = await card.evaluate( (el) => getComputedStyle(el).borderRadius, ) expect(borderRadius).toBe('8px') }) test('global CSS class names are NOT scoped', async ({ page, baseURL }) => { await page.goto(buildUrl(baseURL!, '/')) const element = page.getByTestId('global-styled') await expect(element).toBeVisible() // Get the class attribute - it should be the plain class name (not hashed) const className = await element.getAttribute('class') expect(className).toBe('global-container') }) test('Sass mixin styles are applied on initial page load', async ({ page, baseURL, }) => { await page.goto(buildUrl(baseURL!, '/sass-mixin')) const element = page.getByTestId('mixin-styled') await expect(element).toBeVisible() // Verify the mixin is applied (display: flex from center-mixin) const display = await element.evaluate( (el) => getComputedStyle(el).display, ) expect(display).toBe('flex') const justifyContent = await element.evaluate( (el) => getComputedStyle(el).justifyContent, ) expect(justifyContent).toBe('center') const alignItems = await element.evaluate( (el) => getComputedStyle(el).alignItems, ) expect(alignItems).toBe('center') // Verify other styles from mixin-consumer.scss // #a855f7 (purple-500) in RGB is rgb(168, 85, 247) const backgroundColor = await element.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(168, 85, 247)') const padding = await element.evaluate( (el) => getComputedStyle(el).padding, ) expect(padding).toBe('24px') }) }) test('styles persist after hydration', async ({ page, baseURL }) => { await page.goto(buildUrl(baseURL!, '/')) // Wait for hydration and styles to be applied const element = page.getByTestId('global-styled') await expect(element).toBeVisible() // Wait for CSS to be applied (background color should not be transparent) await expect(async () => { const backgroundColor = await element.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(59, 130, 246)') }).toPass({ timeout: 5000 }) }) test('CSS modules styles persist after hydration', async ({ page, baseURL, }) => { await page.goto(buildUrl(baseURL!, '/modules')) // Wait for hydration and styles to be applied const card = page.getByTestId('module-card') await expect(card).toBeVisible() // Wait for CSS to be applied (background color should not be transparent) await expect(async () => { const backgroundColor = await card.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(240, 253, 244)') }).toPass({ timeout: 5000 }) }) test('styles work correctly after client-side navigation', async ({ page, baseURL, }) => { // Start from home await page.goto(buildUrl(baseURL!, '/')) // Verify initial styles const globalElement = page.getByTestId('global-styled') await expect(globalElement).toBeVisible() let backgroundColor = await globalElement.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(59, 130, 246)') // Navigate to modules page await page.getByTestId('nav-modules').click() await page.waitForURL('**/modules') // Verify CSS modules styles const card = page.getByTestId('module-card') await expect(card).toBeVisible() backgroundColor = await card.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(240, 253, 244)') // Navigate back to home await page.getByTestId('nav-home').click() await page.waitForURL(/\/([^/]*)(\/)?($|\?)/) // Verify global styles still work await expect(globalElement).toBeVisible() backgroundColor = await globalElement.evaluate( (el) => getComputedStyle(el).backgroundColor, ) expect(backgroundColor).toBe('rgb(59, 130, 246)') }) }) ================================================ FILE: e2e/solid-start/css-modules/tests/setup/global.setup.ts ================================================ import { chromium } from '@playwright/test' import { e2eStartDummyServer, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } async function waitForServer(url: string) { const start = Date.now() while (Date.now() - start < 30_000) { const controller = new AbortController() const timer = setTimeout(() => controller.abort(), 5_000) try { const res = await fetch(url, { redirect: 'manual', signal: controller.signal, }) if (res.ok) return } catch { // ignore aborted/network errors } finally { clearTimeout(timer) } await new Promise((r) => setTimeout(r, 250)) } throw new Error(`Timed out waiting for dev server at ${url}`) } async function preOptimizeDevServer(baseURL: string) { const browser = await chromium.launch() const context = await browser.newContext() const page = await context.newPage() try { await page.goto(`${baseURL}/`, { waitUntil: 'domcontentloaded' }) await page.getByTestId('global-styled').waitFor({ state: 'visible' }) await page.waitForLoadState('networkidle') await page.goto(`${baseURL}/modules`, { waitUntil: 'domcontentloaded' }) await page.getByTestId('module-card').waitFor({ state: 'visible' }) await page.waitForLoadState('networkidle') await page.goto(`${baseURL}/sass-mixin`, { waitUntil: 'domcontentloaded' }) await page.getByTestId('mixin-styled').waitFor({ state: 'visible' }) await page.waitForLoadState('networkidle') // Exercise client-side navigation so Vite discovers any remaining deps // that only load via the client router (not full-page navigations). await page.goto(`${baseURL}/`, { waitUntil: 'domcontentloaded' }) await page.getByTestId('global-styled').waitFor({ state: 'visible' }) await page.waitForLoadState('networkidle') await page.getByTestId('nav-modules').click() await page.waitForURL('**/modules') await page.getByTestId('module-card').waitFor({ state: 'visible' }) await page.waitForLoadState('networkidle') await page.getByTestId('nav-home').click() await page.waitForURL(/\/([^/]*)(\/)?($|\?)/) await page.getByTestId('global-styled').waitFor({ state: 'visible' }) await page.waitForLoadState('networkidle') // Ensure we end in a stable state. Vite's optimize step triggers a reload; // this waits until no further navigations happen for a short window. for (let i = 0; i < 40; i++) { const currentUrl = page.url() await page.waitForTimeout(250) if (page.url() === currentUrl) { await page.waitForTimeout(250) if (page.url() === currentUrl) return } } throw new Error('Dev server did not reach a stable URL after warmup') } finally { await context.close() await browser.close() } } export default async function setup() { await e2eStartDummyServer(packageJson.name) if (process.env.MODE !== 'dev') return const port = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${port}` await waitForServer(baseURL) await preOptimizeDevServer(baseURL) } ================================================ FILE: e2e/solid-start/css-modules/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/css-modules/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true, "types": ["vite/client"] } } ================================================ FILE: e2e/solid-start/css-modules/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [tanstackStart(), viteSolid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/custom-basepath/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/custom-basepath/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/solid-start/custom-basepath/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/custom-basepath/express-server.ts ================================================ import express from 'express' import { toNodeHandler } from 'srvx/node' import type { NodeHttp1Handler } from 'srvx' const DEVELOPMENT = process.env.NODE_ENV === 'development' const PORT = Number.parseInt(process.env.PORT || '3000') const app = express() if (DEVELOPMENT) { const viteDevServer = await import('vite').then((vite) => vite.createServer({ server: { middlewareMode: true }, }), ) app.use(viteDevServer.middlewares) app.use(async (req, res, next) => { try { const { default: serverEntry } = await viteDevServer.ssrLoadModule('./src/server.ts') const handler = toNodeHandler(serverEntry.fetch) as NodeHttp1Handler await handler(req, res) } catch (error) { if (typeof error === 'object' && error instanceof Error) { viteDevServer.ssrFixStacktrace(error) } next(error) } }) } else { const { default: handler } = await import('./dist/server/server.js') const nodeHandler = toNodeHandler(handler.fetch) as NodeHttp1Handler app.use('/custom/basepath', express.static('dist/client')) app.use(async (req, res, next) => { try { await nodeHandler(req, res) } catch (error) { next(error) } }) } app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`) }) ================================================ FILE: e2e/solid-start/custom-basepath/package.json ================================================ { "name": "tanstack-solid-start-e2e-custom-basepath", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "cross-env NODE_ENV=development tsx express-server.ts", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "tsx express-server.ts", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-start": "workspace:^", "express": "^5.1.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/express": "^5.0.3", "@types/node": "^22.10.2", "cross-env": "^10.0.0", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", "tsx": "^4.20.3", "typescript": "^5.7.2", "vite": "^8.0.0", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/custom-basepath/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}/custom/basepath` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/custom-basepath/public/script.js ================================================ console.log('SCRIPT_1 loaded') window.SCRIPT_1 = true ================================================ FILE: e2e/solid-start/custom-basepath/public/script2.js ================================================ console.log('SCRIPT_2 loaded') window.SCRIPT_2 = true ================================================ FILE: e2e/solid-start/custom-basepath/public/site.webmanifest ================================================ { "name": "", "short_name": "", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } ================================================ FILE: e2e/solid-start/custom-basepath/src/components/CustomMessage.tsx ================================================ export function CustomMessage({ message }: { message: string }) { return (
This is a custom message:

{message}

) } ================================================ FILE: e2e/solid-start/custom-basepath/src/components/DefaultCatchBoundary.tsx ================================================ import { ErrorComponent, Link, rootRouteId, useMatch, useRouter, } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter() const isRoot = useMatch({ strict: false, select: (state) => state.id === rootRouteId, }) console.error(error) return (
{isRoot() ? ( Home ) : ( { e.preventDefault() window.history.back() }} > Go Back )}
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/components/NotFound.tsx ================================================ import { Link } from '@tanstack/solid-router' export function NotFound({ children }: { children?: any }) { return (
{children ||

The page you are looking for does not exist.

}

Start Over

) } ================================================ FILE: e2e/solid-start/custom-basepath/src/components/PostErrorComponent.tsx ================================================ import { ErrorComponent, ErrorComponentProps } from '@tanstack/solid-router' export function PostErrorComponent({ error }: ErrorComponentProps) { return } ================================================ FILE: e2e/solid-start/custom-basepath/src/components/UserErrorComponent.tsx ================================================ import { ErrorComponent } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' export function UserErrorComponent({ error }: ErrorComponentProps) { return } ================================================ FILE: e2e/solid-start/custom-basepath/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as UsersRouteImport } from './routes/users' import { Route as PostsRouteImport } from './routes/posts' import { Route as NavigateTestRouteImport } from './routes/navigate-test' import { Route as LogoutRouteImport } from './routes/logout' import { Route as DeferredRouteImport } from './routes/deferred' import { Route as IndexRouteImport } from './routes/index' import { Route as UsersIndexRouteImport } from './routes/users.index' import { Route as RedirectIndexRouteImport } from './routes/redirect.index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' const UsersRoute = UsersRouteImport.update({ id: '/users', path: '/users', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const NavigateTestRoute = NavigateTestRouteImport.update({ id: '/navigate-test', path: '/navigate-test', getParentRoute: () => rootRouteImport, } as any) const LogoutRoute = LogoutRouteImport.update({ id: '/logout', path: '/logout', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ id: '/deferred', path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => UsersRoute, } as any) const RedirectIndexRoute = RedirectIndexRouteImport.update({ id: '/redirect/', path: '/redirect/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRoute, } as any) const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ id: '/redirect/throw-it', path: '/redirect/throw-it', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const ApiUsersRoute = ApiUsersRouteImport.update({ id: '/api/users', path: '/api/users', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ id: '/posts_/$postId/deep', path: '/posts/$postId/deep', getParentRoute: () => rootRouteImport, } as any) const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => ApiUsersRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/deferred': typeof DeferredRoute '/logout': typeof LogoutRoute '/navigate-test': typeof NavigateTestRoute '/posts': typeof PostsRouteWithChildren '/users': typeof UsersRouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/redirect/throw-it': typeof RedirectThrowItRoute '/users/$userId': typeof UsersUserIdRoute '/posts/': typeof PostsIndexRoute '/redirect/': typeof RedirectIndexRoute '/users/': typeof UsersIndexRoute '/api/users/$userId': typeof ApiUsersUserIdRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/deferred': typeof DeferredRoute '/logout': typeof LogoutRoute '/navigate-test': typeof NavigateTestRoute '/api/users': typeof ApiUsersRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/redirect/throw-it': typeof RedirectThrowItRoute '/users/$userId': typeof UsersUserIdRoute '/posts': typeof PostsIndexRoute '/redirect': typeof RedirectIndexRoute '/users': typeof UsersIndexRoute '/api/users/$userId': typeof ApiUsersUserIdRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/deferred': typeof DeferredRoute '/logout': typeof LogoutRoute '/navigate-test': typeof NavigateTestRoute '/posts': typeof PostsRouteWithChildren '/users': typeof UsersRouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/redirect/throw-it': typeof RedirectThrowItRoute '/users/$userId': typeof UsersUserIdRoute '/posts/': typeof PostsIndexRoute '/redirect/': typeof RedirectIndexRoute '/users/': typeof UsersIndexRoute '/api/users/$userId': typeof ApiUsersUserIdRoute '/posts_/$postId/deep': typeof PostsPostIdDeepRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/deferred' | '/logout' | '/navigate-test' | '/posts' | '/users' | '/api/users' | '/posts/$postId' | '/redirect/throw-it' | '/users/$userId' | '/posts/' | '/redirect/' | '/users/' | '/api/users/$userId' | '/posts/$postId/deep' fileRoutesByTo: FileRoutesByTo to: | '/' | '/deferred' | '/logout' | '/navigate-test' | '/api/users' | '/posts/$postId' | '/redirect/throw-it' | '/users/$userId' | '/posts' | '/redirect' | '/users' | '/api/users/$userId' | '/posts/$postId/deep' id: | '__root__' | '/' | '/deferred' | '/logout' | '/navigate-test' | '/posts' | '/users' | '/api/users' | '/posts/$postId' | '/redirect/throw-it' | '/users/$userId' | '/posts/' | '/redirect/' | '/users/' | '/api/users/$userId' | '/posts_/$postId/deep' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute DeferredRoute: typeof DeferredRoute LogoutRoute: typeof LogoutRoute NavigateTestRoute: typeof NavigateTestRoute PostsRoute: typeof PostsRouteWithChildren UsersRoute: typeof UsersRouteWithChildren ApiUsersRoute: typeof ApiUsersRouteWithChildren RedirectThrowItRoute: typeof RedirectThrowItRoute RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/users': { id: '/users' path: '/users' fullPath: '/users' preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/navigate-test': { id: '/navigate-test' path: '/navigate-test' fullPath: '/navigate-test' preLoaderRoute: typeof NavigateTestRouteImport parentRoute: typeof rootRouteImport } '/logout': { id: '/logout' path: '/logout' fullPath: '/logout' preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } '/deferred': { id: '/deferred' path: '/deferred' fullPath: '/deferred' preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/users/': { id: '/users/' path: '/' fullPath: '/users/' preLoaderRoute: typeof UsersIndexRouteImport parentRoute: typeof UsersRoute } '/redirect/': { id: '/redirect/' path: '/redirect' fullPath: '/redirect/' preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/users/$userId': { id: '/users/$userId' path: '/$userId' fullPath: '/users/$userId' preLoaderRoute: typeof UsersUserIdRouteImport parentRoute: typeof UsersRoute } '/redirect/throw-it': { id: '/redirect/throw-it' path: '/redirect/throw-it' fullPath: '/redirect/throw-it' preLoaderRoute: typeof RedirectThrowItRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/api/users': { id: '/api/users' path: '/api/users' fullPath: '/api/users' preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } '/posts_/$postId/deep': { id: '/posts_/$postId/deep' path: '/posts/$postId/deep' fullPath: '/posts/$postId/deep' preLoaderRoute: typeof PostsPostIdDeepRouteImport parentRoute: typeof rootRouteImport } '/api/users/$userId': { id: '/api/users/$userId' path: '/$userId' fullPath: '/api/users/$userId' preLoaderRoute: typeof ApiUsersUserIdRouteImport parentRoute: typeof ApiUsersRoute } } } interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) interface UsersRouteChildren { UsersUserIdRoute: typeof UsersUserIdRoute UsersIndexRoute: typeof UsersIndexRoute } const UsersRouteChildren: UsersRouteChildren = { UsersUserIdRoute: UsersUserIdRoute, UsersIndexRoute: UsersIndexRoute, } const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren) interface ApiUsersRouteChildren { ApiUsersUserIdRoute: typeof ApiUsersUserIdRoute } const ApiUsersRouteChildren: ApiUsersRouteChildren = { ApiUsersUserIdRoute: ApiUsersUserIdRoute, } const ApiUsersRouteWithChildren = ApiUsersRoute._addFileChildren( ApiUsersRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, DeferredRoute: DeferredRoute, LogoutRoute: LogoutRoute, NavigateTestRoute: NavigateTestRoute, PostsRoute: PostsRouteWithChildren, UsersRoute: UsersRouteWithChildren, ApiUsersRoute: ApiUsersRouteWithChildren, RedirectThrowItRoute: RedirectThrowItRoute, RedirectIndexRoute: RedirectIndexRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/custom-basepath/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' import { basepath } from './utils/basepath' export function getRouter() { const router = createRouter({ routeTree, defaultPreload: 'intent', defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , scrollRestoration: true, basepath: basepath, }) return router } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { TanStackRouterDevtoolsInProd } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' import { NotFound } from '~/components/NotFound' import appCss from '~/styles/app.css?url' import { seo } from '~/utils/seo' export const Route = createRootRoute({ head: () => ({ meta: [ { name: 'viewport', content: 'width=device-width, initial-scale=1', }, ...seo({ title: 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, }), ], links: [ { rel: 'stylesheet', href: appCss }, { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png', }, { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png', }, { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png', }, { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, { rel: 'icon', href: '/favicon.ico' }, ], }), errorComponent: (props) =>

{props.error.stack}

, notFoundComponent: () => , component: RootComponent, }) function RootComponent() { return (
Home {' '} Posts {' '} Users {' '} Deferred {' '} This Route Does Not Exist
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/api/users.$userId.ts ================================================ import { createFileRoute } from '@tanstack/solid-router' import type { User } from '~/utils/users' let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const Route = createFileRoute('/api/users/$userId')({ server: { handlers: { GET: async ({ params, request }) => { console.info(`Fetching users by id=${params.userId}... @`, request.url) try { const res = await fetch(`${queryURL}/users/${params.userId}`) if (!res.ok) { throw new Error('Failed to fetch user') } const user = (await res.json()) as User return Response.json({ id: user.id, name: user.name, email: user.email, }) } catch (e) { console.error(e) return Response.json({ error: 'User not found' }, { status: 404 }) } }, }, }, }) ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/api/users.ts ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createMiddleware } from '@tanstack/solid-start' import type { User } from '~/utils/users' const userLoggerMiddleware = createMiddleware().server( async ({ next, request }) => { console.info('In: /users') const result = await next() result.response.headers.set('x-users', 'true') console.info('Out: /users') return result }, ) const testParentMiddleware = createMiddleware().server( async ({ next, request }) => { console.info('In: testParentMiddleware') const result = await next() result.response.headers.set('x-test-parent', 'true') console.info('Out: testParentMiddleware') return result }, ) const testMiddleware = createMiddleware() .middleware([testParentMiddleware]) .server(async ({ next, request }) => { console.info('In: testMiddleware') const result = await next() result.response.headers.set('x-test', 'true') // if (Math.random() > 0.5) { // throw new Response(null, { // status: 302, // headers: { Location: 'https://www.google.com' }, // }) // } console.info('Out: testMiddleware') return result }) let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const Route = createFileRoute('/api/users')({ server: { middleware: [testMiddleware, userLoggerMiddleware, testParentMiddleware], handlers: { GET: async ({ request }) => { console.info('Fetching users... @', request.url) const res = await fetch(`${queryURL}/users`) if (!res.ok) { throw new Error('Failed to fetch users') } const data = (await res.json()) as Array const list = data.slice(0, 10) return Response.json( list.map((u) => ({ id: u.id, name: u.name, email: u.email })), ) }, }, }, }) ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/deferred.tsx ================================================ import { Await, createFileRoute } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' import { Suspense, createSignal } from 'solid-js' const personServerFn = createServerFn({ method: 'GET' }) .inputValidator((data: { name: string }) => data) .handler(({ data }) => { return { name: data.name, randomNumber: Math.floor(Math.random() * 100) } }) const slowServerFn = createServerFn({ method: 'GET' }) .inputValidator((data: { name: string }) => data) .handler(async ({ data }) => { await new Promise((r) => setTimeout(r, 1000)) return { name: data.name, randomNumber: Math.floor(Math.random() * 100) } }) export const Route = createFileRoute('/deferred')({ loader: async () => { return { deferredStuff: new Promise((r) => setTimeout(() => r('Hello deferred!'), 2000), ), deferredPerson: slowServerFn({ data: { name: 'Tanner Linsley' } }), person: await personServerFn({ data: { name: 'John Doe' } }), } }, component: Deferred, }) function Deferred() { const [count, setCount] = createSignal(0) // const { deferredStuff, deferredPerson, person } = Route.useLoaderData() const loaderData = Route.useLoaderData() return (
{loaderData().person.name} - {loaderData().person.randomNumber}
Loading person...
}> (
{data.name} - {data.randomNumber}
)} /> Loading stuff...
}>

{data}

} />
Count: {count()}
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { CustomMessage } from '~/components/CustomMessage' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!!!

) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/logout.tsx ================================================ import { createFileRoute, redirect } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' const logoutFn = createServerFn({ method: 'POST', }).handler(async () => { // do logout stuff here throw redirect({ to: '/', }) }) export const Route = createFileRoute('/logout')({ component: Home, }) function Home() { return (

Logout Page

This form tests that server function URLs correctly include the app's basepath. The form action should be '/custom/basepath/_serverFn/...' not just '/_serverFn/...'

) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/navigate-test.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/navigate-test')({ component: NavigateTest, }) function NavigateTest() { const navigate = Route.useNavigate() return (

Navigate Test

{' '}
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/posts.$postId.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' import { fetchPost } from '~/utils/posts' import { NotFound } from '~/components/NotFound' import { PostErrorComponent } from '~/components/PostErrorComponent' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost({ data: postId }), errorComponent: PostErrorComponent, component: PostComponent, notFoundComponent: () => { return Post not found }, }) function PostComponent() { const post = Route.useLoaderData() return (

{post().title}

{post().body}
Deep View
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' import { For } from 'solid-js' import { fetchPosts } from '~/utils/posts' export const Route = createFileRoute('/posts')({ head: () => ({ meta: [ { title: 'Posts page', }, ], }), loader: async () => fetchPosts(), component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {(post) => { return (
  • {post.title.substring(0, 20)}
  • ) }}

) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/posts_.$postId.deep.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' import { PostErrorComponent } from '~/components/PostErrorComponent' import { fetchPost } from '~/utils/posts' export const Route = createFileRoute('/posts_/$postId/deep')({ loader: async ({ params: { postId } }) => fetchPost({ data: postId }), errorComponent: PostErrorComponent, component: PostDeepComponent, }) function PostDeepComponent() { const post = Route.useLoaderData() return (
← All Posts

{post().title}

{post().body}
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/redirect.index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/redirect/')({ component: RouteComponent, }) function RouteComponent() { return (
Throw It
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/redirect.throw-it.tsx ================================================ import { createFileRoute, redirect } from '@tanstack/solid-router' export const Route = createFileRoute('/redirect/throw-it')({ beforeLoad: () => { throw redirect({ to: '/posts/$postId', params: { postId: '1' }, }) }, }) ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/users.$userId.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import axios from 'redaxios' import type { User } from '~/utils/users' import { NotFound } from '~/components/NotFound' import { UserErrorComponent } from '~/components/UserErrorComponent' import { basepath } from '~/utils/basepath' export const Route = createFileRoute('/users/$userId')({ loader: async ({ params: { userId } }) => { return await axios .get(basepath + '/api/users/' + userId) .then((r) => r.data) .catch(() => { throw new Error('Failed to fetch user') }) }, errorComponent: UserErrorComponent, component: UserComponent, notFoundComponent: () => { return User not found }, }) function UserComponent() { const user = Route.useLoaderData() return (

{user().name}

{user().email}
) } ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/users.index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/users/')({ component: UsersIndexComponent, }) function UsersIndexComponent() { return
Select a user.
} ================================================ FILE: e2e/solid-start/custom-basepath/src/routes/users.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' import axios from 'redaxios' import type { User } from '~/utils/users' import { basepath } from '~/utils/basepath' export const Route = createFileRoute('/users')({ loader: async () => { return await axios .get>(basepath + '/api/users') .then((r) => r.data) .catch(() => { throw new Error('Failed to fetch users') }) }, component: UsersComponent, }) function UsersComponent() { const users = Route.useLoaderData() return (
    {[ ...users(), { id: 'i-do-not-exist', name: 'Non-existent User', email: '' }, ].map((user) => { return (
  • {user.name}
  • ) })}

) } ================================================ FILE: e2e/solid-start/custom-basepath/src/server.ts ================================================ import handler from '@tanstack/solid-start/server-entry' export default { fetch(request: Request) { return handler.fetch(request) }, } ================================================ FILE: e2e/solid-start/custom-basepath/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/custom-basepath/src/utils/basepath.ts ================================================ export const basepath = '/custom/basepath' ================================================ FILE: e2e/solid-start/custom-basepath/src/utils/posts.tsx ================================================ import { notFound } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = createServerFn({ method: 'GET' }) .inputValidator((postId: string) => postId) .handler(async ({ data: postId }) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { console.error(err) if (err.status === 404) { throw notFound() } throw err }) return post }) export const fetchPosts = createServerFn({ method: 'GET' }).handler( async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) }, ) ================================================ FILE: e2e/solid-start/custom-basepath/src/utils/seo.ts ================================================ export const seo = ({ title, description, keywords, image, }: { title: string description?: string image?: string keywords?: string }) => { const tags = [ { title }, { name: 'description', content: description }, { name: 'keywords', content: keywords }, { name: 'twitter:title', content: title }, { name: 'twitter:description', content: description }, { name: 'twitter:creator', content: '@tannerlinsley' }, { name: 'twitter:site', content: '@tannerlinsley' }, { name: 'og:type', content: 'website' }, { name: 'og:title', content: title }, { name: 'og:description', content: description }, ...(image ? [ { name: 'twitter:image', content: image }, { name: 'twitter:card', content: 'summary_large_image' }, { name: 'og:image', content: image }, ] : []), ] return tags } ================================================ FILE: e2e/solid-start/custom-basepath/src/utils/users.tsx ================================================ export type User = { id: number name: string email: string } const PORT = process.env.VITE_SERVER_PORT || 3000 export const DEPLOY_URL = `http://localhost:${PORT}` ================================================ FILE: e2e/solid-start/custom-basepath/src/vite-env.d.ts ================================================ declare module '*?url' { const url: string export default url } ================================================ FILE: e2e/solid-start/custom-basepath/tests/navigation.spec.ts ================================================ import { expect, test } from '@playwright/test' test('Navigating to post', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await page.getByRole('link', { name: 'Deep View' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating to user', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Users' }).click() await page.getByRole('link', { name: 'Leanne Graham' }).click() await expect(page.getByRole('heading')).toContainText('Leanne Graham') }) test('Navigating to a not-found route', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test('Should change title on client side navigation', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await expect(page).toHaveTitle('Posts page') }) test('Server function URLs correctly include app basepath', async ({ page, }) => { await page.goto('/logout') const form = page.locator('form') const actionUrl = await form.getAttribute('action') expect(actionUrl).toMatch(/^\/custom\/basepath\/_serverFn\//) }) test('client-side redirect', async ({ page, baseURL }) => { await page.goto('/redirect') await page.getByTestId('link-to-throw-it').click() await page.waitForLoadState('networkidle') expect(await page.getByTestId('post-view').isVisible()).toBe(true) expect(page.url()).toBe(`${baseURL}/posts/1`) }) test('server-side redirect', async ({ page, baseURL }) => { await page.goto('/redirect/throw-it') await page.waitForLoadState('networkidle') expect(await page.getByTestId('post-view').isVisible()).toBe(true) expect(page.url()).toBe(`${baseURL}/posts/1`) // do not follow redirects since we want to test the Location header // first go to the route WITHOUT the base path, this will just add the base path await page.request .get('/redirect/throw-it', { maxRedirects: 0 }) .then((res) => { const headers = new Headers(res.headers()) expect(headers.get('location')).toBe('/custom/basepath/redirect/throw-it') }) await page.request .get('/custom/basepath/redirect/throw-it', { maxRedirects: 0 }) .then((res) => { const headers = new Headers(res.headers()) expect(headers.get('location')).toBe('/custom/basepath/posts/1') }) }) test('navigate() with href containing basepath', async ({ page, baseURL }) => { await page.goto('/navigate-test') await expect(page.getByTestId('navigate-test-component')).toBeVisible() const btn = page.getByTestId('to-posts-href-with-basepath-btn') await btn.click() // Should navigate to /custom/basepath/posts, NOT /custom/basepath/custom/basepath/posts await page.waitForURL(`${baseURL}/posts`) await expect(page.getByTestId('posts-component')).toBeVisible() }) test('navigate() with href containing basepath and reloadDocument=true', async ({ page, baseURL, }) => { await page.goto('/navigate-test') await expect(page.getByTestId('navigate-test-component')).toBeVisible() const btn = page.getByTestId('to-posts-href-with-basepath-reload-btn') await btn.click() // Should navigate to /custom/basepath/posts, NOT stay on current page await page.waitForURL(`${baseURL}/posts`) await expect(page.getByTestId('posts-component')).toBeVisible() }) ================================================ FILE: e2e/solid-start/custom-basepath/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/custom-basepath/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/custom-basepath/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true, "types": ["vite/client"] } } ================================================ FILE: e2e/solid-start/custom-basepath/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, base: '/custom/basepath', server: { port: 3000, }, plugins: [tailwindcss(), tanstackStart(), viteSolid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/query-integration/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/query-integration/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/query-integration/package.json ================================================ { "name": "tanstack-solid-start-e2e-query-integration", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-router-ssr-query": "workspace:^", "@tanstack/solid-start": "workspace:^", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", "vite": "^8.0.0", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/query-integration/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/query-integration/src/queryOptions.ts ================================================ import { queryOptions } from '@tanstack/solid-query' export const makeQueryOptions = (key: string) => queryOptions({ queryKey: ['e2e-test-query-integration', key], queryFn: async () => { console.log('fetching query data') await new Promise((resolve) => { setTimeout(resolve, 500) }) const result = typeof window !== 'undefined' ? 'client' : 'server' console.log('query data result', result) return result }, staleTime: Infinity, }) ================================================ FILE: e2e/solid-start/query-integration/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as UseQueryRouteImport } from './routes/useQuery' import { Route as IndexRouteImport } from './routes/index' import { Route as LoaderFetchQueryTypeRouteImport } from './routes/loader-fetchQuery/$type' const UseQueryRoute = UseQueryRouteImport.update({ id: '/useQuery', path: '/useQuery', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const LoaderFetchQueryTypeRoute = LoaderFetchQueryTypeRouteImport.update({ id: '/loader-fetchQuery/$type', path: '/loader-fetchQuery/$type', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/useQuery': typeof UseQueryRoute '/loader-fetchQuery/$type': typeof LoaderFetchQueryTypeRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/useQuery': typeof UseQueryRoute '/loader-fetchQuery/$type': typeof LoaderFetchQueryTypeRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/useQuery': typeof UseQueryRoute '/loader-fetchQuery/$type': typeof LoaderFetchQueryTypeRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/useQuery' | '/loader-fetchQuery/$type' fileRoutesByTo: FileRoutesByTo to: '/' | '/useQuery' | '/loader-fetchQuery/$type' id: '__root__' | '/' | '/useQuery' | '/loader-fetchQuery/$type' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute UseQueryRoute: typeof UseQueryRoute LoaderFetchQueryTypeRoute: typeof LoaderFetchQueryTypeRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/useQuery': { id: '/useQuery' path: '/useQuery' fullPath: '/useQuery' preLoaderRoute: typeof UseQueryRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/loader-fetchQuery/$type': { id: '/loader-fetchQuery/$type' path: '/loader-fetchQuery/$type' fullPath: '/loader-fetchQuery/$type' preLoaderRoute: typeof LoaderFetchQueryTypeRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, UseQueryRoute: UseQueryRoute, LoaderFetchQueryTypeRoute: LoaderFetchQueryTypeRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/query-integration/src/router.tsx ================================================ import { QueryClient } from '@tanstack/solid-query' import { createRouter } from '@tanstack/solid-router' import { setupRouterSsrQueryIntegration } from '@tanstack/solid-router-ssr-query' import { routeTree } from './routeTree.gen' export function getRouter() { const queryClient = new QueryClient() const router = createRouter({ routeTree, context: { queryClient }, scrollRestoration: true, defaultPreload: 'intent', }) setupRouterSsrQueryIntegration({ router, queryClient, }) return router } ================================================ FILE: e2e/solid-start/query-integration/src/routes/__root.tsx ================================================ /// import { HeadContent, Link, Scripts, createRootRouteWithContext, } from '@tanstack/solid-router' import { TanStackRouterDevtoolsInProd } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' import type { QueryClient } from '@tanstack/solid-query' import appCss from '~/styles/app.css?url' export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({ head: () => ({ meta: [ { charset: 'utf-8', }, ], links: [{ rel: 'stylesheet', href: appCss }], }), shellComponent: RootDocument, }) function RootDocument(props: { children?: any }) { return (
Home {' '} fetchQuery (sync) {' '} fetchQuery (async) {' '} useQuery {' '} {/* useSuspenseQuery {' '} */}

{props.children} ) } ================================================ FILE: e2e/solid-start/query-integration/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Query Integration E2E tests

) } ================================================ FILE: e2e/solid-start/query-integration/src/routes/loader-fetchQuery/$type.tsx ================================================ import { useQuery } from '@tanstack/solid-query' import { createFileRoute } from '@tanstack/solid-router' import z from 'zod' import { makeQueryOptions } from '~/queryOptions' export const Route = createFileRoute('/loader-fetchQuery/$type')({ component: RouteComponent, params: { parse: ({ type }) => z .object({ type: z.union([z.literal('sync'), z.literal('async')]), }) .parse({ type }), }, context: ({ params }) => ({ queryOptions: makeQueryOptions(`loader-fetchQuery-${params.type}`), }), loader: ({ context, params }) => { const queryPromise = context.queryClient.fetchQuery(context.queryOptions) if (params.type === 'sync') { return queryPromise } }, ssr: 'data-only', }) function RouteComponent() { const loaderData = Route.useLoaderData() const context = Route.useRouteContext() const query = useQuery(() => context().queryOptions) return (
loader data:{' '}
{loaderData() ?? 'undefined'}
query data:{' '}
{query.data ?? 'loading...'}
) } ================================================ FILE: e2e/solid-start/query-integration/src/routes/useQuery.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { useQuery } from '@tanstack/solid-query' import { makeQueryOptions } from '~/queryOptions' const qOptions = makeQueryOptions('useQuery') export const Route = createFileRoute('/useQuery')({ loader: ({ context }) => context.queryClient.ensureQueryData(qOptions), component: RouteComponent, ssr: true, }) function RouteComponent() { const query = useQuery(() => qOptions) return (
query data:{' '}
{query.data ?? 'loading...'}
) } ================================================ FILE: e2e/solid-start/query-integration/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } body { @apply text-base; font-family: system-ui, sans-serif; } ================================================ FILE: e2e/solid-start/query-integration/tests/app.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' test.describe('queries are streamed from the server', () => { test('direct visit - loader on server runs fetchQuery and awaits it', async ({ page, }) => { await page.goto('/loader-fetchQuery/sync') const queryData = page.getByTestId('query-data') await expect(queryData).toHaveText('server') const loaderData = page.getByTestId('loader-data') await expect(loaderData).toHaveText('server') }) test('direct visit - loader on server runs fetchQuery and does not await it', async ({ page, }) => { await page.goto('/loader-fetchQuery/async') const queryData = page.getByTestId('query-data') await expect(queryData).toHaveText('server') const loaderData = page.getByTestId('loader-data') await expect(loaderData).toHaveText('undefined') }) test('useQuery', async ({ page }) => { await page.goto('/useQuery') const queryData = page.getByTestId('query-data') await expect(queryData).toHaveText('server') }) }) ================================================ FILE: e2e/solid-start/query-integration/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true, "types": ["vite/client"] } } ================================================ FILE: e2e/solid-start/query-integration/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import solid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, plugins: [tailwindcss(), tanstackStart(), solid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/scroll-restoration/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/scroll-restoration/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/solid-start/scroll-restoration/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/scroll-restoration/package.json ================================================ { "name": "tanstack-solid-start-e2e-basic-scroll-restoration", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-start": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", "vite": "^8.0.0", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "combinate": "^1.1.11", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/scroll-restoration/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/scroll-restoration/public/script.js ================================================ console.log('SCRIPT_1 loaded') window.SCRIPT_1 = true ================================================ FILE: e2e/solid-start/scroll-restoration/public/script2.js ================================================ console.log('SCRIPT_2 loaded') window.SCRIPT_2 = true ================================================ FILE: e2e/solid-start/scroll-restoration/public/site.webmanifest ================================================ { "name": "", "short_name": "", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } ================================================ FILE: e2e/solid-start/scroll-restoration/src/components/DefaultCatchBoundary.tsx ================================================ import { ErrorComponent, Link, rootRouteId, useMatch, useRouter, } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter() const isRoot = useMatch({ strict: false, select: (state) => state.id === rootRouteId, }) console.error(error) return (
{isRoot() ? ( Home ) : ( { e.preventDefault() window.history.back() }} > Go Back )}
) } ================================================ FILE: e2e/solid-start/scroll-restoration/src/components/NotFound.tsx ================================================ import { Link } from '@tanstack/solid-router' export function NotFound({ children }: { children?: any }) { return (
{children ||

The page you are looking for does not exist.

}

Start Over

) } ================================================ FILE: e2e/solid-start/scroll-restoration/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const testsWithSearchRoute = testsWithSearchRouteImport.update({ id: '/(tests)/with-search', path: '/with-search', getParentRoute: () => rootRouteImport, } as any) const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ id: '/(tests)/with-loader', path: '/with-loader', getParentRoute: () => rootRouteImport, } as any) const testsNormalPageRoute = testsNormalPageRouteImport.update({ id: '/(tests)/normal-page', path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/normal-page': typeof testsNormalPageRoute '/with-loader': typeof testsWithLoaderRoute '/with-search': typeof testsWithSearchRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/normal-page': typeof testsNormalPageRoute '/with-loader': typeof testsWithLoaderRoute '/with-search': typeof testsWithSearchRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/(tests)/normal-page': typeof testsNormalPageRoute '/(tests)/with-loader': typeof testsWithLoaderRoute '/(tests)/with-search': typeof testsWithSearchRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/normal-page' | '/with-loader' | '/with-search' fileRoutesByTo: FileRoutesByTo to: '/' | '/normal-page' | '/with-loader' | '/with-search' id: | '__root__' | '/' | '/(tests)/normal-page' | '/(tests)/with-loader' | '/(tests)/with-search' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute testsNormalPageRoute: typeof testsNormalPageRoute testsWithLoaderRoute: typeof testsWithLoaderRoute testsWithSearchRoute: typeof testsWithSearchRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/(tests)/with-search': { id: '/(tests)/with-search' path: '/with-search' fullPath: '/with-search' preLoaderRoute: typeof testsWithSearchRouteImport parentRoute: typeof rootRouteImport } '/(tests)/with-loader': { id: '/(tests)/with-loader' path: '/with-loader' fullPath: '/with-loader' preLoaderRoute: typeof testsWithLoaderRouteImport parentRoute: typeof rootRouteImport } '/(tests)/normal-page': { id: '/(tests)/normal-page' path: '/normal-page' fullPath: '/normal-page' preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, testsNormalPageRoute: testsNormalPageRoute, testsWithLoaderRoute: testsWithLoaderRoute, testsWithSearchRoute: testsWithSearchRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/scroll-restoration/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, defaultPreload: 'intent', defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , }) return router } ================================================ FILE: e2e/solid-start/scroll-restoration/src/routes/(tests)/normal-page.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as Solid from 'solid-js' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/normal-page')({ component: Component, }) function Component() { return (

normal-page


) } ================================================ FILE: e2e/solid-start/scroll-restoration/src/routes/(tests)/with-loader.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { sleep } from 'src/utils/posts' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/with-loader')({ loader: async () => { await sleep(1000) return { foo: 'bar' } }, component: Component, }) function Component() { return (

lazy-with-loader-page


) } ================================================ FILE: e2e/solid-start/scroll-restoration/src/routes/(tests)/with-search.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/with-search')({ validateSearch: zodValidator(z.object({ where: z.string() })), component: Component, }) function Component() { return (

page-with-search


) } ================================================ FILE: e2e/solid-start/scroll-restoration/src/routes/-components/scroll-block.tsx ================================================ export const atTheTopId = 'at-the-top' export const atTheBottomId = 'at-the-bottom' export function ScrollBlock({ number = 100 }: { number?: number }) { return ( <>
{Array.from({ length: number }).map((_, i) => (
{i}
))}
At the bottom
) } ================================================ FILE: e2e/solid-start/scroll-restoration/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, Scripts, createRootRoute, linkOptions, } from '@tanstack/solid-router' import { Dynamic, HydrationScript } from 'solid-js/web' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import { NotFound } from '~/components/NotFound' import appCss from '~/styles/app.css?url' import { seo } from '~/utils/seo' export const Route = createRootRoute({ head: () => ({ meta: [ { charset: 'utf-8', }, { name: 'viewport', content: 'width=device-width, initial-scale=1', }, ...seo({ title: 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, }), ], links: [ { rel: 'stylesheet', href: appCss }, { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png', }, { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png', }, { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png', }, { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, { rel: 'icon', href: '/favicon.ico' }, ], }), errorComponent: (props) => { return

{props.error.stack}

}, notFoundComponent: () => , component: RootComponent, }) function RootComponent() { return (
) } ================================================ FILE: e2e/solid-start/serialization-adapters/src/start.tsx ================================================ import { createStart } from '@tanstack/solid-start' import { carAdapter, fooAdapter, nestedOuterAdapter } from './data' import { customErrorAdapter } from './CustomError' export const startInstance = createStart(() => { return { defaultSsr: true, serializationAdapters: [ fooAdapter, carAdapter, customErrorAdapter, // only register nestedOuterAdapter here, nestedInnerAdapter is registered as an "extends" of nestedOuterAdapter nestedOuterAdapter, ], } }) ================================================ FILE: e2e/solid-start/serialization-adapters/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); /* The default border color has changed to `currentcolor` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. If we ever want to remove these styles, we need to add an explicit border color utility to any element that depends on these defaults. */ @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/serialization-adapters/tests/app.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' import type { Page } from '@playwright/test' async function awaitPageLoaded(page: Page) { // wait for page to be loaded by waiting for the ClientOnly component to be rendered await expect(page.getByTestId('router-isLoading')).toContainText('false') await expect(page.getByTestId('router-status')).toContainText('idle') } async function checkData(page: Page, id: string) { const expectedData = await page .getByTestId(`${id}-car-expected`) .textContent() expect(expectedData).not.toBeNull() await expect(page.getByTestId(`${id}-car-actual`)).toContainText( expectedData!, ) await expect(page.getByTestId(`${id}-foo`)).toContainText( '{"value":"server"}', ) } async function checkNestedData(page: Page) { const expectedShout = await page .getByTestId(`shout-expected-state`) .textContent() expect(expectedShout).not.toBeNull() await expect(page.getByTestId(`shout-actual-state`)).toContainText( expectedShout!, ) const expectedWhisper = await page .getByTestId(`whisper-expected-state`) .textContent() expect(expectedWhisper).not.toBeNull() await expect(page.getByTestId(`whisper-actual-state`)).toContainText( expectedWhisper!, ) } test.use({ whitelistErrors: [ 'Failed to load resource: the server responded with a status of 499', ], }) test.describe('SSR serialization adapters', () => { test(`data-only`, async ({ page }) => { await page.goto('/ssr/data-only') await awaitPageLoaded(page) await Promise.all( ['context', 'loader'].map(async (id) => checkData(page, id)), ) const expectedHonkData = await page .getByTestId('honk-expected-state') .textContent() expect(expectedHonkData).not.toBeNull() await expect(page.getByTestId('honk-actual-state')).toContainText( expectedHonkData!, ) }) test('stream', async ({ page }) => { await page.goto('/ssr/stream') await awaitPageLoaded(page) await checkData(page, 'stream') }) test('nested', async ({ page }) => { await page.goto('/ssr/nested') await awaitPageLoaded(page) await checkNestedData(page) }) }) test.describe('server functions serialization adapters', () => { test('custom error', async ({ page }) => { await page.goto('/server-function/custom-error') await awaitPageLoaded(page) await expect( page.getByTestId('server-function-valid-response'), ).toContainText('null') await expect( page.getByTestId('server-function-invalid-response'), ).toContainText('null') await page.getByTestId('server-function-valid-input').click() await expect( page.getByTestId('server-function-valid-response'), ).toContainText('Hello, world!') await page.getByTestId('server-function-invalid-input').click() await expect( page.getByTestId('server-function-invalid-response'), ).toContainText('{"message":"Invalid input","foo":"bar","bar":"123"}') }) test('nested', async ({ page }) => { await page.goto('/server-function/nested') await awaitPageLoaded(page) await expect(page.getByTestId('waiting-for-response')).toContainText( 'waiting for response...', ) await page.getByTestId('server-function-trigger').click() await checkNestedData(page) }) }) ================================================ FILE: e2e/solid-start/serialization-adapters/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/serialization-adapters/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [tailwindcss(), tanstackStart(), viteSolid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/server-functions/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/server-functions/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/solid-start/server-functions/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/server-functions/package.json ================================================ { "name": "tanstack-solid-start-e2e-server-functions", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-router-ssr-query": "workspace:^", "@tanstack/solid-start": "workspace:^", "js-cookie": "^3.0.5", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", "vite": "^8.0.0", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", "@types/node": "^22.10.2", "combinate": "^1.1.11", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/server-functions/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } export const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `pnpm build && VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/server-functions/src/components/DefaultCatchBoundary.tsx ================================================ import { ErrorComponent, Link, rootRouteId, useMatch, useRouter, } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter() const isRoot = useMatch({ strict: false, select: (state) => state.id === rootRouteId, }) console.error(error) return (
{isRoot() ? ( Home ) : ( { e.preventDefault() window.history.back() }} > Go Back )}
) } ================================================ FILE: e2e/solid-start/server-functions/src/components/NotFound.tsx ================================================ import { Link } from '@tanstack/solid-router' export function NotFound({ children }: { children?: any }) { return (
{children ||

The page you are looking for does not exist.

}

Start Over

) } ================================================ FILE: e2e/solid-start/server-functions/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' import { Route as StatusRouteImport } from './routes/status' import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' import { Route as ReturnNullRouteImport } from './routes/return-null' import { Route as RawResponseRouteImport } from './routes/raw-response' import { Route as MultipartRouteImport } from './routes/multipart' import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' import { Route as HeadersRouteImport } from './routes/headers' import { Route as EnvOnlyRouteImport } from './routes/env-only' import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' import { Route as ConsistentRouteImport } from './routes/consistent' import { Route as IndexRouteImport } from './routes/index' import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' import { Route as FactoryIndexRouteImport } from './routes/factory/index' import { Route as CookiesIndexRouteImport } from './routes/cookies/index' import { Route as AbortSignalIndexRouteImport } from './routes/abort-signal/index' import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' import { Route as CookiesSetRouteImport } from './routes/cookies/set' import { Route as AbortSignalMethodRouteImport } from './routes/abort-signal/$method' import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ id: '/submit-post-formdata', path: '/submit-post-formdata', getParentRoute: () => rootRouteImport, } as any) const StatusRoute = StatusRouteImport.update({ id: '/status', path: '/status', getParentRoute: () => rootRouteImport, } as any) const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ id: '/serialize-form-data', path: '/serialize-form-data', getParentRoute: () => rootRouteImport, } as any) const ReturnNullRoute = ReturnNullRouteImport.update({ id: '/return-null', path: '/return-null', getParentRoute: () => rootRouteImport, } as any) const RawResponseRoute = RawResponseRouteImport.update({ id: '/raw-response', path: '/raw-response', getParentRoute: () => rootRouteImport, } as any) const MultipartRoute = MultipartRouteImport.update({ id: '/multipart', path: '/multipart', getParentRoute: () => rootRouteImport, } as any) const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ id: '/isomorphic-fns', path: '/isomorphic-fns', getParentRoute: () => rootRouteImport, } as any) const HeadersRoute = HeadersRouteImport.update({ id: '/headers', path: '/headers', getParentRoute: () => rootRouteImport, } as any) const EnvOnlyRoute = EnvOnlyRouteImport.update({ id: '/env-only', path: '/env-only', getParentRoute: () => rootRouteImport, } as any) const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ id: '/dead-code-preserve', path: '/dead-code-preserve', getParentRoute: () => rootRouteImport, } as any) const ConsistentRoute = ConsistentRouteImport.update({ id: '/consistent', path: '/consistent', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ id: '/redirect-test/', path: '/redirect-test/', getParentRoute: () => rootRouteImport, } as any) const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ id: '/redirect-test-ssr/', path: '/redirect-test-ssr/', getParentRoute: () => rootRouteImport, } as any) const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ id: '/primitives/', path: '/primitives/', getParentRoute: () => rootRouteImport, } as any) const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ id: '/middleware/', path: '/middleware/', getParentRoute: () => rootRouteImport, } as any) const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ id: '/function-method/', path: '/function-method/', getParentRoute: () => rootRouteImport, } as any) const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ id: '/function-metadata/', path: '/function-metadata/', getParentRoute: () => rootRouteImport, } as any) const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ id: '/formdata-redirect/', path: '/formdata-redirect/', getParentRoute: () => rootRouteImport, } as any) const FactoryIndexRoute = FactoryIndexRouteImport.update({ id: '/factory/', path: '/factory/', getParentRoute: () => rootRouteImport, } as any) const CookiesIndexRoute = CookiesIndexRouteImport.update({ id: '/cookies/', path: '/cookies/', getParentRoute: () => rootRouteImport, } as any) const AbortSignalIndexRoute = AbortSignalIndexRouteImport.update({ id: '/abort-signal/', path: '/abort-signal/', getParentRoute: () => rootRouteImport, } as any) const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ id: '/redirect-test/target', path: '/redirect-test/target', getParentRoute: () => rootRouteImport, } as any) const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ id: '/redirect-test-ssr/target', path: '/redirect-test-ssr/target', getParentRoute: () => rootRouteImport, } as any) const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ id: '/middleware/send-serverFn', path: '/middleware/send-serverFn', getParentRoute: () => rootRouteImport, } as any) const MiddlewareRequestMiddlewareRoute = MiddlewareRequestMiddlewareRouteImport.update({ id: '/middleware/request-middleware', path: '/middleware/request-middleware', getParentRoute: () => rootRouteImport, } as any) const MiddlewareClientMiddlewareRouterRoute = MiddlewareClientMiddlewareRouterRouteImport.update({ id: '/middleware/client-middleware-router', path: '/middleware/client-middleware-router', getParentRoute: () => rootRouteImport, } as any) const CookiesSetRoute = CookiesSetRouteImport.update({ id: '/cookies/set', path: '/cookies/set', getParentRoute: () => rootRouteImport, } as any) const AbortSignalMethodRoute = AbortSignalMethodRouteImport.update({ id: '/abort-signal/$method', path: '/abort-signal/$method', getParentRoute: () => rootRouteImport, } as any) const FormdataRedirectTargetNameRoute = FormdataRedirectTargetNameRouteImport.update({ id: '/formdata-redirect/target/$name', path: '/formdata-redirect/target/$name', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/consistent': typeof ConsistentRoute '/dead-code-preserve': typeof DeadCodePreserveRoute '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute '/serialize-form-data': typeof SerializeFormDataRoute '/status': typeof StatusRoute '/submit-post-formdata': typeof SubmitPostFormdataRoute '/abort-signal/$method': typeof AbortSignalMethodRoute '/cookies/set': typeof CookiesSetRoute '/middleware/client-middleware-router': typeof MiddlewareClientMiddlewareRouterRoute '/middleware/request-middleware': typeof MiddlewareRequestMiddlewareRoute '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/redirect-test-ssr/target': typeof RedirectTestSsrTargetRoute '/redirect-test/target': typeof RedirectTestTargetRoute '/abort-signal/': typeof AbortSignalIndexRoute '/cookies/': typeof CookiesIndexRoute '/factory/': typeof FactoryIndexRoute '/formdata-redirect/': typeof FormdataRedirectIndexRoute '/function-metadata/': typeof FunctionMetadataIndexRoute '/function-method/': typeof FunctionMethodIndexRoute '/middleware/': typeof MiddlewareIndexRoute '/primitives/': typeof PrimitivesIndexRoute '/redirect-test-ssr/': typeof RedirectTestSsrIndexRoute '/redirect-test/': typeof RedirectTestIndexRoute '/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/consistent': typeof ConsistentRoute '/dead-code-preserve': typeof DeadCodePreserveRoute '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute '/serialize-form-data': typeof SerializeFormDataRoute '/status': typeof StatusRoute '/submit-post-formdata': typeof SubmitPostFormdataRoute '/abort-signal/$method': typeof AbortSignalMethodRoute '/cookies/set': typeof CookiesSetRoute '/middleware/client-middleware-router': typeof MiddlewareClientMiddlewareRouterRoute '/middleware/request-middleware': typeof MiddlewareRequestMiddlewareRoute '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/redirect-test-ssr/target': typeof RedirectTestSsrTargetRoute '/redirect-test/target': typeof RedirectTestTargetRoute '/abort-signal': typeof AbortSignalIndexRoute '/cookies': typeof CookiesIndexRoute '/factory': typeof FactoryIndexRoute '/formdata-redirect': typeof FormdataRedirectIndexRoute '/function-metadata': typeof FunctionMetadataIndexRoute '/function-method': typeof FunctionMethodIndexRoute '/middleware': typeof MiddlewareIndexRoute '/primitives': typeof PrimitivesIndexRoute '/redirect-test-ssr': typeof RedirectTestSsrIndexRoute '/redirect-test': typeof RedirectTestIndexRoute '/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/consistent': typeof ConsistentRoute '/dead-code-preserve': typeof DeadCodePreserveRoute '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute '/serialize-form-data': typeof SerializeFormDataRoute '/status': typeof StatusRoute '/submit-post-formdata': typeof SubmitPostFormdataRoute '/abort-signal/$method': typeof AbortSignalMethodRoute '/cookies/set': typeof CookiesSetRoute '/middleware/client-middleware-router': typeof MiddlewareClientMiddlewareRouterRoute '/middleware/request-middleware': typeof MiddlewareRequestMiddlewareRoute '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/redirect-test-ssr/target': typeof RedirectTestSsrTargetRoute '/redirect-test/target': typeof RedirectTestTargetRoute '/abort-signal/': typeof AbortSignalIndexRoute '/cookies/': typeof CookiesIndexRoute '/factory/': typeof FactoryIndexRoute '/formdata-redirect/': typeof FormdataRedirectIndexRoute '/function-metadata/': typeof FunctionMetadataIndexRoute '/function-method/': typeof FunctionMethodIndexRoute '/middleware/': typeof MiddlewareIndexRoute '/primitives/': typeof PrimitivesIndexRoute '/redirect-test-ssr/': typeof RedirectTestSsrIndexRoute '/redirect-test/': typeof RedirectTestIndexRoute '/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/consistent' | '/dead-code-preserve' | '/env-only' | '/headers' | '/isomorphic-fns' | '/multipart' | '/raw-response' | '/return-null' | '/serialize-form-data' | '/status' | '/submit-post-formdata' | '/abort-signal/$method' | '/cookies/set' | '/middleware/client-middleware-router' | '/middleware/request-middleware' | '/middleware/send-serverFn' | '/redirect-test-ssr/target' | '/redirect-test/target' | '/abort-signal/' | '/cookies/' | '/factory/' | '/formdata-redirect/' | '/function-metadata/' | '/function-method/' | '/middleware/' | '/primitives/' | '/redirect-test-ssr/' | '/redirect-test/' | '/formdata-redirect/target/$name' fileRoutesByTo: FileRoutesByTo to: | '/' | '/consistent' | '/dead-code-preserve' | '/env-only' | '/headers' | '/isomorphic-fns' | '/multipart' | '/raw-response' | '/return-null' | '/serialize-form-data' | '/status' | '/submit-post-formdata' | '/abort-signal/$method' | '/cookies/set' | '/middleware/client-middleware-router' | '/middleware/request-middleware' | '/middleware/send-serverFn' | '/redirect-test-ssr/target' | '/redirect-test/target' | '/abort-signal' | '/cookies' | '/factory' | '/formdata-redirect' | '/function-metadata' | '/function-method' | '/middleware' | '/primitives' | '/redirect-test-ssr' | '/redirect-test' | '/formdata-redirect/target/$name' id: | '__root__' | '/' | '/consistent' | '/dead-code-preserve' | '/env-only' | '/headers' | '/isomorphic-fns' | '/multipart' | '/raw-response' | '/return-null' | '/serialize-form-data' | '/status' | '/submit-post-formdata' | '/abort-signal/$method' | '/cookies/set' | '/middleware/client-middleware-router' | '/middleware/request-middleware' | '/middleware/send-serverFn' | '/redirect-test-ssr/target' | '/redirect-test/target' | '/abort-signal/' | '/cookies/' | '/factory/' | '/formdata-redirect/' | '/function-metadata/' | '/function-method/' | '/middleware/' | '/primitives/' | '/redirect-test-ssr/' | '/redirect-test/' | '/formdata-redirect/target/$name' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute ConsistentRoute: typeof ConsistentRoute DeadCodePreserveRoute: typeof DeadCodePreserveRoute EnvOnlyRoute: typeof EnvOnlyRoute HeadersRoute: typeof HeadersRoute IsomorphicFnsRoute: typeof IsomorphicFnsRoute MultipartRoute: typeof MultipartRoute RawResponseRoute: typeof RawResponseRoute ReturnNullRoute: typeof ReturnNullRoute SerializeFormDataRoute: typeof SerializeFormDataRoute StatusRoute: typeof StatusRoute SubmitPostFormdataRoute: typeof SubmitPostFormdataRoute AbortSignalMethodRoute: typeof AbortSignalMethodRoute CookiesSetRoute: typeof CookiesSetRoute MiddlewareClientMiddlewareRouterRoute: typeof MiddlewareClientMiddlewareRouterRoute MiddlewareRequestMiddlewareRoute: typeof MiddlewareRequestMiddlewareRoute MiddlewareSendServerFnRoute: typeof MiddlewareSendServerFnRoute RedirectTestSsrTargetRoute: typeof RedirectTestSsrTargetRoute RedirectTestTargetRoute: typeof RedirectTestTargetRoute AbortSignalIndexRoute: typeof AbortSignalIndexRoute CookiesIndexRoute: typeof CookiesIndexRoute FactoryIndexRoute: typeof FactoryIndexRoute FormdataRedirectIndexRoute: typeof FormdataRedirectIndexRoute FunctionMetadataIndexRoute: typeof FunctionMetadataIndexRoute FunctionMethodIndexRoute: typeof FunctionMethodIndexRoute MiddlewareIndexRoute: typeof MiddlewareIndexRoute PrimitivesIndexRoute: typeof PrimitivesIndexRoute RedirectTestSsrIndexRoute: typeof RedirectTestSsrIndexRoute RedirectTestIndexRoute: typeof RedirectTestIndexRoute FormdataRedirectTargetNameRoute: typeof FormdataRedirectTargetNameRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/submit-post-formdata': { id: '/submit-post-formdata' path: '/submit-post-formdata' fullPath: '/submit-post-formdata' preLoaderRoute: typeof SubmitPostFormdataRouteImport parentRoute: typeof rootRouteImport } '/status': { id: '/status' path: '/status' fullPath: '/status' preLoaderRoute: typeof StatusRouteImport parentRoute: typeof rootRouteImport } '/serialize-form-data': { id: '/serialize-form-data' path: '/serialize-form-data' fullPath: '/serialize-form-data' preLoaderRoute: typeof SerializeFormDataRouteImport parentRoute: typeof rootRouteImport } '/return-null': { id: '/return-null' path: '/return-null' fullPath: '/return-null' preLoaderRoute: typeof ReturnNullRouteImport parentRoute: typeof rootRouteImport } '/raw-response': { id: '/raw-response' path: '/raw-response' fullPath: '/raw-response' preLoaderRoute: typeof RawResponseRouteImport parentRoute: typeof rootRouteImport } '/multipart': { id: '/multipart' path: '/multipart' fullPath: '/multipart' preLoaderRoute: typeof MultipartRouteImport parentRoute: typeof rootRouteImport } '/isomorphic-fns': { id: '/isomorphic-fns' path: '/isomorphic-fns' fullPath: '/isomorphic-fns' preLoaderRoute: typeof IsomorphicFnsRouteImport parentRoute: typeof rootRouteImport } '/headers': { id: '/headers' path: '/headers' fullPath: '/headers' preLoaderRoute: typeof HeadersRouteImport parentRoute: typeof rootRouteImport } '/env-only': { id: '/env-only' path: '/env-only' fullPath: '/env-only' preLoaderRoute: typeof EnvOnlyRouteImport parentRoute: typeof rootRouteImport } '/dead-code-preserve': { id: '/dead-code-preserve' path: '/dead-code-preserve' fullPath: '/dead-code-preserve' preLoaderRoute: typeof DeadCodePreserveRouteImport parentRoute: typeof rootRouteImport } '/consistent': { id: '/consistent' path: '/consistent' fullPath: '/consistent' preLoaderRoute: typeof ConsistentRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/redirect-test/': { id: '/redirect-test/' path: '/redirect-test' fullPath: '/redirect-test/' preLoaderRoute: typeof RedirectTestIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect-test-ssr/': { id: '/redirect-test-ssr/' path: '/redirect-test-ssr' fullPath: '/redirect-test-ssr/' preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } '/primitives/': { id: '/primitives/' path: '/primitives' fullPath: '/primitives/' preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } '/middleware/': { id: '/middleware/' path: '/middleware' fullPath: '/middleware/' preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } '/function-method/': { id: '/function-method/' path: '/function-method' fullPath: '/function-method/' preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } '/function-metadata/': { id: '/function-metadata/' path: '/function-metadata' fullPath: '/function-metadata/' preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/': { id: '/formdata-redirect/' path: '/formdata-redirect' fullPath: '/formdata-redirect/' preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/factory/': { id: '/factory/' path: '/factory' fullPath: '/factory/' preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } '/cookies/': { id: '/cookies/' path: '/cookies' fullPath: '/cookies/' preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } '/abort-signal/': { id: '/abort-signal/' path: '/abort-signal' fullPath: '/abort-signal/' preLoaderRoute: typeof AbortSignalIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect-test/target': { id: '/redirect-test/target' path: '/redirect-test/target' fullPath: '/redirect-test/target' preLoaderRoute: typeof RedirectTestTargetRouteImport parentRoute: typeof rootRouteImport } '/redirect-test-ssr/target': { id: '/redirect-test-ssr/target' path: '/redirect-test-ssr/target' fullPath: '/redirect-test-ssr/target' preLoaderRoute: typeof RedirectTestSsrTargetRouteImport parentRoute: typeof rootRouteImport } '/middleware/send-serverFn': { id: '/middleware/send-serverFn' path: '/middleware/send-serverFn' fullPath: '/middleware/send-serverFn' preLoaderRoute: typeof MiddlewareSendServerFnRouteImport parentRoute: typeof rootRouteImport } '/middleware/request-middleware': { id: '/middleware/request-middleware' path: '/middleware/request-middleware' fullPath: '/middleware/request-middleware' preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport parentRoute: typeof rootRouteImport } '/middleware/client-middleware-router': { id: '/middleware/client-middleware-router' path: '/middleware/client-middleware-router' fullPath: '/middleware/client-middleware-router' preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport parentRoute: typeof rootRouteImport } '/cookies/set': { id: '/cookies/set' path: '/cookies/set' fullPath: '/cookies/set' preLoaderRoute: typeof CookiesSetRouteImport parentRoute: typeof rootRouteImport } '/abort-signal/$method': { id: '/abort-signal/$method' path: '/abort-signal/$method' fullPath: '/abort-signal/$method' preLoaderRoute: typeof AbortSignalMethodRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/target/$name': { id: '/formdata-redirect/target/$name' path: '/formdata-redirect/target/$name' fullPath: '/formdata-redirect/target/$name' preLoaderRoute: typeof FormdataRedirectTargetNameRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, ConsistentRoute: ConsistentRoute, DeadCodePreserveRoute: DeadCodePreserveRoute, EnvOnlyRoute: EnvOnlyRoute, HeadersRoute: HeadersRoute, IsomorphicFnsRoute: IsomorphicFnsRoute, MultipartRoute: MultipartRoute, RawResponseRoute: RawResponseRoute, ReturnNullRoute: ReturnNullRoute, SerializeFormDataRoute: SerializeFormDataRoute, StatusRoute: StatusRoute, SubmitPostFormdataRoute: SubmitPostFormdataRoute, AbortSignalMethodRoute: AbortSignalMethodRoute, CookiesSetRoute: CookiesSetRoute, MiddlewareClientMiddlewareRouterRoute: MiddlewareClientMiddlewareRouterRoute, MiddlewareRequestMiddlewareRoute: MiddlewareRequestMiddlewareRoute, MiddlewareSendServerFnRoute: MiddlewareSendServerFnRoute, RedirectTestSsrTargetRoute: RedirectTestSsrTargetRoute, RedirectTestTargetRoute: RedirectTestTargetRoute, AbortSignalIndexRoute: AbortSignalIndexRoute, CookiesIndexRoute: CookiesIndexRoute, FactoryIndexRoute: FactoryIndexRoute, FormdataRedirectIndexRoute: FormdataRedirectIndexRoute, FunctionMetadataIndexRoute: FunctionMetadataIndexRoute, FunctionMethodIndexRoute: FunctionMethodIndexRoute, MiddlewareIndexRoute: MiddlewareIndexRoute, PrimitivesIndexRoute: PrimitivesIndexRoute, RedirectTestSsrIndexRoute: RedirectTestSsrIndexRoute, RedirectTestIndexRoute: RedirectTestIndexRoute, FormdataRedirectTargetNameRoute: FormdataRedirectTargetNameRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/server-functions/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { setupRouterSsrQueryIntegration } from '@tanstack/solid-router-ssr-query' import { QueryClient } from '@tanstack/solid-query' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' export function getRouter() { const queryClient = new QueryClient() const router = createRouter({ routeTree, defaultPreload: 'intent', defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , scrollRestoration: true, context: { foo: { bar: 'baz', }, }, }) setupRouterSsrQueryIntegration({ router, queryClient }) return router } declare module '@tanstack/solid-router' { interface Register { router: ReturnType } } ================================================ FILE: e2e/solid-start/server-functions/src/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' import { NotFound } from '~/components/NotFound' import appCss from '~/styles/app.css?url' export const Route = createRootRoute({ head: () => ({ meta: [ { charSet: 'utf-8', }, { name: 'viewport', content: 'width=device-width, initial-scale=1', }, ], links: [{ rel: 'stylesheet', href: appCss }], }), errorComponent: (props) => { return

{props.error.stack}

}, notFoundComponent: () => , component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/abort-signal/$method.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createServerFn, createServerOnlyFn } from '@tanstack/solid-start' import { getRequest } from '@tanstack/solid-start/server' import * as Solid from 'solid-js' import z from 'zod' export const Route = createFileRoute('/abort-signal/$method')({ params: z.object({ method: z.union([z.literal('GET'), z.literal('POST')]), }), component: RouteComponent, }) const serverFnImpl = createServerOnlyFn(async () => { const request = getRequest() const signal = request.signal console.log('server function started', { signal }) return new Promise((resolve, reject) => { if (signal.aborted) { return reject(new Error('Aborted before start')) } const timerId = setTimeout(() => { console.log('server function finished') resolve('server function result') }, 1000) const onAbort = () => { clearTimeout(timerId) console.log('server function aborted') reject(new Error('Aborted')) } signal.addEventListener('abort', onAbort, { once: true }) }) }) const abortableServerFnGET = createServerFn().handler(serverFnImpl) const abortableServerFnPOST = createServerFn({ method: 'POST' }).handler( serverFnImpl, ) function RouteComponent() { const params = Route.useParams() const abortableServerFn = () => params().method === 'GET' ? abortableServerFnGET : abortableServerFnPOST const [errorMessage, setErrorMessage] = Solid.createSignal< string | undefined >(undefined) const [result, setResult] = Solid.createSignal(undefined) const reset = () => { setErrorMessage(undefined) setResult(undefined) } return (

result:

{result() ?? '$undefined'}

message:{' '}

{errorMessage() ?? '$undefined'}

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/abort-signal/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/abort-signal/')({ component: Home, }) function Home() { return (

Server functions E2E Abort Signal Tests

  • Abortable server function call with GET
  • Abortable server function call with POST
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/consistent.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as Solid from 'solid-js' import { createServerFn } from '@tanstack/solid-start' /** * This checks whether the returned payloads from a * server function are the same, regardless of whether the server function is * called directly from the client or from within the server function. * @link https://github.com/TanStack/router/issues/1866 * @link https://github.com/TanStack/router/issues/2481 */ export const Route = createFileRoute('/consistent')({ component: ConsistentServerFnCalls, loader: async () => { const data = await cons_serverGetFn1({ data: { username: 'TEST' } }) console.log('cons_serverGetFn1', data) return { data } }, }) const cons_getFn1 = createServerFn() .inputValidator((d: { username: string }) => d) .handler(({ data }) => { return { payload: data } }) const cons_serverGetFn1 = createServerFn() .inputValidator((d: { username: string }) => d) .handler(async ({ data }) => { return cons_getFn1({ data }) }) const cons_postFn1 = createServerFn({ method: 'POST' }) .inputValidator((d: { username: string }) => d) .handler(({ data }) => { return { payload: data } }) const cons_serverPostFn1 = createServerFn({ method: 'POST' }) .inputValidator((d: { username: string }) => d) .handler(({ data }) => { return cons_postFn1({ data }) }) function ConsistentServerFnCalls() { const [getServerResult, setGetServerResult] = Solid.createSignal({}) const [getDirectResult, setGetDirectResult] = Solid.createSignal({}) const [postServerResult, setPostServerResult] = Solid.createSignal({}) const [postDirectResult, setPostDirectResult] = Solid.createSignal({}) return (

Consistent Server Fn GET Calls

This component checks whether the returned payloads from server function are the same, regardless of whether the server function is called directly from the client or from within the server function.

It should return{' '}
            {JSON.stringify({ payload: { username: 'TEST' } })}
          

{`GET: cons_getFn1 called from server cons_serverGetFn1 returns`}
{JSON.stringify(getServerResult())}

{`GET: cons_getFn1 called directly returns`}
{JSON.stringify(getDirectResult())}

{`POST: cons_postFn1 called from cons_serverPostFn1 returns`}
{JSON.stringify(postServerResult())}

{`POST: cons_postFn1 called directly returns`}
{JSON.stringify(postDirectResult())}

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/cookies/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' import { z } from 'zod' const cookieSchema = z .object({ value: z.string() }) .catch(() => ({ value: `CLIENT-${Date.now()}` })) export const Route = createFileRoute('/cookies/')({ validateSearch: cookieSchema, component: RouteComponent, }) function RouteComponent() { const search = Route.useSearch() return ( got to route that sets the cookies with {JSON.stringify(search())} ) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/cookies/set.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' import { setCookie } from '@tanstack/solid-start/server' import { z } from 'zod' import Cookies from 'js-cookie' import * as Solid from 'solid-js' const cookieSchema = z.object({ value: z.string() }) export const Route = createFileRoute('/cookies/set')({ validateSearch: cookieSchema, loaderDeps: ({ search }) => search, loader: async ({ deps }) => { await setCookieServerFn1({ data: deps }) await setCookieServerFn2({ data: deps }) }, component: RouteComponent, }) export const setCookieServerFn1 = createServerFn() .inputValidator(cookieSchema) .handler(({ data }) => { setCookie(`cookie-1-${data.value}`, data.value) setCookie(`cookie-2-${data.value}`, data.value) }) export const setCookieServerFn2 = createServerFn() .inputValidator(cookieSchema) .handler(({ data }) => { setCookie(`cookie-3-${data.value}`, data.value) setCookie(`cookie-4-${data.value}`, data.value) }) function RouteComponent() { const search = Route.useSearch() const [cookiesFromDocument, setCookiesFromDocument] = Solid.createSignal< Record | undefined >(undefined) Solid.createEffect(() => { const tempCookies: Record = {} for (let i = 1; i <= 4; i++) { const key = `cookie-${i}-${search().value}` tempCookies[key] = Cookies.get(key) } setCookiesFromDocument(tempCookies) }, []) return (

cookies result

{Object.entries(cookiesFromDocument() || {}).map(([key, value]) => ( ))}
cookie value
{key} {value}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/dead-code-preserve.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as fs from 'node:fs' import { createServerFn } from '@tanstack/solid-start' import { getRequestHeader } from '@tanstack/solid-start/server' import { createSignal } from 'solid-js' import {} from '@tanstack/solid-router' export const Route = createFileRoute('/dead-code-preserve')({ component: RouteComponent, }) // by using this we make sure DCE still works - this errors when imported on the client const filePath = 'count-effect.txt' async function readCount() { return parseInt( await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'), ) } async function updateCount() { const count = await readCount() await fs.promises.writeFile(filePath, `${count + 1}`) return true } const writeFileServerFn = createServerFn().handler(async () => { // eslint-disable-next-line unused-imports/no-unused-vars const test = await updateCount() return getRequestHeader('X-Test') }) const readFileServerFn = createServerFn().handler(async () => { const data = await readCount() return data }) function RouteComponent() { const [serverFnOutput, setServerFnOutput] = createSignal() return (

Dead code test

This server function writes to a file as a side effect, then reads it.

Server output

{serverFnOutput()}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/env-only.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createClientOnlyFn, createServerFn, createServerOnlyFn, } from '@tanstack/solid-start' import { createSignal } from 'solid-js' const serverEcho = createServerOnlyFn((input: string) => 'server got: ' + input) const clientEcho = createClientOnlyFn((input: string) => 'client got: ' + input) const testOnServer = createServerFn().handler(() => { const serverOnServer = serverEcho('hello') let clientOnServer: string try { clientOnServer = clientEcho('hello') } catch (e) { clientOnServer = 'clientEcho threw an error: ' + (e instanceof Error ? e.message : String(e)) } return { serverOnServer, clientOnServer } }) export const Route = createFileRoute('/env-only')({ component: RouteComponent, }) function RouteComponent() { const [results, setResults] = createSignal>>() async function handleClick() { const { serverOnServer, clientOnServer } = await testOnServer() const clientOnClient = clientEcho('hello') let serverOnClient: string try { serverOnClient = serverEcho('hello') } catch (e) { serverOnClient = 'serverEcho threw an error: ' + (e instanceof Error ? e.message : String(e)) } setResults({ serverOnServer, clientOnServer, clientOnClient, serverOnClient, }) } return (
{!!results() && (

serverEcho

When we called the function on the server:
{results()?.serverOnServer}
When we called the function on the client:
{results()?.serverOnClient}

clientEcho

When we called the function on the server:
{results()?.clientOnServer}
When we called the function on the client:
{results()?.clientOnClient}
)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/factory/-functions/createBarServerFn.ts ================================================ import { createMiddleware } from '@tanstack/solid-start' import { createFooServerFn } from './createFooServerFn' const barMiddleware = createMiddleware({ type: 'function' }).server( ({ next }) => { console.log('Bar middleware triggered') return next({ context: { bar: 'bar' } as const, }) }, ) export const createBarServerFn = createFooServerFn().middleware([barMiddleware]) export const barFnInsideFactoryFile = createBarServerFn().handler( ({ context, method }) => { return { name: 'barFnInsideFactoryFile', context, method, } }, ) ================================================ FILE: e2e/solid-start/server-functions/src/routes/factory/-functions/createFakeFn.ts ================================================ export function createFakeFn() { return { handler: (cb: () => Promise) => cb, } } ================================================ FILE: e2e/solid-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts ================================================ import { createMiddleware, createServerFn } from '@tanstack/solid-start' import { getRequest } from '@tanstack/solid-start/server' const fooMiddleware = createMiddleware({ type: 'function' }).server( ({ next }) => { const request = getRequest() console.log('Foo middleware triggered') return next({ context: { foo: 'foo', method: request.method } as const, }) }, ) export const createFooServerFn = createServerFn().middleware([fooMiddleware]) export const fooFnInsideFactoryFile = createFooServerFn().handler( async ({ context, method }) => { console.log('fooFnInsideFactoryFile handler triggered', context.method) return { name: 'fooFnInsideFactoryFile', context, method, } }, ) ================================================ FILE: e2e/solid-start/server-functions/src/routes/factory/-functions/functions.ts ================================================ import { createMiddleware, createServerFn } from '@tanstack/solid-start' import { createBarServerFn } from './createBarServerFn' import { createFooServerFn } from './createFooServerFn' import { createFakeFn } from './createFakeFn' export const fooFn = createFooServerFn().handler(({ context, method }) => { return { name: 'fooFn', context, method, } }) export const fooFnPOST = createFooServerFn({ method: 'POST' }).handler( ({ context, method }) => { return { name: 'fooFnPOST', context, method, } }, ) export const barFn = createBarServerFn().handler(({ context, method }) => { return { name: 'barFn', context, method, } }) export const barFnPOST = createBarServerFn({ method: 'POST' }).handler( ({ context, method }) => { return { name: 'barFnPOST', context, method, } }, ) const localMiddleware = createMiddleware({ type: 'function' }).server( ({ next }) => { console.log('local middleware triggered') return next({ context: { local: 'local' } as const, }) }, ) const localFnFactory = createBarServerFn.middleware([localMiddleware]) const anotherMiddleware = createMiddleware({ type: 'function' }).server( ({ next }) => { console.log('another middleware triggered') return next({ context: { another: 'another' } as const, }) }, ) export const localFn = localFnFactory() .middleware([anotherMiddleware]) .handler(({ context, method }) => { return { name: 'localFn', context, method, } }) export const localFnPOST = localFnFactory({ method: 'POST' }) .middleware([anotherMiddleware]) .handler(({ context, method }) => { return { name: 'localFnPOST', context, method, } }) export const fakeFn = createFakeFn().handler(async () => { return { name: 'fakeFn', window, } }) export const composeFactory = createServerFn({ method: 'GET' }).middleware([ createBarServerFn, ]) export const composedFn = composeFactory() .middleware([anotherMiddleware, localFnFactory]) .handler(({ context, method }) => { return { name: 'composedFn', context, method, } }) ================================================ FILE: e2e/solid-start/server-functions/src/routes/factory/index.tsx ================================================ import { createFileRoute, deepEqual } from '@tanstack/solid-router' import { createSignal, For } from 'solid-js' import { createServerFn } from '@tanstack/solid-start' import { fooFnInsideFactoryFile } from './-functions/createFooServerFn' import { barFn, barFnPOST, composedFn, fakeFn, fooFn, fooFnPOST, localFn, localFnPOST, } from './-functions/functions' export const Route = createFileRoute('/factory/')({ ssr: false, component: RouteComponent, }) const fnInsideRoute = createServerFn({ method: 'GET' }).handler( ({ method }) => { return { name: 'fnInsideRoute', method, } }, ) const functions = { fnInsideRoute: { fn: fnInsideRoute, type: 'serverFn', expected: { name: 'fnInsideRoute', method: 'GET', }, }, fooFnInsideFactoryFile: { fn: fooFnInsideFactoryFile, type: 'serverFn', expected: { name: 'fooFnInsideFactoryFile', context: { foo: 'foo', method: 'GET' }, method: 'GET', }, }, fooFn: { fn: fooFn, type: 'serverFn', expected: { name: 'fooFn', context: { foo: 'foo', method: 'GET' }, method: 'GET', }, }, fooFnPOST: { fn: fooFnPOST, type: 'serverFn', expected: { name: 'fooFnPOST', context: { foo: 'foo', method: 'POST' }, method: 'POST', }, }, barFn: { fn: barFn, type: 'serverFn', expected: { name: 'barFn', context: { foo: 'foo', method: 'GET', bar: 'bar' }, method: 'GET', }, }, barFnPOST: { fn: barFnPOST, type: 'serverFn', expected: { name: 'barFnPOST', context: { foo: 'foo', method: 'POST', bar: 'bar' }, method: 'POST', }, }, localFn: { fn: localFn, type: 'serverFn', expected: { name: 'localFn', context: { foo: 'foo', method: 'GET', bar: 'bar', local: 'local', another: 'another', }, method: 'GET', }, }, localFnPOST: { fn: localFnPOST, type: 'serverFn', expected: { name: 'localFnPOST', context: { foo: 'foo', method: 'POST', bar: 'bar', local: 'local', another: 'another', }, method: 'POST', }, }, composedFn: { fn: composedFn, type: 'serverFn', expected: { name: 'composedFn', context: { foo: 'foo', method: 'GET', bar: 'bar', another: 'another', local: 'local', }, method: 'GET', }, }, fakeFn: { fn: fakeFn, type: 'localFn', expected: { name: 'fakeFn', window, }, }, } satisfies Record interface TestCase { fn: () => Promise expected: any type: 'serverFn' | 'localFn' } function Test(props: TestCase) { const [result, setResult] = createSignal(null) function comparison() { if (result()) { const isEqual = deepEqual(result(), props.expected) return isEqual ? 'equal' : 'not equal' } return 'Loading...' } return (

It should return{' '}
            {props.type === 'serverFn'
              ? JSON.stringify(props.expected)
              : 'localFn'}
          

fn returns:
{result() ? props.type === 'serverFn' ? JSON.stringify(result()) : 'localFn' : 'Loading...'} {' '} {comparison()}

) } function RouteComponent() { return (

Server functions middleware E2E tests

{([name, testCase]) => }
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/formdata-redirect/index.tsx ================================================ import { createFileRoute, redirect } from '@tanstack/solid-router' import { createServerFn, useServerFn } from '@tanstack/solid-start' import { z } from 'zod' export const Route = createFileRoute('/formdata-redirect/')({ component: SubmitPostFormDataFn, validateSearch: z.object({ mode: z.union([z.literal('js'), z.literal('no-js')]).default('js'), }), }) const testValues = { name: 'Sean', } export const greetUser = createServerFn({ method: 'POST' }) .inputValidator((data: FormData) => { if (!(data instanceof FormData)) { throw new Error('Invalid! FormData is required') } const name = data.get('name') if (!name) { throw new Error('Name is required') } return { name: name.toString(), } }) .handler(({ data: { name } }) => { throw redirect({ to: '/formdata-redirect/target/$name', params: { name } }) }) function SubmitPostFormDataFn() { const mode = Route.useSearch({ select: (search) => search.mode }) const greetUserFn = useServerFn(greetUser) return (

Submit POST FormData Fn Call

It should return redirect to /formdata-redirect/target/{testValues.name}{' '} and greet the user with their name:
            {testValues.name}
          
{ if (mode() === 'js') { evt.preventDefault() const data = new FormData(evt.currentTarget) await greetUserFn({ data }) } }} >
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/formdata-redirect/target.$name.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/formdata-redirect/target/$name')({ component: RouteComponent, }) function RouteComponent() { const params = Route.useParams() return (
Hello{' '} {params().name}!
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/function-metadata/-functions/normalServerFn.ts ================================================ import { createServerFn } from '@tanstack/solid-start' export const getServerFn = createServerFn().handler(({ serverFnMeta }) => { return serverFnMeta }) export const postServerFn = createServerFn({ method: 'POST' }).handler( ({ serverFnMeta }) => { return serverFnMeta }, ) ================================================ FILE: e2e/solid-start/server-functions/src/routes/function-metadata/-functions/serverFnCallingServerFn.ts ================================================ import { createServerFn } from '@tanstack/solid-start' import { getServerFn, postServerFn } from './normalServerFn' export const getServerFnCallingServerFn = createServerFn().handler( async ({ serverFnMeta }) => { const post = await postServerFn() const get = await getServerFn() return { meta: serverFnMeta, inner: { get, post, }, } }, ) export const postServerFnCallingServerFn = createServerFn().handler( async ({ serverFnMeta }) => { const post = await postServerFn() const get = await getServerFn() return { meta: serverFnMeta, inner: { get, post, }, } }, ) ================================================ FILE: e2e/solid-start/server-functions/src/routes/function-metadata/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { getServerFn, postServerFn } from './-functions/normalServerFn' import { getServerFnCallingServerFn, postServerFnCallingServerFn, } from './-functions/serverFnCallingServerFn' export const Route = createFileRoute('/function-metadata/')({ component: RouteComponent, loader: async () => { const normalGet = await getServerFn() const normalPost = await postServerFn() const nestingGet = await getServerFnCallingServerFn() const nestingPost = await postServerFnCallingServerFn() return { normalGet, normalPost, nestingGet, nestingPost, } }, }) function RouteComponent() { const loaderData = Route.useLoaderData() return (

Server functions metadata E2E tests


Loader Data (SSR)

Server Captured Metadata:

Function Metadata:{' '} {JSON.stringify(loaderData().normalGet)}
Function Metadata:{' '} {JSON.stringify(loaderData().normalPost)}
Function Metadata:{' '} {JSON.stringify(loaderData().nestingGet)}
Function Metadata:{' '} {JSON.stringify(loaderData().nestingPost)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/function-method/-functions/serverFnCallingServerFn.ts ================================================ import { createServerFn } from '@tanstack/solid-start' const postServerFn = createServerFn({ method: 'POST' }).handler( ({ method }) => { return { method, } }, ) const getServerFn = createServerFn({ method: 'GET' }).handler(({ method }) => { return { method, } }) export const getServerFnCallingPost = createServerFn({ method: 'GET' }).handler( async ({ method }) => { const innerFnResult = await postServerFn({}) return { name: 'getServerFnCallingPost', method, innerFnResult, } }, ) export const postServerFnCallingGet = createServerFn({ method: 'POST', }).handler(async ({ method }) => { const innerFnResult = await getServerFn({}) return { name: 'postServerFnCallingGet', method, innerFnResult, } }) ================================================ FILE: e2e/solid-start/server-functions/src/routes/function-method/index.tsx ================================================ import { For, createSignal } from 'solid-js' import { Link, createFileRoute, deepEqual } from '@tanstack/solid-router' import { getServerFnCallingPost, postServerFnCallingGet, } from './-functions/serverFnCallingServerFn' export const Route = createFileRoute('/function-method/')({ component: RouteComponent, }) const functions = { getServerFnCallingPost: { fn: getServerFnCallingPost, expected: { name: 'getServerFnCallingPost', method: 'GET', innerFnResult: { method: 'POST', }, }, }, postServerFnCallingGet: { fn: postServerFnCallingGet, expected: { name: 'postServerFnCallingGet', method: 'POST', innerFnResult: { method: 'GET', }, }, }, } satisfies Record function RouteComponent() { return (

Server functions methods E2E tests

Go to Factory Functions and request method E2E test

{([, testCase]) => }
) } interface TestCase { fn: () => Promise expected: any } function Test({ fn, expected }: TestCase) { const [result, setResult] = createSignal(null) function comparison() { if (result()) { const isEqual = deepEqual(result(), expected) return isEqual ? 'equal' : 'not equal' } return 'Loading...' } return (
It should return{' '}
            {JSON.stringify(expected)}
          

fn returns:
{result() ? JSON.stringify(result()) : 'Loading...'} {' '} {comparison()}

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/headers.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as Solid from 'solid-js' import { createServerFn } from '@tanstack/solid-start' import { getRequestHeaders, setResponseHeader, } from '@tanstack/solid-start/server' import type { RequestHeaderName } from '@tanstack/solid-start/server' export const Route = createFileRoute('/headers')({ loader: async () => { return { testHeaders: await getTestHeaders(), } }, component: () => { const loaderData = Route.useLoaderData() return }, }) export const getTestHeaders = createServerFn().handler(() => { setResponseHeader('x-test-header', 'test-value') const reqHeaders = Object.fromEntries(getRequestHeaders().entries()) return { serverHeaders: reqHeaders, headers: reqHeaders, } }) type TestHeadersResult = { headers?: Partial> serverHeaders?: Partial> } function ResponseHeaders({ initialTestHeaders, }: { initialTestHeaders: TestHeadersResult }) { const [testHeadersResult, setTestHeadersResult] = Solid.createSignal(null) return (

Headers Test

{ evt.preventDefault() getTestHeaders().then(setTestHeadersResult) }} >

Initial Headers:

          {JSON.stringify(initialTestHeaders.headers, null, 2)}
        
{testHeadersResult() && ( <>

Updated Headers:

              {JSON.stringify(testHeadersResult()?.headers, null, 2)}
            
)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Server functions E2E tests

  • Consistent server function returns both on client and server for GET and POST calls
  • submitting multipart/form-data as server function input
  • Server function can return null for GET and POST calls
  • Server function can correctly send and receive FormData
  • server function can correctly send and receive headers
  • Direct POST submitting FormData to a Server function returns the correct message
  • invoking a server function with custom response status code
  • isomorphic functions can have different implementations on client and server
  • env-only functions can only be called on the server or client respectively
  • server function sets cookies
  • dead code elimation only affects code after transformation
  • aborting a server function call
  • server function returns raw response
  • Server Functions method E2E tests
  • Server Functions metadata E2E tests
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/isomorphic-fns.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createIsomorphicFn, createServerFn } from '@tanstack/solid-start' import { createSignal } from 'solid-js' const getEnv = createIsomorphicFn() .server(() => 'server') .client(() => 'client') const getServerEnv = createServerFn().handler(() => getEnv()) const getEcho = createIsomorphicFn() .server((input: string) => 'server received ' + input) .client((input) => 'client received ' + input) const getServerEcho = createServerFn() .inputValidator((input: string) => input) .handler(({ data }) => getEcho(data)) export const Route = createFileRoute('/isomorphic-fns')({ component: RouteComponent, loader() { return { envOnLoad: getEnv(), } }, }) function RouteComponent() { const loaderData = Route.useLoaderData() const [results, setResults] = createSignal>>() async function handleClick() { const envOnClick = getEnv() const echo = getEcho('hello') const [serverEnv, serverEcho] = await Promise.all([ getServerEnv(), getServerEcho({ data: 'hello' }), ]) setResults({ envOnClick, echo, serverEnv, serverEcho }) } return (
{!!results() && (

getEnv

When we called the function on the server it returned:
            {JSON.stringify(results()?.serverEnv)}
          
When we called the function on the client it returned:
            {JSON.stringify(results()?.envOnClick)}
          
When we called the function during SSR it returned:
            {JSON.stringify(loaderData().envOnLoad)}
          

echo

When we called the function on the server it returned:
            {JSON.stringify(results()?.serverEcho)}
          
When we called the function on the client it returned:
            {JSON.stringify(results()?.echo)}
          
)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/middleware/client-middleware-router.tsx ================================================ import { createFileRoute, useRouter } from '@tanstack/solid-router' import { createMiddleware, createServerFn, getRouterInstance, } from '@tanstack/solid-start' import { createSignal } from 'solid-js' const middleware = createMiddleware({ type: 'function' }).client( async ({ next }) => { const router = await getRouterInstance() return next({ sendContext: { routerContext: router.options.context, }, }) }, ) const serverFn = createServerFn() .middleware([middleware]) .handler(({ context }) => { return context.routerContext }) export const Route = createFileRoute('/middleware/client-middleware-router')({ component: RouteComponent, loader: async () => ({ serverFnLoaderResult: await serverFn() }), }) function RouteComponent() { const [serverFnClientResult, setServerFnClientResult] = createSignal({}) const loaderData = Route.useLoaderData() const router = useRouter() return (

Client Middleware has access to router instance

This component checks that the client middleware has access to the router instance and thus its context.

It should return{' '}
            {JSON.stringify(router.options.context)}
          

serverFn when invoked in the loader returns:
{JSON.stringify(serverFnClientResult())}

serverFn when invoked on the client returns:
{JSON.stringify(loaderData().serverFnLoaderResult)}

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/middleware/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/middleware/')({ component: RouteComponent, }) function RouteComponent() { return (

Server functions middleware E2E tests

  • Client Middleware has access to router instance
  • Client Middleware can send server function reference in context
  • Request Middleware in combination with server function
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/middleware/request-middleware.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createMiddleware, createServerFn } from '@tanstack/solid-start' import { getRequest } from '@tanstack/solid-start/server' import { createSignal, Show } from 'solid-js' const requestMiddleware = createMiddleware({ type: 'request' }).server( async ({ next, request }) => { return next({ context: { requestParam: request.url, requestFunc: getRequest().url, }, }) }, ) const serverFn = createServerFn() .middleware([requestMiddleware]) .handler(async ({ context: { requestParam, requestFunc } }) => { return { requestParam, requestFunc } }) export const Route = createFileRoute('/middleware/request-middleware')({ loader: () => serverFn(), component: RouteComponent, }) function RouteComponent() { const loaderData = Route.useLoaderData() const [clientData, setClientData] = createSignal | null>(null) return (

Request Middleware in combination with server function


Loader Data

Request Param:
{loaderData().requestParam}
Request Func:
{loaderData().requestFunc}


Client Data

{(data) => (
Request Param:
{data().requestParam}
Request Func:
{data().requestFunc}
)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/middleware/send-serverFn.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createMiddleware, createServerFn } from '@tanstack/solid-start' import { createSignal } from 'solid-js' const middleware = createMiddleware({ type: 'function' }).client( async ({ next }) => { return next({ sendContext: { serverFn: barFn, }, }) }, ) const fooFn = createServerFn() .middleware([middleware]) .handler(({ context }) => { return context.serverFn() }) const barFn = createServerFn().handler(() => { return 'bar' }) export const Route = createFileRoute('/middleware/send-serverFn')({ component: RouteComponent, loader: async () => ({ serverFnLoaderResult: await fooFn() }), }) function RouteComponent() { const [serverFnClientResult, setServerFnClientResult] = createSignal({}) const loaderData = Route.useLoaderData() return (

Send server function in context

This component checks that the client middleware can send a reference to a server function in the context, which can then be invoked in the server function handler.

It should return{' '}
            {JSON.stringify('bar')}
          

serverFn when invoked in the loader returns:
{JSON.stringify(serverFnClientResult())}

serverFn when invoked on the client returns:
{JSON.stringify(loaderData().serverFnLoaderResult)}

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/multipart.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as Solid from 'solid-js' import { createServerFn } from '@tanstack/solid-start' export const Route = createFileRoute('/multipart')({ component: MultipartServerFnCall, }) const multipartFormDataServerFn = createServerFn({ method: 'POST' }) .inputValidator((x: unknown) => { if (!(x instanceof FormData)) { throw new Error('Invalid form data') } const value = x.get('input_field') const file = x.get('input_file') if (typeof value !== 'string') { throw new Error('Submitted value is not a string') } if (!(file instanceof File)) { throw new Error('File is required') } return { submittedValue: value, file, } }) .handler(async ({ data }) => { const contents = await data.file.text() return { value: data.submittedValue, file: { name: data.file.name, size: data.file.size, contents: contents, }, } }) function MultipartServerFnCall() { let formRef: HTMLFormElement | undefined const [multipartResult, setMultipartResult] = Solid.createSignal({}) const handleSubmit = (e: any) => { e.preventDefault() if (!formRef) { return } const formData = new FormData(formRef) multipartFormDataServerFn({ data: formData }).then(setMultipartResult) } return (

Multipart Server Fn POST Call

It should return{' '}
            {JSON.stringify({
              value: 'test field value',
              file: { name: 'my_file.txt', size: 9, contents: 'test data' },
            })}
          
          {JSON.stringify(multipartResult())}
        
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/primitives/index.tsx ================================================ import { useQuery } from '@tanstack/solid-query' import { createFileRoute } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' import { For, Show } from 'solid-js' import { z } from 'zod' export const Route = createFileRoute('/primitives/')({ component: RouteComponent, ssr: true, }) function stringify(data: any) { return JSON.stringify(data === undefined ? '$undefined' : data) } const $stringPost = createServerFn({ method: 'POST' }) .inputValidator(z.string()) .handler((ctx) => ctx.data) const $stringGet = createServerFn({ method: 'GET' }) .inputValidator(z.string()) .handler((ctx) => ctx.data) const $undefinedPost = createServerFn({ method: 'POST' }) .inputValidator(z.undefined()) .handler((ctx) => ctx.data) const $undefinedGet = createServerFn({ method: 'GET' }) .inputValidator(z.undefined()) .handler((ctx) => ctx.data) const $nullPost = createServerFn({ method: 'POST' }) .inputValidator(z.null()) .handler((ctx) => ctx.data) const $nullGet = createServerFn({ method: 'GET' }) .inputValidator(z.null()) .handler((ctx) => ctx.data) interface PrimitiveComponentProps { serverFn: { get: (opts: { data: T }) => Promise post: (opts: { data: T }) => Promise } data: { value: T type: string } } interface TestProps extends PrimitiveComponentProps { method: 'get' | 'post' } function Test(props: TestProps) { const query = useQuery(() => ({ queryKey: [props.data.type, props.method], queryFn: async () => { const result = await props.serverFn[props.method]({ data: props.data.value, }) if (result === undefined) { return '$undefined' } return result }, })) const testId = `${props.method}-${props.data.type}` return (

serverFn method={props.method}

expected

{stringify(props.data.value)}

result

{stringify(query.data)}
) } function PrimitiveComponent(props: PrimitiveComponentProps) { return (

data type: {props.data.type}




) } function makeTestCase(props: PrimitiveComponentProps) { return props } const testCases = [ makeTestCase({ data: { value: null, type: 'null', }, serverFn: { get: $nullGet, post: $nullPost, }, }), makeTestCase({ data: { value: undefined, type: 'undefined', }, serverFn: { get: $undefinedGet, post: $undefinedPost, }, }), makeTestCase({ data: { value: 'foo-bar', type: 'string', }, serverFn: { get: $stringGet, post: $stringPost, }, }), ] as Array> function RouteComponent() { return {(t) => } } ================================================ FILE: e2e/solid-start/server-functions/src/routes/raw-response.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as Solid from 'solid-js' import { createServerFn } from '@tanstack/solid-start' export const Route = createFileRoute('/raw-response')({ component: RouteComponent, }) const expectedValue = 'Hello from a server function!' export const rawResponseFn = createServerFn().handler(() => { return new Response(expectedValue) }) function RouteComponent() { const [formDataResult, setFormDataResult] = Solid.createSignal({}) return (

Raw Response

It should return{' '}
{expectedValue}
{JSON.stringify(formDataResult())}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/redirect-test/index.tsx ================================================ import { useQuery } from '@tanstack/solid-query' import { createFileRoute, redirect } from '@tanstack/solid-router' import { createServerFn, useServerFn } from '@tanstack/solid-start' import { Suspense } from 'solid-js' const $redirectServerFn = createServerFn({ method: 'GET' }).handler( async () => { throw redirect({ to: '/redirect-test/target' }) }, ) export const Route = createFileRoute('/redirect-test/')({ component: RouteComponent, ssr: 'data-only', }) function RouteComponent() { const redirectFn = useServerFn($redirectServerFn) const query = useQuery(() => ({ queryKey: ['redirect-test'], queryFn: () => redirectFn(), })) return (

Redirect Source

{JSON.stringify(query.data)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/redirect-test/target.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/redirect-test/target')({ component: RouteComponent, }) function RouteComponent() { return (

Redirect Target

Successfully redirected!

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/redirect-test-ssr/index.tsx ================================================ import { useQuery } from '@tanstack/solid-query' import { createFileRoute, redirect } from '@tanstack/solid-router' import { createServerFn, useServerFn } from '@tanstack/solid-start' import { Suspense } from 'solid-js' const $redirectServerFn = createServerFn({ method: 'GET' }).handler( async () => { throw redirect({ to: '/redirect-test-ssr/target' }) }, ) export const Route = createFileRoute('/redirect-test-ssr/')({ component: RouteComponent, ssr: true, }) function RouteComponent() { const redirectFn = useServerFn($redirectServerFn) const query = useQuery(() => ({ queryKey: ['redirect-test-ssr'], queryFn: () => redirectFn(), })) return (

Redirect Source SSR

{JSON.stringify(query.data)}
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/redirect-test-ssr/target.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/redirect-test-ssr/target')({ component: RouteComponent, }) function RouteComponent() { return (

Redirect Target SSR

Successfully redirected!

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/return-null.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' import * as Solid from 'solid-js' /** * This checks whether the server function can * return null without throwing an error or returning something else. * @link https://github.com/TanStack/router/issues/2776 */ export const Route = createFileRoute('/return-null')({ component: AllowServerFnReturnNull, }) const $allow_return_null_getFn = createServerFn().handler(async () => { return null }) const $allow_return_null_postFn = createServerFn({ method: 'POST' }).handler( async () => { return null }, ) function AllowServerFnReturnNull() { const [getServerResult, setGetServerResult] = Solid.createSignal('-') const [postServerResult, setPostServerResult] = Solid.createSignal('-') return (

Allow ServerFn to return `null`

This component checks whether the server function can return null without throwing an error.

It should return{' '}
{JSON.stringify(null)}

{`GET: $allow_return_null_getFn returns`}
{JSON.stringify(getServerResult())}

{`POST: $allow_return_null_postFn returns`}
{JSON.stringify(postServerResult())}

) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/serialize-form-data.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import * as Solid from 'solid-js' import { createServerFn } from '@tanstack/solid-start' const testValues = { name: 'Sean', age: 25, pet1: 'dog', pet2: 'cat', __adder: 1, } export const greetUser = createServerFn({ method: 'POST' }) .inputValidator((data: FormData) => { if (!(data instanceof FormData)) { throw new Error('Invalid! FormData is required') } const name = data.get('name') const age = data.get('age') const pets = data.getAll('pet') if (!name || !age || pets.length === 0) { throw new Error('Name, age and pets are required') } return { name: name.toString(), age: parseInt(age.toString(), 10), pets: pets.map((pet) => pet.toString()), } }) .handler(({ data: { name, age, pets } }) => { return `Hello, ${name}! You are ${age + testValues.__adder} years old, and your favorite pets are ${pets.join(',')}.` }) export function SerializeFormDataFnCall() { const [formDataResult, setFormDataResult] = Solid.createSignal({}) return (

Serialize FormData Fn POST Call

It should return{' '}
            Hello, {testValues.name}! You are{' '}
            {testValues.age + testValues.__adder} years old, and your favorite{' '}
            pets are {testValues.pet1},{testValues.pet2}.
          
{ evt.preventDefault() const data = new FormData(evt.currentTarget) greetUser({ data }).then(setFormDataResult) }} >
          {JSON.stringify(formDataResult())}
        
) } export const Route = createFileRoute('/serialize-form-data')({ component: SerializeFormDataFnCall, }) ================================================ FILE: e2e/solid-start/server-functions/src/routes/status.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createServerFn, useServerFn } from '@tanstack/solid-start' import { setResponseStatus } from '@tanstack/solid-start/server' const helloFn = createServerFn().handler(() => { setResponseStatus(225, `hello`) return { hello: 'world', } }) export const Route = createFileRoute('/status')({ component: StatusComponent, }) function StatusComponent() { const hello = useServerFn(helloFn) return (
) } ================================================ FILE: e2e/solid-start/server-functions/src/routes/submit-post-formdata.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' export const Route = createFileRoute('/submit-post-formdata')({ component: SubmitPostFormDataFn, }) const testValues = { name: 'Sean', } export const greetUser = createServerFn({ method: 'POST' }) .inputValidator((data: FormData) => { if (!(data instanceof FormData)) { throw new Error('Invalid! FormData is required') } const name = data.get('name') if (!name) { throw new Error('Name is required') } return { name: name.toString(), } }) .handler(({ data: { name } }) => { return new Response(`Hello, ${name}!`) }) function SubmitPostFormDataFn() { return (

Submit POST FormData Fn Call

It should return navigate and return{' '}
            Hello, {testValues.name}!
          
) } ================================================ FILE: e2e/solid-start/server-functions/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/server-functions/src/vite-env.d.ts ================================================ declare module '*?url' { const url: string export default url } ================================================ FILE: e2e/solid-start/server-functions/tests/server-functions.spec.ts ================================================ import * as fs from 'node:fs' import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' import { PORT } from '../playwright.config' import type { Page } from '@playwright/test' test('Server function URLs correctly include constant ids', async ({ page, }) => { for (const currentPage of ['/submit-post-formdata', '/formdata-redirect']) { await page.goto(currentPage) await page.waitForLoadState('networkidle') const form = page.locator('form') const actionUrl = await form.getAttribute('action') expect(actionUrl).toMatch(/^\/_serverFn\/constant_id/) } }) test('invoking a server function with custom response status code', async ({ page, }) => { await page.goto('/status') await page.waitForLoadState('networkidle') const requestPromise = new Promise((resolve) => { page.on('response', (response) => { expect(response.status()).toBe(225) expect(response.statusText()).toBe('hello') expect(response.headers()['content-type']).toContain('application/json') resolve() }) }) await page.getByTestId('invoke-server-fn').click() await requestPromise }) test('Consistent server function returns both on client and server for GET and POST calls', async ({ page, }) => { await page.goto('/consistent') await page.waitForLoadState('networkidle') const expected = (await page .getByTestId('expected-consistent-server-fns-result') .textContent()) || '' expect(expected).not.toBe('') await page.getByTestId('test-consistent-server-fn-calls-btn').click() await page.waitForLoadState('networkidle') // GET calls await expect(page.getByTestId('cons_serverGetFn1-response')).toContainText( expected, ) await expect(page.getByTestId('cons_getFn1-response')).toContainText(expected) // POST calls await expect(page.getByTestId('cons_serverPostFn1-response')).toContainText( expected, ) await expect(page.getByTestId('cons_postFn1-response')).toContainText( expected, ) }) test('submitting multipart/form-data as server function input', async ({ page, }) => { await page.goto('/multipart') await page.waitForLoadState('networkidle') const expected = (await page .getByTestId('expected-multipart-server-fn-result') .textContent()) || '' expect(expected).not.toBe('') const fileChooserPromise = page.waitForEvent('filechooser') await page.getByTestId('multipart-form-file-input').click() const fileChooser = await fileChooserPromise await fileChooser.setFiles({ name: 'my_file.txt', mimeType: 'text/plain', buffer: Buffer.from('test data', 'utf-8'), }) await page.getByText('Submit (onClick)').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('multipart-form-response')).toContainText( expected, ) }) test('isomorphic functions can have different implementations on client and server', async ({ page, }) => { await page.goto('/isomorphic-fns') await page.waitForLoadState('networkidle') await page.getByTestId('test-isomorphic-results-btn').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('server-result')).toContainText('server') await expect(page.getByTestId('client-result')).toContainText('client') await expect(page.getByTestId('ssr-result')).toContainText('server') await expect(page.getByTestId('server-echo-result')).toContainText( 'server received hello', ) await expect(page.getByTestId('client-echo-result')).toContainText( 'client received hello', ) }) test('env-only functions can only be called on the server or client respectively', async ({ page, }) => { await page.goto('/env-only') await page.waitForLoadState('networkidle') await page.getByTestId('test-env-only-results-btn').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('server-on-server')).toContainText( 'server got: hello', ) await expect(page.getByTestId('server-on-client')).toContainText( 'serverEcho threw an error: createServerOnlyFn() functions can only be called on the server!', ) await expect(page.getByTestId('client-on-server')).toContainText( 'clientEcho threw an error: createClientOnlyFn() functions can only be called on the client!', ) await expect(page.getByTestId('client-on-client')).toContainText( 'client got: hello', ) }) test('Server function can return null for GET and POST calls', async ({ page, }) => { await page.goto('/return-null') await page.waitForLoadState('networkidle') await page.getByTestId('test-allow-server-fn-return-null-btn').click() await page.waitForLoadState('networkidle') // GET call await expect( page.getByTestId('allow_return_null_getFn-response'), ).toContainText(JSON.stringify(null)) // POST call await expect( page.getByTestId('allow_return_null_postFn-response'), ).toContainText(JSON.stringify(null)) }) test('Server function can correctly send and receive FormData', async ({ page, }) => { await page.goto('/serialize-form-data') await page.waitForLoadState('networkidle') const expected = (await page .getByTestId('expected-serialize-formdata-server-fn-result') .textContent()) || '' expect(expected).not.toBe('') await page.getByTestId('test-serialize-formdata-fn-calls-btn').click() await page.waitForLoadState('networkidle') await expect( page.getByTestId('serialize-formdata-form-response'), ).toContainText(expected) }) test('server function can correctly send and receive headers', async ({ page, }) => { await page.goto('/headers') await page.waitForLoadState('networkidle') let headers = JSON.parse( await page.getByTestId('initial-headers-result').innerText(), ) expect(headers['host']).toBe(`localhost:${PORT}`) expect(headers['user-agent']).toContain('Mozilla/5.0') expect(headers['sec-fetch-mode']).toBe('navigate') await page.getByTestId('test-headers-btn').click() await page.waitForSelector('[data-testid="updated-headers-result"]') headers = JSON.parse( await page.getByTestId('updated-headers-result').innerText(), ) expect(headers['host']).toBe(`localhost:${PORT}`) expect(headers['user-agent']).toContain('Mozilla/5.0') expect(headers['sec-fetch-mode']).toBe('cors') expect(headers['referer']).toBe(`http://localhost:${PORT}/headers`) }) test('Direct POST submitting FormData to a Server function returns the correct message', async ({ page, }) => { await page.goto('/submit-post-formdata') await page.waitForLoadState('networkidle') const expected = (await page .getByTestId('expected-submit-post-formdata-server-fn-result') .textContent()) || '' expect(expected).not.toBe('') await page.getByTestId('test-submit-post-formdata-fn-calls-btn').click() await page.waitForLoadState('networkidle') const result = await page.innerText('body') expect(result).toBe(expected) }) test("server function's dead code is preserved if already there", async ({ page, }) => { await page.goto('/dead-code-preserve') await page.waitForLoadState('networkidle') await page.getByTestId('test-dead-code-fn-call-btn').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('dead-code-fn-call-response')).toContainText( '1', ) await fs.promises.rm('count-effect.txt') }) test.describe('server function sets cookies', () => { async function runCookieTest(page: Page, expectedCookieValue: string) { for (let i = 1; i <= 4; i++) { const key = `cookie-${i}-${expectedCookieValue}` const actualValue = await page.getByTestId(key).textContent() expect(actualValue).toBe(expectedCookieValue) } } test('SSR', async ({ page }) => { const expectedCookieValue = `SSR-${Date.now()}` await page.goto(`/cookies/set?value=${expectedCookieValue}`) await runCookieTest(page, expectedCookieValue) }) test('client side navigation', async ({ page }) => { const expectedCookieValue = `CLIENT-${Date.now()}` await page.goto(`/cookies?value=${expectedCookieValue}`) await page.getByTestId('link-to-set').click() await runCookieTest(page, expectedCookieValue) }) }) ;['GET', 'POST'].forEach((method) => { test.describe(`aborting a server function call: method ${method}`, () => { test('without aborting', async ({ page }) => { await page.goto('/abort-signal/' + method) await page.waitForLoadState('networkidle') await page.getByTestId('run-without-abort-btn').click() await page.waitForLoadState('networkidle') await page.waitForSelector( '[data-testid="result"]:has-text("server function result")', ) await page.waitForSelector( '[data-testid="errorMessage"]:has-text("$undefined")', ) const result = (await page.getByTestId('result').textContent()) || '' expect(result).toBe('server function result') const errorMessage = (await page.getByTestId('errorMessage').textContent()) || '' expect(errorMessage).toBe('$undefined') }) test('aborting', async ({ page }) => { await page.goto('/abort-signal/' + method) await page.waitForLoadState('networkidle') await page.getByTestId('run-with-abort-btn').click() await page.waitForLoadState('networkidle') await page.waitForSelector( '[data-testid="result"]:has-text("$undefined")', ) await page.waitForSelector( '[data-testid="errorMessage"]:has-text("aborted")', ) const result = (await page.getByTestId('result').textContent()) || '' expect(result).toBe('$undefined') const errorMessage = (await page.getByTestId('errorMessage').textContent()) || '' expect(errorMessage).toContain('abort') }) }) }) test('raw response', async ({ page }) => { await page.goto('/raw-response') await page.waitForLoadState('networkidle') const expectedValue = (await page.getByTestId('expected').textContent()) || '' expect(expectedValue).not.toBe('') await page.getByTestId('button').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('response')).toContainText(expectedValue) }) test.describe('formdata redirect modes', () => { for (const mode of ['js', 'no-js']) { test(`Server function can redirect when sending formdata: mode = ${mode}`, async ({ page, }) => { await page.goto('/formdata-redirect?mode=' + mode) await page.waitForLoadState('networkidle') const expected = (await page .getByTestId('expected-submit-post-formdata-server-fn-result') .textContent()) || '' expect(expected).not.toBe('') await page.getByTestId('test-submit-post-formdata-fn-calls-btn').click() await page.waitForLoadState('networkidle') await expect( page.getByTestId('formdata-redirect-target-name'), ).toContainText(expected) expect(page.url().endsWith(`/formdata-redirect/target/${expected}`)) }) } }) test.describe('middleware', () => { test.describe('client middleware should have access to router context via the router instance', () => { async function runTest(page: Page) { await page.waitForLoadState('networkidle') const expected = (await page.getByTestId('expected-server-fn-result').textContent()) || '' expect(expected).not.toBe('') await page.getByTestId('btn-serverFn').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('serverFn-loader-result')).toContainText( expected, ) await expect(page.getByTestId('serverFn-client-result')).toContainText( expected, ) } test('direct visit', async ({ page }) => { await page.goto('/middleware/client-middleware-router') await runTest(page) }) test('client navigation', async ({ page }) => { await page.goto('/middleware') await page.getByTestId('client-middleware-router-link').click() await runTest(page) }) }) test('server function in combination with request middleware', async ({ page, }) => { await page.goto('/middleware/request-middleware') await page.waitForLoadState('networkidle') async function checkEqual(prefix: string) { const requestParam = await page .getByTestId(`${prefix}-data-request-param`) .textContent() expect(requestParam).not.toBe('') const requestFunc = await page .getByTestId(`${prefix}-data-request-func`) .textContent() expect(requestParam).toBe(requestFunc) } await checkEqual('loader') await page.getByTestId('client-call-button').click() await page.waitForLoadState('networkidle') await checkEqual('client') }) }) test('factory', async ({ page }) => { await page.goto('/factory') await expect(page.getByTestId('factory-route-component')).toBeInViewport() const buttons = await page .locator('[data-testid^="btn-fn-"]') .elementHandles() for (const button of buttons) { const testId = await button.getAttribute('data-testid') if (!testId) { throw new Error('Button is missing data-testid') } const suffix = testId.replace('btn-fn-', '') const expected = (await page.getByTestId(`expected-fn-result-${suffix}`).textContent()) || '' expect(expected).not.toBe('') await button.click() await expect(page.getByTestId(`fn-result-${suffix}`)).toContainText( expected, ) await expect(page.getByTestId(`fn-comparison-${suffix}`)).toContainText( 'equal', ) } }) test('primitives', async ({ page }) => { await page.goto('/primitives') await page.waitForLoadState('networkidle') // Wait for client-side hydration to complete await expect(page.locator('[data-testid^="expected-"]').first()).toBeVisible() const testCases = await page .locator('[data-testid^="expected-"]') .elementHandles() expect(testCases.length).not.toBe(0) for (const testCase of testCases) { const testId = await testCase.getAttribute('data-testid') if (!testId) { throw new Error('testcase is missing data-testid') } const suffix = testId.replace('expected-', '') const expected = (await page.getByTestId(`expected-${suffix}`).textContent()) || '' expect(expected).not.toBe('') await expect(page.getByTestId(`result-${suffix}`)).toContainText(expected) } }) test('redirect in server function on direct navigation', async ({ page }) => { // Test direct navigation to a route with a server function that redirects await page.goto('/redirect-test') // Should redirect to target page await expect(page.getByTestId('redirect-target')).toBeVisible() expect(page.url()).toContain('/redirect-test/target') }) test('redirect in server function called in query during SSR', async ({ page, }) => { // Test direct navigation to a route with a server function that redirects // when called inside a query with ssr: true await page.goto('/redirect-test-ssr') // Should redirect to target page await expect(page.getByTestId('redirect-target-ssr')).toBeVisible() expect(page.url()).toContain('/redirect-test-ssr/target') }) test('server function is called with correct method option', async ({ page, }) => { await page.goto('/function-method', { waitUntil: 'networkidle' }) await expect(page.getByTestId('method-route-component')).toBeInViewport() const buttons = await page .locator('[data-testid^="btn-fn-"]') .elementHandles() for (const button of buttons) { const testId = await button.getAttribute('data-testid') if (!testId) { throw new Error('Button is missing data-testid') } const suffix = testId.replace('btn-fn-', '') const expected = (await page.getByTestId(`expected-fn-result-${suffix}`).textContent()) || '' expect(expected).not.toBe('') await button.click() await expect(page.getByTestId(`fn-result-${suffix}`)).toContainText( expected, ) await expect(page.getByTestId(`fn-comparison-${suffix}`)).toContainText( 'equal', ) } }) test('server function receives serverFnMeta in options', async ({ page }) => { // This test verifies that: // 1. Server functions receive `serverFnMeta` with full { id, name, filename } // 2. No1 works even when the said server function is called from another server function await page.goto('/function-metadata', { waitUntil: 'networkidle' }) await expect(page.getByTestId('metadata-route-component')).toBeInViewport() // Test for no1 const loaderNormalGet = await page .getByTestId('loader-normal-get-function-metadata') .textContent() const loaderNormalPost = await page .getByTestId('loader-normal-post-function-metadata') .textContent() // stringified metadata should not be empty string expect(loaderNormalGet).toBeTruthy() // metadata should have `id`, `name`, and `filename` property, with all of them being a non-empty string const normalGetMetadata = JSON.parse(loaderNormalGet!) expect(normalGetMetadata).toHaveProperty('id') expect(normalGetMetadata).toHaveProperty('name') expect(normalGetMetadata).toHaveProperty('filename') expect(typeof normalGetMetadata.id).toBe('string') expect(normalGetMetadata.id.length).toBeGreaterThan(0) expect(typeof normalGetMetadata.name).toBe('string') expect(normalGetMetadata.name.length).toBeGreaterThan(0) expect(typeof normalGetMetadata.filename).toBe('string') expect(normalGetMetadata.filename.length).toBeGreaterThan(0) // stringified metadata should not be empty string expect(loaderNormalPost).toBeTruthy() // metadata should have `id`, `name`, and `filename` property, with all of them being a non-empty string const normalPostMetadata = JSON.parse(loaderNormalPost!) expect(normalPostMetadata).toHaveProperty('id') expect(normalPostMetadata).toHaveProperty('name') expect(normalPostMetadata).toHaveProperty('filename') expect(typeof normalPostMetadata.id).toBe('string') expect(normalPostMetadata.id.length).toBeGreaterThan(0) expect(typeof normalPostMetadata.name).toBe('string') expect(normalPostMetadata.name.length).toBeGreaterThan(0) expect(typeof normalPostMetadata.filename).toBe('string') expect(normalPostMetadata.filename.length).toBeGreaterThan(0) // Test for no2 const loaderNestingGet = await page .getByTestId('loader-nesting-get-function-metadata') .textContent() const loaderNestingPost = await page .getByTestId('loader-nesting-post-function-metadata') .textContent() // metadata should have `id`, `name`, and `filename` property, with all of them being a non-empty string const nestingGetMetadata = JSON.parse(loaderNestingGet!) expect(nestingGetMetadata).toHaveProperty('meta.id') expect(nestingGetMetadata).toHaveProperty('meta.name') expect(nestingGetMetadata).toHaveProperty('meta.filename') expect(typeof nestingGetMetadata.meta.id).toBe('string') expect(nestingGetMetadata.meta.id.length).toBeGreaterThan(0) expect(typeof nestingGetMetadata.meta.name).toBe('string') expect(nestingGetMetadata.meta.name.length).toBeGreaterThan(0) expect(typeof nestingGetMetadata.meta.filename).toBe('string') expect(nestingGetMetadata.meta.filename.length).toBeGreaterThan(0) expect(nestingGetMetadata).toHaveProperty('inner.get.id') expect(nestingGetMetadata).toHaveProperty('inner.get.name') expect(nestingGetMetadata).toHaveProperty('inner.get.filename') expect(nestingGetMetadata.inner.get.id.length).toBeGreaterThan(0) expect(nestingGetMetadata.inner.get.name.length).toBeGreaterThan(0) expect(nestingGetMetadata.inner.get.filename.length).toBeGreaterThan(0) expect(nestingGetMetadata).toHaveProperty('inner.post.id') expect(nestingGetMetadata).toHaveProperty('inner.post.name') expect(nestingGetMetadata).toHaveProperty('inner.post.filename') expect(nestingGetMetadata.inner.post.id.length).toBeGreaterThan(0) expect(nestingGetMetadata.inner.post.name.length).toBeGreaterThan(0) expect(nestingGetMetadata.inner.post.filename.length).toBeGreaterThan(0) // metadata should have `id`, `name`, and `filename` property, with all of them being a non-empty string const nestingPostMetadata = JSON.parse(loaderNestingPost!) expect(nestingPostMetadata).toHaveProperty('meta.id') expect(nestingPostMetadata).toHaveProperty('meta.name') expect(nestingPostMetadata).toHaveProperty('meta.filename') expect(typeof nestingPostMetadata.meta.id).toBe('string') expect(nestingPostMetadata.meta.id.length).toBeGreaterThan(0) expect(typeof nestingPostMetadata.meta.name).toBe('string') expect(nestingPostMetadata.meta.name.length).toBeGreaterThan(0) expect(typeof nestingPostMetadata.meta.filename).toBe('string') expect(nestingPostMetadata.meta.filename.length).toBeGreaterThan(0) expect(nestingPostMetadata).toHaveProperty('inner.get.id') expect(nestingPostMetadata).toHaveProperty('inner.get.name') expect(nestingPostMetadata).toHaveProperty('inner.get.filename') expect(nestingPostMetadata.inner.get.id.length).toBeGreaterThan(0) expect(nestingPostMetadata.inner.get.name.length).toBeGreaterThan(0) expect(nestingPostMetadata.inner.get.filename.length).toBeGreaterThan(0) expect(nestingPostMetadata).toHaveProperty('inner.post.id') expect(nestingPostMetadata).toHaveProperty('inner.post.name') expect(nestingPostMetadata).toHaveProperty('inner.post.filename') expect(nestingPostMetadata.inner.post.id.length).toBeGreaterThan(0) expect(nestingPostMetadata.inner.post.name.length).toBeGreaterThan(0) expect(nestingPostMetadata.inner.post.filename.length).toBeGreaterThan(0) }) ================================================ FILE: e2e/solid-start/server-functions/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/server-functions/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' const FUNCTIONS_WITH_CONSTANT_ID = [ 'src/routes/submit-post-formdata.tsx/greetUser_createServerFn_handler', 'src/routes/formdata-redirect/index.tsx/greetUser_createServerFn_handler', ] export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [ tailwindcss(), tanstackStart({ serverFns: { generateFunctionId: (opts) => { const id = `${opts.filename}/${opts.functionName}` if (FUNCTIONS_WITH_CONSTANT_ID.includes(id)) return 'constant_id' else return undefined }, }, }), viteSolid({ ssr: true }), ], }) ================================================ FILE: e2e/solid-start/server-routes/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/server-routes/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/solid-start/server-routes/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/server-routes/package.json ================================================ { "name": "tanstack-solid-start-e2e-server-routes", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-router-ssr-query": "workspace:^", "@tanstack/solid-start": "workspace:^", "js-cookie": "^3.0.5", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", "vite": "^8.0.0", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", "@types/node": "^22.10.2", "combinate": "^1.1.11", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/server-routes/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } export const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `pnpm build && VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx ================================================ import { ErrorComponent, Link, rootRouteId, useMatch, useRouter, } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter() const isRoot = useMatch({ strict: false, select: (state) => state.id === rootRouteId, }) console.error(error) return (
{isRoot() ? ( Home ) : ( { e.preventDefault() window.history.back() }} > Go Back )}
) } ================================================ FILE: e2e/solid-start/server-routes/src/components/NotFound.tsx ================================================ import { Link } from '@tanstack/solid-router' export function NotFound({ children }: { children?: any }) { return (
{children ||

The page you are looking for does not exist.

}

Start Over

) } ================================================ FILE: e2e/solid-start/server-routes/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as MergeMiddlewareContextRouteImport } from './routes/merge-middleware-context' import { Route as MethodsRouteRouteImport } from './routes/methods/route' import { Route as IndexRouteImport } from './routes/index' import { Route as MethodsIndexRouteImport } from './routes/methods/index' import { Route as MethodsOnlyAnyRouteImport } from './routes/methods/only-any' import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' import { Route as ApiParamsFooRouteRouteImport } from './routes/api/params/$foo/route' import { Route as ApiParamsFooBarRouteImport } from './routes/api/params/$foo/$bar' const MergeMiddlewareContextRoute = MergeMiddlewareContextRouteImport.update({ id: '/merge-middleware-context', path: '/merge-middleware-context', getParentRoute: () => rootRouteImport, } as any) const MethodsRouteRoute = MethodsRouteRouteImport.update({ id: '/methods', path: '/methods', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const MethodsIndexRoute = MethodsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => MethodsRouteRoute, } as any) const MethodsOnlyAnyRoute = MethodsOnlyAnyRouteImport.update({ id: '/only-any', path: '/only-any', getParentRoute: () => MethodsRouteRoute, } as any) const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ id: '/api/only-any', path: '/api/only-any', getParentRoute: () => rootRouteImport, } as any) const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ id: '/api/middleware-context', path: '/api/middleware-context', getParentRoute: () => rootRouteImport, } as any) const ApiParamsFooRouteRoute = ApiParamsFooRouteRouteImport.update({ id: '/api/params/$foo', path: '/api/params/$foo', getParentRoute: () => rootRouteImport, } as any) const ApiParamsFooBarRoute = ApiParamsFooBarRouteImport.update({ id: '/$bar', path: '/$bar', getParentRoute: () => ApiParamsFooRouteRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/methods': typeof MethodsRouteRouteWithChildren '/merge-middleware-context': typeof MergeMiddlewareContextRoute '/api/middleware-context': typeof ApiMiddlewareContextRoute '/api/only-any': typeof ApiOnlyAnyRoute '/methods/only-any': typeof MethodsOnlyAnyRoute '/methods/': typeof MethodsIndexRoute '/api/params/$foo': typeof ApiParamsFooRouteRouteWithChildren '/api/params/$foo/$bar': typeof ApiParamsFooBarRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/merge-middleware-context': typeof MergeMiddlewareContextRoute '/api/middleware-context': typeof ApiMiddlewareContextRoute '/api/only-any': typeof ApiOnlyAnyRoute '/methods/only-any': typeof MethodsOnlyAnyRoute '/methods': typeof MethodsIndexRoute '/api/params/$foo': typeof ApiParamsFooRouteRouteWithChildren '/api/params/$foo/$bar': typeof ApiParamsFooBarRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/methods': typeof MethodsRouteRouteWithChildren '/merge-middleware-context': typeof MergeMiddlewareContextRoute '/api/middleware-context': typeof ApiMiddlewareContextRoute '/api/only-any': typeof ApiOnlyAnyRoute '/methods/only-any': typeof MethodsOnlyAnyRoute '/methods/': typeof MethodsIndexRoute '/api/params/$foo': typeof ApiParamsFooRouteRouteWithChildren '/api/params/$foo/$bar': typeof ApiParamsFooBarRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/methods' | '/merge-middleware-context' | '/api/middleware-context' | '/api/only-any' | '/methods/only-any' | '/methods/' | '/api/params/$foo' | '/api/params/$foo/$bar' fileRoutesByTo: FileRoutesByTo to: | '/' | '/merge-middleware-context' | '/api/middleware-context' | '/api/only-any' | '/methods/only-any' | '/methods' | '/api/params/$foo' | '/api/params/$foo/$bar' id: | '__root__' | '/' | '/methods' | '/merge-middleware-context' | '/api/middleware-context' | '/api/only-any' | '/methods/only-any' | '/methods/' | '/api/params/$foo' | '/api/params/$foo/$bar' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute MethodsRouteRoute: typeof MethodsRouteRouteWithChildren MergeMiddlewareContextRoute: typeof MergeMiddlewareContextRoute ApiMiddlewareContextRoute: typeof ApiMiddlewareContextRoute ApiOnlyAnyRoute: typeof ApiOnlyAnyRoute ApiParamsFooRouteRoute: typeof ApiParamsFooRouteRouteWithChildren } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/merge-middleware-context': { id: '/merge-middleware-context' path: '/merge-middleware-context' fullPath: '/merge-middleware-context' preLoaderRoute: typeof MergeMiddlewareContextRouteImport parentRoute: typeof rootRouteImport } '/methods': { id: '/methods' path: '/methods' fullPath: '/methods' preLoaderRoute: typeof MethodsRouteRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/methods/': { id: '/methods/' path: '/' fullPath: '/methods/' preLoaderRoute: typeof MethodsIndexRouteImport parentRoute: typeof MethodsRouteRoute } '/methods/only-any': { id: '/methods/only-any' path: '/only-any' fullPath: '/methods/only-any' preLoaderRoute: typeof MethodsOnlyAnyRouteImport parentRoute: typeof MethodsRouteRoute } '/api/only-any': { id: '/api/only-any' path: '/api/only-any' fullPath: '/api/only-any' preLoaderRoute: typeof ApiOnlyAnyRouteImport parentRoute: typeof rootRouteImport } '/api/middleware-context': { id: '/api/middleware-context' path: '/api/middleware-context' fullPath: '/api/middleware-context' preLoaderRoute: typeof ApiMiddlewareContextRouteImport parentRoute: typeof rootRouteImport } '/api/params/$foo': { id: '/api/params/$foo' path: '/api/params/$foo' fullPath: '/api/params/$foo' preLoaderRoute: typeof ApiParamsFooRouteRouteImport parentRoute: typeof rootRouteImport } '/api/params/$foo/$bar': { id: '/api/params/$foo/$bar' path: '/$bar' fullPath: '/api/params/$foo/$bar' preLoaderRoute: typeof ApiParamsFooBarRouteImport parentRoute: typeof ApiParamsFooRouteRoute } } } interface MethodsRouteRouteChildren { MethodsOnlyAnyRoute: typeof MethodsOnlyAnyRoute MethodsIndexRoute: typeof MethodsIndexRoute } const MethodsRouteRouteChildren: MethodsRouteRouteChildren = { MethodsOnlyAnyRoute: MethodsOnlyAnyRoute, MethodsIndexRoute: MethodsIndexRoute, } const MethodsRouteRouteWithChildren = MethodsRouteRoute._addFileChildren( MethodsRouteRouteChildren, ) interface ApiParamsFooRouteRouteChildren { ApiParamsFooBarRoute: typeof ApiParamsFooBarRoute } const ApiParamsFooRouteRouteChildren: ApiParamsFooRouteRouteChildren = { ApiParamsFooBarRoute: ApiParamsFooBarRoute, } const ApiParamsFooRouteRouteWithChildren = ApiParamsFooRouteRoute._addFileChildren(ApiParamsFooRouteRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, MethodsRouteRoute: MethodsRouteRouteWithChildren, MergeMiddlewareContextRoute: MergeMiddlewareContextRoute, ApiMiddlewareContextRoute: ApiMiddlewareContextRoute, ApiOnlyAnyRoute: ApiOnlyAnyRoute, ApiParamsFooRouteRoute: ApiParamsFooRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/server-routes/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { setupRouterSsrQueryIntegration } from '@tanstack/solid-router-ssr-query' import { QueryClient } from '@tanstack/solid-query' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' export function getRouter() { const queryClient = new QueryClient() const router = createRouter({ routeTree, defaultPreload: 'intent', defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , scrollRestoration: true, }) setupRouterSsrQueryIntegration({ router, queryClient }) return router } ================================================ FILE: e2e/solid-start/server-routes/src/routes/__root.tsx ================================================ import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' import { NotFound } from '~/components/NotFound' import appCss from '~/styles/app.css?url' export const Route = createRootRoute({ head: () => ({ meta: [ { charSet: 'utf-8', }, { name: 'viewport', content: 'width=device-width, initial-scale=1', }, ], links: [{ rel: 'stylesheet', href: appCss }], }), errorComponent: (props) => { return

{props.error.stack}

}, notFoundComponent: () => , component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: e2e/solid-start/server-routes/src/routes/api/middleware-context.ts ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createMiddleware } from '@tanstack/solid-start' const testParentMiddleware = createMiddleware().server(async ({ next }) => { const result = await next({ context: { testParent: true } }) return result }) const testMiddleware = createMiddleware() .middleware([testParentMiddleware]) .server(async ({ next }) => { const result = await next({ context: { test: true } }) return result }) export const Route = createFileRoute('/api/middleware-context')({ server: { middleware: [testMiddleware], handlers: { GET: ({ request, context }) => { return Response.json({ url: request.url, context: context, expectedContext: { testParent: true, test: true }, }) }, }, }, }) ================================================ FILE: e2e/solid-start/server-routes/src/routes/api/only-any.ts ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/api/only-any')({ server: { handlers: { ANY: ({ request }) => { return Response.json( { handler: 'ANY', method: request.method, }, { headers: { 'X-HANDLER': 'ANY', 'X-METHOD': request.method } }, ) }, }, }, }) ================================================ FILE: e2e/solid-start/server-routes/src/routes/api/params/$foo/$bar.ts ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/api/params/$foo/$bar')({ server: { handlers: { GET: ({ params }) => { return new Response('hello, ' + params.foo + ' and ' + params.bar) }, }, }, }) ================================================ FILE: e2e/solid-start/server-routes/src/routes/api/params/$foo/route.ts ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/api/params/$foo')({ server: { handlers: { GET: ({ params }) => { return new Response('hello, ' + params.foo) }, }, }, }) ================================================ FILE: e2e/solid-start/server-routes/src/routes/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Server routes E2E tests

  • server route middleware context is merged correctly
  • server route methods
) } ================================================ FILE: e2e/solid-start/server-routes/src/routes/merge-middleware-context.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { createSignal } from 'solid-js' export const Route = createFileRoute('/merge-middleware-context')({ component: () => , }) function MergeMiddlewareContext() { const [apiResponse, setApiResponse] = createSignal(null) const fetchMiddlewareContext = async () => { try { const response = await fetch('/api/middleware-context') const data = await response.json() setApiResponse(data) } catch (error) { console.error('Error fetching middleware context:', error) setApiResponse({ error: 'Failed to fetch' }) } } return (

Merge Server Route Middleware Context Test

{apiResponse() && (

API Response:

              {JSON.stringify(apiResponse(), null, 2)}
            

Context Verification:

{JSON.stringify(apiResponse()?.context, null, 2)}
Has testParent:{' '} {apiResponse()?.context?.testParent ? 'true' : 'false'}
Has test: {apiResponse()?.context?.test ? 'true' : 'false'}
)}
) } ================================================ FILE: e2e/solid-start/server-routes/src/routes/methods/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/methods/')({ component: RouteComponent, }) function RouteComponent() { return (
  • Server Route only has ANY handler
) } ================================================ FILE: e2e/solid-start/server-routes/src/routes/methods/only-any.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { useQuery } from '@tanstack/solid-query' import { For } from 'solid-js' export const Route = createFileRoute('/methods/only-any')({ ssr: false, component: RouteComponent, }) const HttpMethods = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', ] as const type HttpMethods = (typeof HttpMethods)[number] function Test(props: { method: HttpMethods }) { const query = useQuery(() => ({ queryKey: [props.method], queryFn: async () => { const response = await fetch(`/api/only-any`, { method: props.method, }) try { const json = (await response.json()) as Promise<{ method: HttpMethods handler: HttpMethods & 'ANY' }> return json } catch (e) {} // handle HEAD and OPTIONS that have no body const result = { handler: response.headers.get('x-handler') as HttpMethods & 'ANY', method: response.headers.get('x-method') as HttpMethods, } return result }, })) return (

method={props.method}

expected

{props.method}

result

{query.data ? (
{query.data.method}
) : null}
) } function RouteComponent() { return (

Server Route has only ANY handler

{(method) => }
) } ================================================ FILE: e2e/solid-start/server-routes/src/routes/methods/route.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/methods')({ component: RouteComponent, }) function RouteComponent() { return (

Server Routes Methods E2E tests

) } ================================================ FILE: e2e/solid-start/server-routes/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/server-routes/src/vite-env.d.ts ================================================ declare module '*?url' { const url: string export default url } ================================================ FILE: e2e/solid-start/server-routes/tests/server-routes.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' test('merge-middleware-context', async ({ page }) => { await page.goto('/merge-middleware-context') await page.waitForLoadState('networkidle') await page.getByTestId('test-middleware-context-btn').click() await page.waitForLoadState('networkidle') await expect(page.getByTestId('has-test-parent')).toContainText('true') await expect(page.getByTestId('has-test')).toContainText('true') const contextResult = await page.getByTestId('context-result').textContent() expect(contextResult).toContain('testParent') expect(contextResult).toContain('test') }) test.describe('methods', () => { test('only ANY', async ({ page }) => { await page.goto('/methods/only-any') // wait for page to be loaded by waiting for the route component to be rendered await expect(page.getByTestId('route-component')).toBeInViewport() const testCases = await page .locator('[data-testid^="expected-"]') .elementHandles() expect(testCases.length).not.toBe(0) for (const testCase of testCases) { const testId = await testCase.getAttribute('data-testid') if (!testId) { throw new Error('testcase is missing data-testid') } const suffix = testId.replace('expected-', '') const expected = (await page.getByTestId(`expected-${suffix}`).textContent()) || '' expect(expected).not.toBe('') await expect(page.getByTestId(`result-${suffix}`)).toContainText(expected) } }) }) ================================================ FILE: e2e/solid-start/server-routes/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/server-routes/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [tailwindcss(), tanstackStart(), viteSolid({ ssr: true })], }) ================================================ FILE: e2e/solid-start/spa-mode/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/spa-mode/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build# Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ count.txt ================================================ FILE: e2e/solid-start/spa-mode/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/spa-mode/package.json ================================================ { "name": "tanstack-solid-start-e2e-spa-mode", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "npx serve dist/client", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-start": "workspace:^", "solid-js": "^1.9.10", "zod": "^3.24.2" }, "devDependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite": "^8.0.0", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/spa-mode/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_SERVER_PORT=${PORT} pnpm build && NODE_ENV=production PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/spa-mode/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/posts' | '/posts/$postId' fileRoutesByTo: FileRoutesByTo to: '/' | '/posts' | '/posts/$postId' id: '__root__' | '/' | '/posts' | '/posts/$postId' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute PostsRoute: typeof PostsRouteWithChildren } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } } } interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, PostsRoute: PostsRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/spa-mode/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, }) return router } declare module '@tanstack/solid-router' { interface Register { router: ReturnType } } ================================================ FILE: e2e/solid-start/spa-mode/src/routes/__root.tsx ================================================ /// import { ClientOnly, HeadContent, Link, Outlet, Scripts, createRootRoute, useRouterState, } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' import type * as Solid from 'solid-js' import appCss from '~/styles/app.css?url' export const Route = createRootRoute({ head: () => ({ meta: [ { charSet: 'utf-8', }, { name: 'viewport', content: 'width=device-width, initial-scale=1', }, { title: 'SPA Mode E2E Test', }, ], links: [{ rel: 'stylesheet', href: appCss }], }), beforeLoad: () => { console.log( `beforeLoad for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) return { root: typeof window === 'undefined' ? 'server' : 'client', } }, loader: () => { console.log( `loader for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) return { root: typeof window === 'undefined' ? 'server' : 'client' } }, shellComponent: RootDocument, component: () => { const loaderData = Route.useLoaderData() const context = Route.useRouteContext() return (

root

loader: {loaderData().root}
context: {context().root}

) }, pendingComponent: () =>
__root Loading...
, }) function RootDocument({ children }: { children: Solid.JSX.Element }) { const routerState = useRouterState({ select: (state) => ({ isLoading: state.isLoading, status: state.status }), }) return (

SPA Mode E2E Test

Home

router isLoading:{' '} {routerState().isLoading ? 'true' : 'false'}
router status:{' '} {routerState().status}

{children} ) } ================================================ FILE: e2e/solid-start/spa-mode/src/routes/index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Home

Go to /posts/1
) } ================================================ FILE: e2e/solid-start/spa-mode/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/posts/$postId')({ beforeLoad: () => { console.log( `beforeLoad for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) return { postId: typeof window === 'undefined' ? 'server' : 'client', } }, loader: () => { console.log( `loader for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) return { postId: typeof window === 'undefined' ? 'server' : 'client' } }, component: () => { const loaderData = Route.useLoaderData() const context = Route.useRouteContext() return (

postId

loader: {loaderData().postId}
context: {context().postId}
) }, pendingComponent: () =>
$postId Loading...
, }) ================================================ FILE: e2e/solid-start/spa-mode/src/routes/posts.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/posts')({ beforeLoad: () => { console.log( `beforeLoad for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) return { posts: typeof window === 'undefined' ? 'server' : 'client', } }, loader: () => { console.log( `loader for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) return { posts: typeof window === 'undefined' ? 'server' : 'client' } }, component: () => { const loaderData = Route.useLoaderData() const context = Route.useRouteContext() return (

posts

loader: {loaderData().posts}
context: {context().posts}

) }, pendingComponent: () =>
posts Loading...
, }) ================================================ FILE: e2e/solid-start/spa-mode/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/spa-mode/tests/app.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' import type { Page } from '@playwright/test' async function runTest( page: Page, expectedData: { loader: { root: 'server' | 'client' posts: 'server' | 'client' postId: 'server' | 'client' } context: { root: 'server' | 'client' posts: 'server' | 'client' postId: 'server' | 'client' } }, ) { // wait for page to be loaded by waiting for the leaf route to be rendered await expect(page.getByTestId('postId-heading')).toContainText('postId') // check expectations await Promise.all( Object.entries(expectedData.loader).map(async ([route, expectedLoader]) => { await expect(page.getByTestId(`${route}-loader`)).toContainText( expectedLoader, ) }), ) await Promise.all( Object.entries(expectedData.context).map( async ([route, expectedContext]) => { await expect(page.getByTestId(`${route}-context`)).toContainText( expectedContext, ) }, ), ) await expect(page.getByTestId('router-isLoading')).toContainText('false') await expect(page.getByTestId('router-status')).toContainText('idle') } test.describe('SPA mode', () => { test(`directly visiting prerendered /posts/1`, async ({ page, }: { page: Page }) => { await page.goto('/posts/1') await runTest(page, { loader: { root: 'server', posts: 'server', postId: 'server', }, context: { root: 'server', posts: 'server', postId: 'server', }, }) }) test(`client-side navigation to /posts/1`, async ({ page, }: { page: Page }) => { await page.goto('/') const testId = 'link-posts-1' await page.getByTestId(testId).click() await runTest(page, { loader: { root: 'server', posts: 'client', postId: 'client', }, context: { root: 'client', posts: 'client', postId: 'client', }, }) }) }) ================================================ FILE: e2e/solid-start/spa-mode/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/spa-mode/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [ tailwindcss(), tanstackStart({ spa: { enabled: true, prerender: { outputPath: 'index.html', crawlLinks: true, }, }, pages: [ { path: '/posts/1', prerender: { enabled: true, outputPath: '/posts/1/index.html' }, }, ], }), viteSolid({ ssr: true }), ], }) ================================================ FILE: e2e/solid-start/virtual-routes/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/virtual-routes/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/solid-start/virtual-routes/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/virtual-routes/package.json ================================================ { "name": "tanstack-solid-start-e2e-virtual-routes", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-start": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", "vite": "^8.0.0", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "combinate": "^1.1.11", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/virtual-routes/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/virtual-routes/public/script.js ================================================ console.log('SCRIPT_1 loaded') window.SCRIPT_1 = true ================================================ FILE: e2e/solid-start/virtual-routes/public/script2.js ================================================ console.log('SCRIPT_2 loaded') window.SCRIPT_2 = true ================================================ FILE: e2e/solid-start/virtual-routes/public/site.webmanifest ================================================ { "name": "", "short_name": "", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } ================================================ FILE: e2e/solid-start/virtual-routes/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('layout/second-layout.tsx', [ route('route-without-file', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), ]), physical('/classic', 'file-based-subtree'), route('/special|pipe', 'pipe.tsx'), ]) ================================================ FILE: e2e/solid-start/virtual-routes/src/posts.tsx ================================================ import { notFound } from '@tanstack/solid-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/root' import { Route as pipeRouteImport } from './routes/pipe' import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' import { Route as bRouteImport } from './routes/b' import { Route as aRouteImport } from './routes/a' const pipeRoute = pipeRouteImport.update({ id: '/special|pipe', path: '/special|pipe', getParentRoute: () => rootRouteImport, } as any) const postsPostsRoute = postsPostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) const homeRoute = homeRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => postsPostsRoute, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ id: '/classic/hello', path: '/classic/hello', getParentRoute: () => rootRouteImport, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ id: '/world', path: '/world', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) const bRoute = bRouteImport.update({ id: '/route-without-file/layout-b', path: '/route-without-file/layout-b', getParentRoute: () => layoutSecondLayoutRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof homeRoute '/posts': typeof postsPostsRouteWithChildren '/special|pipe': typeof pipeRoute '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute '/route-without-file/layout-a': typeof aRoute '/route-without-file/layout-b': typeof bRoute } export interface FileRoutesByTo { '/': typeof homeRoute '/special|pipe': typeof pipeRoute '/posts': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello': typeof ClassicHelloIndexRoute '/route-without-file/layout-a': typeof aRoute '/route-without-file/layout-b': typeof bRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof homeRoute '/_first': typeof layoutFirstLayoutRouteWithChildren '/posts': typeof postsPostsRouteWithChildren '/special|pipe': typeof pipeRoute '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/_first/_second-layout': typeof layoutSecondLayoutRouteWithChildren '/posts/$postId': typeof postsPostsDetailRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute '/_first/_second-layout/route-without-file/layout-a': typeof aRoute '/_first/_second-layout/route-without-file/layout-b': typeof bRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/special|pipe' | '/classic/hello' | '/posts/' | '/posts/$postId' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' | '/route-without-file/layout-a' | '/route-without-file/layout-b' fileRoutesByTo: FileRoutesByTo to: | '/' | '/special|pipe' | '/posts' | '/posts/$postId' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello' | '/route-without-file/layout-a' | '/route-without-file/layout-b' id: | '__root__' | '/' | '/_first' | '/posts' | '/special|pipe' | '/classic/hello' | '/posts/' | '/_first/_second-layout' | '/posts/$postId' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' | '/_first/_second-layout/route-without-file/layout-a' | '/_first/_second-layout/route-without-file/layout-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { homeRoute: typeof homeRoute layoutFirstLayoutRoute: typeof layoutFirstLayoutRouteWithChildren postsPostsRoute: typeof postsPostsRouteWithChildren pipeRoute: typeof pipeRoute ClassicHelloRouteRoute: typeof ClassicHelloRouteRouteWithChildren } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/special|pipe': { id: '/special|pipe' path: '/special|pipe' fullPath: '/special|pipe' preLoaderRoute: typeof pipeRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } '/_first': { id: '/_first' path: '' fullPath: '/' preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof postsPostsDetailRouteImport parentRoute: typeof postsPostsRoute } '/_first/_second-layout': { id: '/_first/_second-layout' path: '' fullPath: '/' preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } '/classic/hello': { id: '/classic/hello' path: '/classic/hello' fullPath: '/classic/hello' preLoaderRoute: typeof ClassicHelloRouteRouteImport parentRoute: typeof rootRouteImport } '/classic/hello/': { id: '/classic/hello/' path: '/' fullPath: '/classic/hello/' preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/world': { id: '/classic/hello/world' path: '/world' fullPath: '/classic/hello/world' preLoaderRoute: typeof ClassicHelloWorldRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' fullPath: '/classic/hello/universe' preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-b': { id: '/_first/_second-layout/route-without-file/layout-b' path: '/route-without-file/layout-b' fullPath: '/route-without-file/layout-b' preLoaderRoute: typeof bRouteImport parentRoute: typeof layoutSecondLayoutRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' path: '/route-without-file/layout-a' fullPath: '/route-without-file/layout-a' preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } } } interface layoutSecondLayoutRouteChildren { aRoute: typeof aRoute bRoute: typeof bRoute } const layoutSecondLayoutRouteChildren: layoutSecondLayoutRouteChildren = { aRoute: aRoute, bRoute: bRoute, } const layoutSecondLayoutRouteWithChildren = layoutSecondLayoutRoute._addFileChildren(layoutSecondLayoutRouteChildren) interface layoutFirstLayoutRouteChildren { layoutSecondLayoutRoute: typeof layoutSecondLayoutRouteWithChildren } const layoutFirstLayoutRouteChildren: layoutFirstLayoutRouteChildren = { layoutSecondLayoutRoute: layoutSecondLayoutRouteWithChildren, } const layoutFirstLayoutRouteWithChildren = layoutFirstLayoutRoute._addFileChildren(layoutFirstLayoutRouteChildren) interface postsPostsRouteChildren { postsPostsHomeRoute: typeof postsPostsHomeRoute postsPostsDetailRoute: typeof postsPostsDetailRoute } const postsPostsRouteChildren: postsPostsRouteChildren = { postsPostsHomeRoute: postsPostsHomeRoute, postsPostsDetailRoute: postsPostsDetailRoute, } const postsPostsRouteWithChildren = postsPostsRoute._addFileChildren( postsPostsRouteChildren, ) interface ClassicHelloRouteRouteChildren { ClassicHelloUniverseRoute: typeof ClassicHelloUniverseRoute ClassicHelloWorldRoute: typeof ClassicHelloWorldRoute ClassicHelloIndexRoute: typeof ClassicHelloIndexRoute } const ClassicHelloRouteRouteChildren: ClassicHelloRouteRouteChildren = { ClassicHelloUniverseRoute: ClassicHelloUniverseRoute, ClassicHelloWorldRoute: ClassicHelloWorldRoute, ClassicHelloIndexRoute: ClassicHelloIndexRoute, } const ClassicHelloRouteRouteWithChildren = ClassicHelloRouteRoute._addFileChildren(ClassicHelloRouteRouteChildren) const rootRouteChildren: RootRouteChildren = { homeRoute: homeRoute, layoutFirstLayoutRoute: layoutFirstLayoutRouteWithChildren, postsPostsRoute: postsPostsRouteWithChildren, pipeRoute: pipeRoute, ClassicHelloRouteRoute: ClassicHelloRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/virtual-routes/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' export function getRouter() { const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, }) return router } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/a.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute( '/_first/_second-layout/route-without-file/layout-a', )({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/b.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute( '/_first/_second-layout/route-without-file/layout-b', )({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/file-based-subtree/hello/index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/classic/hello/')({ component: () =>
This is the index
, }) ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/classic/hello')({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/file-based-subtree/hello/universe.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/classic/hello/universe')({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/file-based-subtree/hello/world.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/classic/hello/world')({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/home.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/layout/first-layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/_first')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/_first/_second-layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/pipe.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/special|pipe')({ component: RouteComponent, }) function RouteComponent() { return (
Hello "/special|pipe"!
) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/posts/posts-detail.tsx ================================================ import { ErrorComponent, createFileRoute } from '@tanstack/solid-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/solid-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post().title}

{post().body}
) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/posts/posts-home.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/posts/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts(), { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: e2e/solid-start/virtual-routes/src/routes/root.tsx ================================================ /// import { HeadContent, Link, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import type { JSX } from 'solid-js' import appCss from '~/styles/app.css?url' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, head: () => { return { links: [{ rel: 'stylesheet', href: appCss }], } }, }) function RootComponent() { return ( ) } function RootDocument({ children }: { children: JSX.Element }) { return (
Home {' '} Posts {' '} Layout {' '} Subtree {' '} Pipe {' '} This Route Does Not Exist

{children} {/* Start rendering router matches */} ) } ================================================ FILE: e2e/solid-start/virtual-routes/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/virtual-routes/src/utils/posts.tsx ================================================ import { notFound } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = createServerFn({ method: 'GET' }) .inputValidator((postId: string) => postId) .handler(async ({ data: postId }) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { console.error(err) if (err.status === 404) { throw notFound() } throw err }) return post }) export const fetchPosts = createServerFn({ method: 'GET' }).handler( async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) }, ) ================================================ FILE: e2e/solid-start/virtual-routes/src/utils/seo.ts ================================================ export const seo = ({ title, description, keywords, image, }: { title: string description?: string image?: string keywords?: string }) => { const tags = [ { title }, { name: 'description', content: description }, { name: 'keywords', content: keywords }, { name: 'twitter:title', content: title }, { name: 'twitter:description', content: description }, { name: 'twitter:creator', content: '@tannerlinsley' }, { name: 'twitter:site', content: '@tannerlinsley' }, { name: 'og:type', content: 'website' }, { name: 'og:title', content: title }, { name: 'og:description', content: description }, ...(image ? [ { name: 'twitter:image', content: image }, { name: 'twitter:card', content: 'summary_large_image' }, { name: 'og:image', content: image }, ] : []), ] return tags } ================================================ FILE: e2e/solid-start/virtual-routes/src/utils/users.tsx ================================================ export type User = { id: number name: string email: string } const PORT = process.env.VITE_SERVER_PORT || 3000 export const DEPLOY_URL = `http://localhost:${PORT}` ================================================ FILE: e2e/solid-start/virtual-routes/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('body')).toContainText("I'm a layout") await expect(page.locator('body')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('body')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('body')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/solid-start/virtual-routes/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/virtual-routes/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/solid-start/virtual-routes/tests/special-characters.spec.ts ================================================ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' test.use({ whitelistErrors: [ /Failed to load resource: the server responded with a status of 404/, ], }) test.describe('Unicode route rendering', () => { test.beforeEach(async ({ page }) => { await page.goto('/') await page.waitForURL('/') }) test.describe('Special characters in route paths', () => { test('should render route with pipe character in path on direct navigation', async ({ page, baseURL, }) => { await page.goto('/special|pipe') await page.waitForURL(`${baseURL}/special%7Cpipe`) await expect( page.getByTestId('special-pipe-route-heading'), ).toBeInViewport() }) test('should render route with pipe character in path on router navigation', async ({ page, baseURL, }) => { const pipeLink = page.getByTestId('special-pipe-link') await pipeLink.click() await page.waitForURL(`${baseURL}/special%7Cpipe`) await expect( page.getByTestId('special-pipe-route-heading'), ).toBeInViewport() }) }) }) ================================================ FILE: e2e/solid-start/virtual-routes/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true, "types": ["vite/client"] } } ================================================ FILE: e2e/solid-start/virtual-routes/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [ tailwindcss(), tanstackStart({ router: { virtualRouteConfig: './routes.ts', }, }), viteSolid({ ssr: true }), ], }) ================================================ FILE: e2e/solid-start/website/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/solid-start/website/.gitignore ================================================ node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output /build/ /api/ /server/build /public/build # Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/solid-start/website/.prettierignore ================================================ **/build **/public pnpm-lock.yaml routeTree.gen.ts ================================================ FILE: e2e/solid-start/website/package.json ================================================ { "name": "tanstack-solid-start-e2e-website", "private": true, "sideEffects": false, "type": "module", "scripts": { "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "pnpx srvx --prod -s ../client dist/server/server.js", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", "@tanstack/solid-start": "workspace:^", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", "vite": "^8.0.0", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vite-plugin-solid": "^2.11.11" } } ================================================ FILE: e2e/solid-start/website/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `pnpm build && VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/solid-start/website/public/site.webmanifest ================================================ { "name": "", "short_name": "", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } ================================================ FILE: e2e/solid-start/website/src/components/DefaultCatchBoundary.tsx ================================================ import { ErrorComponent, Link, rootRouteId, useMatch, useRouter, } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter() const isRoot = useMatch({ strict: false, select: (state) => state.id === rootRouteId, }) console.error(error) return (
{isRoot() ? ( Home ) : ( { e.preventDefault() window.history.back() }} > Go Back )}
) } ================================================ FILE: e2e/solid-start/website/src/components/NotFound.tsx ================================================ import { Link } from '@tanstack/solid-router' export function NotFound({ children }: { children?: any }) { return (
{children ||

The page you are looking for does not exist.

}

Start Over

) } ================================================ FILE: e2e/solid-start/website/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as LibraryRouteImport } from './routes/_library' import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as ProjectIndexRouteImport } from './routes/$project.index' import { Route as LibraryProjectRouteImport } from './routes/_library.$project' import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsIndexRouteImport } from './routes/$project.$version.docs.index' import { Route as ProjectVersionDocsFrameworkFrameworkRouteImport } from './routes/$project.$version.docs.framework.$framework' import { Route as ProjectVersionDocsFrameworkFrameworkIndexRouteImport } from './routes/$project.$version.docs.framework.$framework.index' import { Route as ProjectVersionDocsFrameworkFrameworkSplatRouteImport } from './routes/$project.$version.docs.framework.$framework.$' import { Route as ProjectVersionDocsFrameworkFrameworkExamplesSplatRouteImport } from './routes/$project.$version.docs.framework.$framework.examples.$' const LibraryRoute = LibraryRouteImport.update({ id: '/_library', getParentRoute: () => rootRouteImport, } as any) const LibraryIndexRoute = LibraryIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => LibraryRoute, } as any) const ProjectIndexRoute = ProjectIndexRouteImport.update({ id: '/$project/', path: '/$project/', getParentRoute: () => rootRouteImport, } as any) const LibraryProjectRoute = LibraryProjectRouteImport.update({ id: '/$project', path: '/$project', getParentRoute: () => LibraryRoute, } as any) const LibraryProjectVersionIndexRoute = LibraryProjectVersionIndexRouteImport.update({ id: '/$version/', path: '/$version/', getParentRoute: () => LibraryProjectRoute, } as any) const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ id: '/$project/$version/docs/', path: '/$project/$version/docs/', getParentRoute: () => rootRouteImport, } as any) const ProjectVersionDocsFrameworkFrameworkRoute = ProjectVersionDocsFrameworkFrameworkRouteImport.update({ id: '/$project/$version/docs/framework/$framework', path: '/$project/$version/docs/framework/$framework', getParentRoute: () => rootRouteImport, } as any) const ProjectVersionDocsFrameworkFrameworkIndexRoute = ProjectVersionDocsFrameworkFrameworkIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, } as any) const ProjectVersionDocsFrameworkFrameworkSplatRoute = ProjectVersionDocsFrameworkFrameworkSplatRouteImport.update({ id: '/$', path: '/$', getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, } as any) const ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute = ProjectVersionDocsFrameworkFrameworkExamplesSplatRouteImport.update({ id: '/examples/$', path: '/examples/$', getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, } as any) export interface FileRoutesByFullPath { '/': typeof LibraryIndexRoute '/$project': typeof LibraryProjectRouteWithChildren '/$project/': typeof ProjectIndexRoute '/$project/$version/docs/': typeof ProjectVersionDocsIndexRoute '/$project/$version/': typeof LibraryProjectVersionIndexRoute '/$project/$version/docs/framework/$framework': typeof ProjectVersionDocsFrameworkFrameworkRouteWithChildren '/$project/$version/docs/framework/$framework/$': typeof ProjectVersionDocsFrameworkFrameworkSplatRoute '/$project/$version/docs/framework/$framework/': typeof ProjectVersionDocsFrameworkFrameworkIndexRoute '/$project/$version/docs/framework/$framework/examples/$': typeof ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute } export interface FileRoutesByTo { '/$project': typeof ProjectIndexRoute '/': typeof LibraryIndexRoute '/$project/$version/docs': typeof ProjectVersionDocsIndexRoute '/$project/$version': typeof LibraryProjectVersionIndexRoute '/$project/$version/docs/framework/$framework/$': typeof ProjectVersionDocsFrameworkFrameworkSplatRoute '/$project/$version/docs/framework/$framework': typeof ProjectVersionDocsFrameworkFrameworkIndexRoute '/$project/$version/docs/framework/$framework/examples/$': typeof ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/_library': typeof LibraryRouteWithChildren '/_library/$project': typeof LibraryProjectRouteWithChildren '/$project/': typeof ProjectIndexRoute '/_library/': typeof LibraryIndexRoute '/$project/$version/docs/': typeof ProjectVersionDocsIndexRoute '/_library/$project/$version/': typeof LibraryProjectVersionIndexRoute '/$project/$version/docs/framework/$framework': typeof ProjectVersionDocsFrameworkFrameworkRouteWithChildren '/$project/$version/docs/framework/$framework/$': typeof ProjectVersionDocsFrameworkFrameworkSplatRoute '/$project/$version/docs/framework/$framework/': typeof ProjectVersionDocsFrameworkFrameworkIndexRoute '/$project/$version/docs/framework/$framework/examples/$': typeof ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/$project' | '/$project/' | '/$project/$version/docs/' | '/$project/$version/' | '/$project/$version/docs/framework/$framework' | '/$project/$version/docs/framework/$framework/$' | '/$project/$version/docs/framework/$framework/' | '/$project/$version/docs/framework/$framework/examples/$' fileRoutesByTo: FileRoutesByTo to: | '/$project' | '/' | '/$project/$version/docs' | '/$project/$version' | '/$project/$version/docs/framework/$framework/$' | '/$project/$version/docs/framework/$framework' | '/$project/$version/docs/framework/$framework/examples/$' id: | '__root__' | '/_library' | '/_library/$project' | '/$project/' | '/_library/' | '/$project/$version/docs/' | '/_library/$project/$version/' | '/$project/$version/docs/framework/$framework' | '/$project/$version/docs/framework/$framework/$' | '/$project/$version/docs/framework/$framework/' | '/$project/$version/docs/framework/$framework/examples/$' fileRoutesById: FileRoutesById } export interface RootRouteChildren { LibraryRoute: typeof LibraryRouteWithChildren ProjectIndexRoute: typeof ProjectIndexRoute ProjectVersionDocsIndexRoute: typeof ProjectVersionDocsIndexRoute ProjectVersionDocsFrameworkFrameworkRoute: typeof ProjectVersionDocsFrameworkFrameworkRouteWithChildren } declare module '@tanstack/solid-router' { interface FileRoutesByPath { '/_library': { id: '/_library' path: '' fullPath: '/' preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } '/_library/': { id: '/_library/' path: '/' fullPath: '/' preLoaderRoute: typeof LibraryIndexRouteImport parentRoute: typeof LibraryRoute } '/$project/': { id: '/$project/' path: '/$project' fullPath: '/$project/' preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } '/_library/$project': { id: '/_library/$project' path: '/$project' fullPath: '/$project' preLoaderRoute: typeof LibraryProjectRouteImport parentRoute: typeof LibraryRoute } '/_library/$project/$version/': { id: '/_library/$project/$version/' path: '/$version' fullPath: '/$project/$version/' preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport parentRoute: typeof LibraryProjectRoute } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' fullPath: '/$project/$version/docs/' preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } '/$project/$version/docs/framework/$framework': { id: '/$project/$version/docs/framework/$framework' path: '/$project/$version/docs/framework/$framework' fullPath: '/$project/$version/docs/framework/$framework' preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkRouteImport parentRoute: typeof rootRouteImport } '/$project/$version/docs/framework/$framework/': { id: '/$project/$version/docs/framework/$framework/' path: '/' fullPath: '/$project/$version/docs/framework/$framework/' preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkIndexRouteImport parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute } '/$project/$version/docs/framework/$framework/$': { id: '/$project/$version/docs/framework/$framework/$' path: '/$' fullPath: '/$project/$version/docs/framework/$framework/$' preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkSplatRouteImport parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute } '/$project/$version/docs/framework/$framework/examples/$': { id: '/$project/$version/docs/framework/$framework/examples/$' path: '/examples/$' fullPath: '/$project/$version/docs/framework/$framework/examples/$' preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkExamplesSplatRouteImport parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute } } } interface LibraryProjectRouteChildren { LibraryProjectVersionIndexRoute: typeof LibraryProjectVersionIndexRoute } const LibraryProjectRouteChildren: LibraryProjectRouteChildren = { LibraryProjectVersionIndexRoute: LibraryProjectVersionIndexRoute, } const LibraryProjectRouteWithChildren = LibraryProjectRoute._addFileChildren( LibraryProjectRouteChildren, ) interface LibraryRouteChildren { LibraryProjectRoute: typeof LibraryProjectRouteWithChildren LibraryIndexRoute: typeof LibraryIndexRoute } const LibraryRouteChildren: LibraryRouteChildren = { LibraryProjectRoute: LibraryProjectRouteWithChildren, LibraryIndexRoute: LibraryIndexRoute, } const LibraryRouteWithChildren = LibraryRoute._addFileChildren(LibraryRouteChildren) interface ProjectVersionDocsFrameworkFrameworkRouteChildren { ProjectVersionDocsFrameworkFrameworkSplatRoute: typeof ProjectVersionDocsFrameworkFrameworkSplatRoute ProjectVersionDocsFrameworkFrameworkIndexRoute: typeof ProjectVersionDocsFrameworkFrameworkIndexRoute ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute: typeof ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute } const ProjectVersionDocsFrameworkFrameworkRouteChildren: ProjectVersionDocsFrameworkFrameworkRouteChildren = { ProjectVersionDocsFrameworkFrameworkSplatRoute: ProjectVersionDocsFrameworkFrameworkSplatRoute, ProjectVersionDocsFrameworkFrameworkIndexRoute: ProjectVersionDocsFrameworkFrameworkIndexRoute, ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute: ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute, } const ProjectVersionDocsFrameworkFrameworkRouteWithChildren = ProjectVersionDocsFrameworkFrameworkRoute._addFileChildren( ProjectVersionDocsFrameworkFrameworkRouteChildren, ) const rootRouteChildren: RootRouteChildren = { LibraryRoute: LibraryRouteWithChildren, ProjectIndexRoute: ProjectIndexRoute, ProjectVersionDocsIndexRoute: ProjectVersionDocsIndexRoute, ProjectVersionDocsFrameworkFrameworkRoute: ProjectVersionDocsFrameworkFrameworkRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/solid-start' declare module '@tanstack/solid-start' { interface Register { ssr: true router: Awaited> } } ================================================ FILE: e2e/solid-start/website/src/router.tsx ================================================ import { createRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, defaultPreload: 'intent', defaultStaleTime: 5000, defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , }) return router } declare module '@tanstack/solid-router' { interface Register { router: ReturnType } } ================================================ FILE: e2e/solid-start/website/src/routes/$project.$version.docs.framework.$framework.$.tsx ================================================ import { ErrorComponent, createFileRoute } from '@tanstack/solid-router' import type { ErrorComponentProps } from '@tanstack/solid-router' import { NotFound } from '~/components/NotFound' import { getDocument } from '~/server/document' import { capitalize, seo } from '~/utils/seo' export const Route = createFileRoute( '/$project/$version/docs/framework/$framework/$', )({ loader: ({ params: { _splat } }) => getDocument({ data: _splat!, }), head: ({ loaderData, params }) => ({ meta: seo({ title: `${loaderData?.title || 'Project'} | TanStack ${capitalize(params.project)} ${capitalize(params.framework)}`, }), }), errorComponent: PostErrorComponent, component: Page, notFoundComponent: () => { return Document not found }, }) function PostErrorComponent({ error }: ErrorComponentProps) { return } function Page() { const post = Route.useLoaderData() return (

{post().title}

{post().content}
) } ================================================ FILE: e2e/solid-start/website/src/routes/$project.$version.docs.framework.$framework.examples.$.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' import { NotFound } from '~/components/NotFound' import { capitalize, seo } from '~/utils/seo' export const Route = createFileRoute( '/$project/$version/docs/framework/$framework/examples/$', )({ head: ({ params }) => ({ meta: seo({ title: `${capitalize(params._splat || '')} Example | TanStack ${capitalize(params.project)} ${capitalize(params.framework)}`, }), }), component: Page, notFoundComponent: () => { return Example not found }, }) function Page() { const params = Route.useParams() return (

{params()._splat} example

) } ================================================ FILE: e2e/solid-start/website/src/routes/$project.$version.docs.framework.$framework.index.tsx ================================================ import { redirect, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute( '/$project/$version/docs/framework/$framework/', )({ beforeLoad: () => { throw redirect({ from: '/$project/$version/docs/framework/$framework/', to: '/$project/$version/docs/framework/$framework/$', params: { _splat: 'overview', }, }) }, }) ================================================ FILE: e2e/solid-start/website/src/routes/$project.$version.docs.framework.$framework.tsx ================================================ import { Link, Outlet, createFileRoute, useLocation, } from '@tanstack/solid-router' import { getDocumentHeads } from '~/server/document' import { getProject } from '~/server/projects' export const Route = createFileRoute( '/$project/$version/docs/framework/$framework', )({ loader: async ({ params: { project } }) => { const library = await getProject({ data: project }) const documents = await getDocumentHeads() return { library, documents, } }, component: Page, }) function Page() { const project = Route.useLoaderData({ select: (s) => s.library }) const documents = Route.useLoaderData({ select: (s) => s.documents }) const pathname = useLocation({ select: (s) => s.pathname }) return (

{pathname()}

) } ================================================ FILE: e2e/solid-start/website/src/routes/$project.$version.docs.index.tsx ================================================ import { redirect, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/$project/$version/docs/')({ beforeLoad: () => { throw redirect({ from: '/$project/$version/docs/', to: '/$project/$version/docs/framework/$framework/$', params: { framework: 'solid', _splat: 'overview', }, }) }, }) ================================================ FILE: e2e/solid-start/website/src/routes/$project.index.tsx ================================================ import { redirect, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/$project/')({ loader: ({ params }) => { throw redirect({ to: '/$project/$version', params: { project: params.project, version: 'latest', }, }) }, }) ================================================ FILE: e2e/solid-start/website/src/routes/__root.tsx ================================================ /// import { HeadContent, Outlet, Scripts, createRootRoute, } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' import { HydrationScript } from 'solid-js/web' import { NotFound } from '~/components/NotFound' import appCss from '~/styles/app.css?url' import { seo } from '~/utils/seo' export const Route = createRootRoute({ head: () => ({ meta: [ { charset: 'utf-8', }, { name: 'viewport', content: 'width=device-width, initial-scale=1', }, ...seo({ title: 'TanStack Website', description: `TanStack projects are type-safe!!!`, }), ], links: [ { rel: 'stylesheet', href: appCss }, { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png', }, { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png', }, { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png', }, { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, { rel: 'icon', href: '/favicon.ico' }, ], }), errorComponent: (props) => { return

{props.error.stack}

}, notFoundComponent: () => , component: RootComponent, }) function RootComponent() { return ( ) } ================================================ FILE: e2e/solid-start/website/src/routes/_library.$project.$version.index.tsx ================================================ import { Link, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/_library/$project/$version/')({ component: Page, }) function Page() { const params = Route.useParams() return (

{params().project} landing page

version: {params().version}

Get started with our documentation.

) } ================================================ FILE: e2e/solid-start/website/src/routes/_library.$project.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/solid-router' import { getProject } from '~/server/projects' import { seo } from '~/utils/seo' export const Route = createFileRoute('/_library/$project')({ loader: ({ params: { project } }) => getProject({ data: project }), head: ({ loaderData }) => ({ meta: seo({ title: `TanStack ${loaderData?.name || 'Project'}` }), }), component: () => (
), }) ================================================ FILE: e2e/solid-start/website/src/routes/_library.index.tsx ================================================ import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/_library/')({ component: Home, }) function Home() { return (

Website Landing Page

) } ================================================ FILE: e2e/solid-start/website/src/routes/_library.tsx ================================================ import { Link, Outlet, createFileRoute, useLocation, } from '@tanstack/solid-router' import { getProjects } from '~/server/projects' export const Route = createFileRoute('/_library')({ loader: async () => { const projects = await getProjects() return { libraries: projects, } }, component: Layout, }) function Layout() { const loaderData = Route.useLoaderData() const pathname = useLocation({ select: (s) => s.pathname }) return (

{pathname()}

) } ================================================ FILE: e2e/solid-start/website/src/server/document.tsx ================================================ import { notFound } from '@tanstack/solid-router' import { createServerFn } from '@tanstack/solid-start' const documents: Array<{ id: string; title: string; content: string }> = [ { id: 'overview', title: 'Overview', content: 'This is the content of the overview document', }, { id: 'getting-started', title: 'Getting Started', content: 'To get started, you need to do the following...', }, { id: 'installation', title: 'Installation', content: 'To install this package, run the following command...', }, { id: 'ref/useQueryFunction', title: 'useQuery Reference', content: 'The useQuery function is used to...', }, { id: 'ref/useMutationFunction', title: 'useMutation Reference', content: 'The useMutation function is used to...', }, ] export const getDocumentHeads = createServerFn({ method: 'GET' }).handler( async () => { await new Promise((resolve) => setTimeout(resolve, 200)) return documents.map(({ id, title }) => ({ id, title, })) }, ) export const getDocument = createServerFn({ method: 'GET' }) .inputValidator((id: string) => id) .handler(async ({ data: id }) => { await new Promise((resolve) => setTimeout(resolve, 200)) const document = documents.find((doc) => doc.id === id) if (!document) { throw notFound() } return document }) ================================================ FILE: e2e/solid-start/website/src/server/projects.tsx ================================================ import { createServerFn } from '@tanstack/solid-start' import { notFound } from '@tanstack/solid-router' import { capitalize } from '~/utils/seo' const projects = ['router', 'table', 'query', 'form', 'ranger'] export const getProjects = createServerFn({ method: 'GET' }).handler( async () => { await new Promise((resolve) => setTimeout(resolve, 200)) return projects }, ) export const getProject = createServerFn({ method: 'GET' }) .inputValidator((project: string) => project) .handler(async (ctx) => { await new Promise((resolve) => setTimeout(resolve, 200)) const selectedProject = projects.find((p) => p === ctx.data.toLowerCase()) if (!selectedProject) { throw notFound() } return { id: selectedProject, name: capitalize(selectedProject), versions: ['latest', 'v2', 'v1'], frameworks: ['solid', 'react', 'vue', 'solidjs', 'svelte'], examples: ['basic', 'kitchen-sink'], } }) ================================================ FILE: e2e/solid-start/website/src/styles/app.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ================================================ FILE: e2e/solid-start/website/src/utils/seo.ts ================================================ export const seo = ({ title, description, keywords, image, }: { title: string description?: string image?: string keywords?: string }) => { const tags = [ { title }, { name: 'description', content: description }, { name: 'keywords', content: keywords }, { name: 'twitter:title', content: title }, { name: 'twitter:description', content: description }, { name: 'twitter:creator', content: '@tannerlinsley' }, { name: 'twitter:site', content: '@tannerlinsley' }, { name: 'og:type', content: 'website' }, { name: 'og:title', content: title }, { name: 'og:description', content: description }, ...(image ? [ { name: 'twitter:image', content: image }, { name: 'twitter:card', content: 'summary_large_image' }, { name: 'og:image', content: image }, ] : []), ] return tags } export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1) ================================================ FILE: e2e/solid-start/website/src/vite-env.d.ts ================================================ declare module '*?url' { const url: string export default url } ================================================ FILE: e2e/solid-start/website/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' const routeTestId = 'selected-route-label' test('resolves to the latest version on load of a project like "/router"', async ({ page, }) => { await page.goto('/router') await expect(page.getByTestId(routeTestId)).toContainText('/router/latest') }) test('resolves to the overview docs page', async ({ page }) => { await page.goto('/router/latest/docs') await expect(page.getByTestId(routeTestId)).toContainText( '/router/latest/docs/framework/solid/overview', ) }) test('clicking on Documentation link navigates to the overview docs page', async ({ page, }) => { await page.goto('/router') await page.waitForLoadState('networkidle') const documentationLink = page.getByLabel('Documentation') await documentationLink.click() await page.waitForLoadState('networkidle') const pathname = new URL(page.url()).pathname expect(pathname).toBe('/router/latest/docs/framework/solid/overview') }) ================================================ FILE: e2e/solid-start/website/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] }, "noEmit": true } } ================================================ FILE: e2e/solid-start/website/vite.config.ts ================================================ import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/solid-start/plugin/vite' import viteSolid from 'vite-plugin-solid' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3000, }, plugins: [tailwindcss(), tanstackStart(), viteSolid({ ssr: true })], }) ================================================ FILE: e2e/vue-router/basepath-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basepath-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/basepath-file-based/index.html ================================================
================================================ FILE: e2e/vue-router/basepath-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-basepath-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/vue-query": "^5.90.0", "@tanstack/vue-query-devtools": "^6.1.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basepath-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basepath-file-based/src/main.tsx ================================================ import { createApp, h } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, basepath: '/app/', }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const app = createApp({ setup() { return () => h(RouterProvider, { router }) }, }) app.mount('#app') } ================================================ FILE: e2e/vue-router/basepath-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' const AboutRoute = AboutRouteImport.update({ id: '/about', path: '/about', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/about': typeof AboutRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/about': typeof AboutRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/about' fileRoutesByTo: FileRoutesByTo to: '/' | '/about' id: '__root__' | '/' | '/about' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute AboutRoute: typeof AboutRoute } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/about': { id: '/about' path: '/about' fullPath: '/about' preLoaderRoute: typeof AboutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AboutRoute: AboutRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/basepath-file-based/src/routes/__root.tsx ================================================ import { createRootRoute } from '@tanstack/vue-router' export const Route = createRootRoute() ================================================ FILE: e2e/vue-router/basepath-file-based/src/routes/about.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/about')({ component: RouteComponent, }) function RouteComponent() { const navigate = Route.useNavigate() return (
) } ================================================ FILE: e2e/vue-router/basepath-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: App, }) function App() { const navigate = Route.useNavigate() return (
) } ================================================ FILE: e2e/vue-router/basepath-file-based/tests/reload-document.test.ts ================================================ import { expect, test } from '@playwright/test' test('navigate() respects basepath for when reloadDocument=true', async ({ page, }) => { await page.goto(`/app/`) await expect(page.getByTestId(`home-component`)).toBeInViewport() const aboutBtn = page.getByTestId(`to-about-btn`) await aboutBtn.click() await page.waitForURL('/app/about') await expect(page.getByTestId(`about-component`)).toBeInViewport() const homeBtn = page.getByTestId(`to-home-btn`) await homeBtn.click() await page.waitForURL('/app/') await expect(page.getByTestId(`home-component`)).toBeInViewport() }) ================================================ FILE: e2e/vue-router/basepath-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basepath-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basepath-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "vue", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basepath-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ base: '/app/', plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true, }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/basic/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* node_modules dist dist-ssr *.local # Editor directories and files .vscode/* !.vscode/extensions.json .idea .DS_Store *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: e2e/vue-router/basic/index.html ================================================
================================================ FILE: e2e/vue-router/basic/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && vue-tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic/src/main.tsx ================================================ import { createApp } from 'vue' import { ErrorComponent, Link, Outlet, RouterProvider, createLink, createRootRoute, createRoute, createRouter, redirect, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' import { NotFoundError, fetchPost, fetchPosts } from './posts' import './styles.css' import type { ErrorComponentProps } from '@tanstack/vue-router' const rootRoute = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { const SvgLink = createLink('svg') return ( <>
Home {' '} Posts {' '} View Transition {' '} View Transition types {' '} Layout {' '} This Route Does Not Exist {' '}
Link in SVG
) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: IndexComponent, }) function IndexComponent() { return (

Welcome Home!

) } export const postsRoute = createRoute({ getParentRoute: () => rootRoute, path: 'posts', loader: () => fetchPosts(), }).lazy(() => import('./posts.lazy').then((d) => d.Route)) const postsIndexRoute = createRoute({ getParentRoute: () => postsRoute, path: '/', component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} function PostErrorComponent({ error }: ErrorComponentProps) { if (error instanceof NotFoundError) { return
{error.message}
} return } const postRoute = createRoute({ getParentRoute: () => postsRoute, path: '$postId', errorComponent: PostErrorComponent, loader: ({ params }) => fetchPost(params.postId), component: PostComponent, }) function PostComponent() { const post = postRoute.useLoaderData() return (

{post.value.title}


{post.value.body}
) } const layoutRoute = createRoute({ getParentRoute: () => rootRoute, id: '_layout', component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } const layout2Route = createRoute({ getParentRoute: () => layoutRoute, id: '_layout-2', component: Layout2Component, }) function Layout2Component() { return (
I'm a nested layout
Layout A Layout B
) } const layoutARoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-a', component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} const layoutBRoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-b', component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} const paramsPsRoute = createRoute({ getParentRoute: () => rootRoute, path: '/params-ps', }) const paramsPsIndexRoute = createRoute({ getParentRoute: () => paramsPsRoute, path: '/', component: function ParamsIndex() { return (

Named path params

  • /params-ps/named/$foo
  • /params-ps/named/{'prefix{$foo}'}
  • /params-ps/named/{'{$foo}suffix'}

Wildcard path params

  • /params-ps/wildcard/$
  • /params-ps/wildcard/{'prefix{$}'}
  • /params-ps/wildcard/{'{$}suffix'}
) }, }) const paramsPsNamedRoute = createRoute({ getParentRoute: () => paramsPsRoute, path: '/named', }) const paramsPsNamedIndexRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/', beforeLoad: () => { throw redirect({ to: '/params-ps' }) }, }) const paramsPsNamedFooRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/$foo', component: function ParamsNamedFoo() { const p = paramsPsNamedFooRoute.useParams() return (

ParamsNamedFoo

{JSON.stringify(p.value)}
) }, }) const paramsPsNamedFooPrefixRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/prefix{$foo}', component: function ParamsNamedFooMarkdown() { const p = paramsPsNamedFooPrefixRoute.useParams() return (

ParamsNamedFooPrefix

{JSON.stringify(p.value)}
) }, }) const paramsPsNamedFooSuffixRoute = createRoute({ getParentRoute: () => paramsPsNamedRoute, path: '/{$foo}suffix', component: function ParamsNamedFooSuffix() { const p = paramsPsNamedFooSuffixRoute.useParams() return (

ParamsNamedFooSuffix

{JSON.stringify(p.value)}
) }, }) const paramsPsWildcardRoute = createRoute({ getParentRoute: () => paramsPsRoute, path: '/wildcard', }) const paramsPsWildcardIndexRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: '/', beforeLoad: () => { throw redirect({ to: '/params-ps' }) }, }) const paramsPsWildcardSplatRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: '$', component: function ParamsWildcardSplat() { const p = paramsPsWildcardSplatRoute.useParams() return (

ParamsWildcardSplat

{JSON.stringify(p.value)}
) }, }) const paramsPsWildcardSplatPrefixRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: 'prefix{$}', component: function ParamsWildcardSplatPrefix() { const p = paramsPsWildcardSplatPrefixRoute.useParams() return (

ParamsWildcardSplatPrefix

{JSON.stringify(p.value)}
) }, }) const paramsPsWildcardSplatSuffixRoute = createRoute({ getParentRoute: () => paramsPsWildcardRoute, path: '{$}suffix', component: function ParamsWildcardSplatSuffix() { const p = paramsPsWildcardSplatSuffixRoute.useParams() return (

ParamsWildcardSplatSuffix

{JSON.stringify(p.value)}
) }, }) const routeTree = rootRoute.addChildren([ postsRoute.addChildren([postRoute, postsIndexRoute]), layoutRoute.addChildren([ layout2Route.addChildren([layoutARoute, layoutBRoute]), ]), paramsPsRoute.addChildren([ paramsPsNamedRoute.addChildren([ paramsPsNamedFooPrefixRoute, paramsPsNamedFooSuffixRoute, paramsPsNamedFooRoute, paramsPsNamedIndexRoute, ]), paramsPsWildcardRoute.addChildren([ paramsPsWildcardSplatRoute, paramsPsWildcardSplatPrefixRoute, paramsPsWildcardSplatSuffixRoute, paramsPsWildcardIndexRoute, ]), paramsPsIndexRoute, ]), indexRoute, ]) // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/basic/src/posts.lazy.tsx ================================================ import { Link, Outlet, createLazyRoute } from '@tanstack/vue-router' export const Route = createLazyRoute('/posts')({ component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}
) } ================================================ FILE: e2e/vue-router/basic/src/posts.ts ================================================ import axios from 'redaxios' export class NotFoundError extends Error {} type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } ================================================ FILE: e2e/vue-router/basic/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test('Navigating to a post page with viewTransition', async ({ page }) => { await page.getByRole('link', { name: 'View Transition', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating to a post page with viewTransition types', async ({ page, }) => { await page.getByRole('link', { name: 'View Transition types' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Link in SVG does not trigger a full page reload', async ({ page }) => { let fullPageLoad = false page.on('domcontentloaded', () => { fullPageLoad = true }) await page.getByRole('link', { name: 'Open posts from SVG' }).click() const url = `http://localhost:${PORT}/posts` await page.waitForURL(url) expect(fullPageLoad).toBeFalsy() }) ================================================ FILE: e2e/vue-router/basic/tests/params.spec.ts ================================================ import { expect, test } from '@playwright/test' test.describe('params operations + prefix/suffix', () => { test.beforeEach(async ({ page }) => { await page.goto('/params-ps') }) test.describe('named params', () => { const NAMED_PARAMS_PAIRS = [ // Test ID | Expected href { id: 'l-to-named-foo', pathname: '/params-ps/named/foo', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFoo', }, { id: 'l-to-named-prefixfoo', pathname: '/params-ps/named/prefixfoo', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFooPrefix', }, { id: 'l-to-named-foosuffix', pathname: '/params-ps/named/foosuffix', params: { foo: 'foo' }, destHeadingId: 'ParamsNamedFooSuffix', }, ] satisfies Array<{ id: string pathname: string params: Record destHeadingId: string }> test.describe('Link', () => { NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => { test(`interpolation for testid="${id}" has href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await expect(link).toHaveAttribute('href', pathname) }) }) NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => { test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await link.click() await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) }) }) }) NAMED_PARAMS_PAIRS.forEach(({ pathname, params, destHeadingId }) => { test(`on first-load to "${pathname}" has correct params`, async ({ page, }) => { await page.goto(pathname) await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) const headingEl = page.getByRole('heading', { name: destHeadingId }) await expect(headingEl).toBeVisible() const paramsEl = page.getByTestId('params-output') const paramsText = await paramsEl.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual(params) }) }) }) test.describe('wildcard param', () => { const WILDCARD_PARAM_PAIRS = [ // Test ID | Expected href { id: 'l-to-wildcard-foo', pathname: '/params-ps/wildcard/foo', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplat', }, { id: 'l-to-wildcard-prefixfoo', pathname: '/params-ps/wildcard/prefixfoo', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplatPrefix', }, { id: 'l-to-wildcard-foosuffix', pathname: '/params-ps/wildcard/foosuffix', params: { '*': 'foo', _splat: 'foo' }, destHeadingId: 'ParamsWildcardSplatSuffix', }, ] satisfies Array<{ id: string pathname: string params: Record destHeadingId: string }> test.describe('Link', () => { WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => { test(`interpolation for testid="${id}" has href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await expect(link).toHaveAttribute('href', pathname) }) }) WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => { test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({ page, }) => { const link = page.getByTestId(id) await link.click() await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) }) }) }) WILDCARD_PARAM_PAIRS.forEach(({ pathname, params, destHeadingId }) => { test(`on first-load to "${pathname}" has correct params`, async ({ page, }) => { await page.goto(pathname) await page.waitForLoadState('networkidle') const pagePathname = new URL(page.url()).pathname expect(pagePathname).toBe(pathname) const headingEl = page.getByRole('heading', { name: destHeadingId }) await expect(headingEl).toBeVisible() const paramsEl = page.getByTestId('params-output') const paramsText = await paramsEl.innerText() const paramsObj = JSON.parse(paramsText) expect(paramsObj).toEqual(params) }) }) }) }) ================================================ FILE: e2e/vue-router/basic/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "strict": true, "jsx": "preserve", "jsxImportSource": "vue", "moduleResolution": "bundler", "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, "types": ["vite/client"] }, "include": ["src"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic/vite.config.js ================================================ import { defineConfig } from 'vite' import vueJsx from '@vitejs/plugin-vue-jsx' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), vueJsx()], }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/eslint.config.js ================================================ import js from '@eslint/js' import typescript from '@typescript-eslint/eslint-plugin' import typescriptParser from '@typescript-eslint/parser' import vue from 'eslint-plugin-vue' import vueParser from 'vue-eslint-parser' export default [ js.configs.recommended, ...vue.configs['flat/recommended'], { files: ['**/*.{js,jsx,ts,tsx,vue}'], languageOptions: { parser: vueParser, parserOptions: { parser: typescriptParser, ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true, }, }, }, plugins: { '@typescript-eslint': typescript, vue, }, rules: { // Vue specific rules 'vue/multi-word-component-names': 'off', 'vue/no-unused-vars': 'error', // TypeScript rules '@typescript-eslint/no-unused-vars': 'error', '@typescript-eslint/no-explicit-any': 'warn', // General rules 'no-unused-vars': 'off', // Let TypeScript handle this }, }, { files: ['**/*.vue'], languageOptions: { parser: vueParser, parserOptions: { parser: typescriptParser, }, }, }, ] ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/index.html ================================================
================================================ FILE: e2e/vue-router/basic-esbuild-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-esbuild-file-based", "private": true, "type": "module", "scripts": { "dev": "node src/esbuild.run.js dev --port 5601", "build": "node src/esbuild.run.js build && vue-tsc --noEmit", "preview": "node src/esbuild.run.js preview", "start": "pnpm dev", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/router-plugin": "workspace:^", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "postcss": "^8.5.1", "redaxios": "^0.5.1", "vue": "^3.5.16", "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.36.0", "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@typescript-eslint/eslint-plugin": "^8.44.1", "@typescript-eslint/parser": "^8.44.1", "esbuild": "^0.27.4", "esbuild-plugin-vue3": "^0.5.1", "eslint-plugin-vue": "^9.33.0", "typescript": "^5.8.3", "vue-eslint-parser": "^9.4.3", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/postcss.config.mjs ================================================ export default { plugins: { '@tailwindcss/postcss': {}, }, } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/EditingAComponent.tsx ================================================ import { ref, defineComponent } from 'vue' import { useBlocker, useNavigate } from '@tanstack/vue-router' export const EditingAComponent = defineComponent({ setup() { const navigate = useNavigate() const input = ref('') const blocker = useBlocker({ shouldBlockFn: ({ next }) => { if (next.fullPath === '/editing-b' && input.value.length > 0) { return true } return false }, withResolver: true, }) return () => (

Editing A

{blocker.value.status === 'blocked' && ( )}
) }, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/EditingBComponent.tsx ================================================ import { ref, toValue, defineComponent } from 'vue' import { useBlocker, useNavigate } from '@tanstack/vue-router' export const EditingBComponent = defineComponent({ setup() { const navigate = useNavigate() const input = ref('') const blocker = useBlocker({ shouldBlockFn: () => !!toValue(input), withResolver: true, }) return () => (

Editing B

{blocker.value.status === 'blocked' && ( )}
) }, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/NotFoundComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/NotRemountDepsComponent.tsx ================================================ import { ref, onMounted, defineComponent } from 'vue' import { useSearch, useNavigate } from '@tanstack/vue-router' export const NotRemountDepsComponent = defineComponent({ setup() { // Component-scoped ref - will be recreated on component remount const mounts = ref(0) const search = useSearch({ from: '/notRemountDeps' }) const navigate = useNavigate() onMounted(() => { mounts.value++ }) return () => (
Search: {search.value.searchParam}
Page component mounts: {mounts.value}
) }, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/PostErrorComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/RemountDepsComponent.tsx ================================================ import { ref, onMounted, defineComponent } from 'vue' import { useSearch, useNavigate } from '@tanstack/vue-router' // Module-scoped ref to persist across component remounts const mounts = ref(0) export const RemountDepsComponent = defineComponent({ setup() { const search = useSearch({ from: '/remountDeps' }) const navigate = useNavigate() onMounted(() => { mounts.value++ }) return () => (
Search: {search.value.searchParam}
Page component mounts: {mounts.value}
) }, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/components/VueLogo.vue ================================================ ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/esbuild.config.js ================================================ import { tanstackRouter } from '@tanstack/router-plugin/esbuild' import vuePlugin from 'esbuild-plugin-vue3' export default { // ... plugins: [ vuePlugin({ cssInline: true, }), tanstackRouter({ target: 'vue', autoCodeSplitting: true, }), ], } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/esbuild.run.js ================================================ import * as esbuild from 'esbuild' import http from 'node:http' import { createReadStream } from 'node:fs' import { promises as fs } from 'node:fs' import path from 'node:path' import esbuildConfig from './esbuild.config.js' const args = process.argv.slice(2) const command = args[0] ?? 'build' function getArgValue(name) { const idx = args.indexOf(name) if (idx === -1) return return args[idx + 1] } function getPort() { const fromArg = getArgValue('--port') const fromEnv = process.env.VITE_SERVER_PORT const port = Number(fromArg ?? fromEnv ?? 5601) if (!Number.isFinite(port) || port <= 0) { throw new Error(`Invalid port: ${String(fromArg ?? fromEnv)}`) } return port } function getMimeType(filePath) { const ext = path.extname(filePath).toLowerCase() switch (ext) { case '.html': return 'text/html; charset=utf-8' case '.js': return 'text/javascript; charset=utf-8' case '.css': return 'text/css; charset=utf-8' case '.json': return 'application/json; charset=utf-8' case '.map': return 'application/json; charset=utf-8' case '.svg': return 'image/svg+xml' case '.png': return 'image/png' case '.jpg': case '.jpeg': return 'image/jpeg' case '.webp': return 'image/webp' case '.ico': return 'image/x-icon' case '.woff': return 'font/woff' case '.woff2': return 'font/woff2' case '.ttf': return 'font/ttf' default: return 'application/octet-stream' } } async function fileExists(filePath) { try { await fs.stat(filePath) return true } catch { return false } } async function buildOnce() { const entry = path.join(process.cwd(), 'src/main.tsx') const outfile = path.join(process.cwd(), 'dist/main.js') const inject = [path.join(process.cwd(), 'src/jsx-shim.ts')] await esbuild.build({ entryPoints: [entry], bundle: true, outfile, platform: 'browser', format: 'esm', jsx: 'transform', jsxFactory: 'h', jsxFragment: 'Fragment', inject, sourcemap: true, define: { 'process.env.NODE_ENV': JSON.stringify( process.env.VITE_NODE_ENV ?? process.env.NODE_ENV ?? 'development', ), }, ...esbuildConfig, }) } function startStaticServer({ port }) { const rootDir = process.cwd() const indexPath = path.join(rootDir, 'index.html') const server = http.createServer(async (req, res) => { try { const url = new URL( req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`, ) const requestPath = decodeURIComponent(url.pathname) const normalizedPath = path.posix .normalize(requestPath) .replace(/^(\.\.(\/|\\|$))+/, '') .replace(/^\/+/, '') const filePath = path.join(rootDir, normalizedPath) const hasExtension = path.posix.basename(normalizedPath).includes('.') const servedPath = (await fileExists(filePath)) && !(await fs.stat(filePath)).isDirectory() ? filePath : !hasExtension ? indexPath : undefined if (!servedPath) { res.statusCode = 404 res.end('Not found') return } res.statusCode = 200 res.setHeader('Content-Type', getMimeType(servedPath)) createReadStream(servedPath).pipe(res) } catch (err) { res.statusCode = 500 res.end(String(err)) } }) return new Promise((resolve) => { server.listen(port, () => { console.log(`http://localhost:${port}`) resolve(server) }) }) } async function main() { if (command === 'build') { await buildOnce() return } if (command === 'preview') { const port = getPort() await startStaticServer({ port }) return } if (command === 'dev') { const port = getPort() const inject = [path.join(process.cwd(), 'src/jsx-shim.ts')] const ctx = await esbuild.context({ entryPoints: [path.join(process.cwd(), 'src/main.tsx')], bundle: true, outfile: path.join(process.cwd(), 'dist/main.js'), platform: 'browser', format: 'esm', jsx: 'transform', jsxFactory: 'h', jsxFragment: 'Fragment', inject, sourcemap: true, define: { 'process.env.NODE_ENV': JSON.stringify( process.env.VITE_NODE_ENV ?? process.env.NODE_ENV ?? 'development', ), }, ...esbuildConfig, }) await ctx.watch() await startStaticServer({ port }) return } throw new Error(`Unknown command: ${command}`) } main().catch((err) => { console.error(err) process.exit(1) }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/jsx-shim.ts ================================================ import { Fragment, h } from 'vue' export { Fragment, h } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/main.tsx ================================================ import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import { createApp } from 'vue' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>('https://jsonplaceholder.typicode.com/posts') .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' import { Route as SfcComponentRouteImport } from './routes/sfcComponent' import { Route as RemountDepsRouteImport } from './routes/remountDeps' import { Route as PostsRouteImport } from './routes/posts' import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' import { Route as EditingBRouteImport } from './routes/editing-b' import { Route as EditingARouteImport } from './routes/editing-a' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' import { Route as groupInsideRouteImport } from './routes/(group)/inside' import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' const Char45824Char54620Char48124Char44397Route = Char45824Char54620Char48124Char44397RouteImport.update({ id: '/대한민국', path: '/대한민국', getParentRoute: () => rootRouteImport, } as any) const SfcComponentRoute = SfcComponentRouteImport.update({ id: '/sfcComponent', path: '/sfcComponent', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/sfcComponent.component.vue'), 'default', ), }) const RemountDepsRoute = RemountDepsRouteImport.update({ id: '/remountDeps', path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ id: '/notRemountDeps', path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) const EditingBRoute = EditingBRouteImport.update({ id: '/editing-b', path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) const EditingARoute = EditingARouteImport.update({ id: '/editing-a', path: '/editing-a', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const groupLazyinsideRoute = groupLazyinsideRouteImport.update({ id: '/(group)/lazyinside', path: '/lazyinside', getParentRoute: () => rootRouteImport, } as any) const groupInsideRoute = groupInsideRouteImport.update({ id: '/(group)/inside', path: '/inside', getParentRoute: () => rootRouteImport, } as any) const groupLayoutRoute = groupLayoutRouteImport.update({ id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport.update({ id: '/(another-group)/onlyrouteinside', path: '/onlyrouteinside', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ id: '/posts_/$postId/edit', path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ id: '/(group)/subfolder/inside', path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ id: '/insidelayout', path: '/insidelayout', getParentRoute: () => groupLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/sfcComponent': typeof SfcComponentRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/remountDeps': typeof RemountDepsRoute '/sfcComponent': typeof SfcComponentRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/sfcComponent': typeof SfcComponentRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/(another-group)/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/(group)/_layout': typeof groupLayoutRouteWithChildren '/(group)/inside': typeof groupInsideRoute '/(group)/lazyinside': typeof groupLazyinsideRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/(group)/_layout/insidelayout': typeof groupLayoutInsidelayoutRoute '/(group)/subfolder/inside': typeof groupSubfolderInsideRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute '/posts_/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/sfcComponent' | '/대한민국' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/posts/' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/posts/$postId/edit' fileRoutesByTo: FileRoutesByTo to: | '/' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/remountDeps' | '/sfcComponent' | '/대한민국' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/posts' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/posts/$postId/edit' id: | '__root__' | '/' | '/_layout' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/sfcComponent' | '/대한민국' | '/(another-group)/onlyrouteinside' | '/(group)/_layout' | '/(group)/inside' | '/(group)/lazyinside' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/(group)/_layout/insidelayout' | '/(group)/subfolder/inside' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' | '/posts_/$postId/edit' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren EditingARoute: typeof EditingARoute EditingBRoute: typeof EditingBRoute NotRemountDepsRoute: typeof NotRemountDepsRoute PostsRoute: typeof PostsRouteWithChildren RemountDepsRoute: typeof RemountDepsRoute SfcComponentRoute: typeof SfcComponentRoute Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute groupLayoutRoute: typeof groupLayoutRouteWithChildren groupInsideRoute: typeof groupInsideRoute groupLazyinsideRoute: typeof groupLazyinsideRoute groupSubfolderInsideRoute: typeof groupSubfolderInsideRoute PostsPostIdEditRoute: typeof PostsPostIdEditRoute } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/대한민국': { id: '/대한민국' path: '/대한민국' fullPath: '/대한민국' preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport parentRoute: typeof rootRouteImport } '/sfcComponent': { id: '/sfcComponent' path: '/sfcComponent' fullPath: '/sfcComponent' preLoaderRoute: typeof SfcComponentRouteImport parentRoute: typeof rootRouteImport } '/remountDeps': { id: '/remountDeps' path: '/remountDeps' fullPath: '/remountDeps' preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { id: '/notRemountDeps' path: '/notRemountDeps' fullPath: '/notRemountDeps' preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } '/editing-b': { id: '/editing-b' path: '/editing-b' fullPath: '/editing-b' preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/editing-a': { id: '/editing-a' path: '/editing-a' fullPath: '/editing-a' preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/(group)/lazyinside': { id: '/(group)/lazyinside' path: '/lazyinside' fullPath: '/lazyinside' preLoaderRoute: typeof groupLazyinsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/inside': { id: '/(group)/inside' path: '/inside' fullPath: '/inside' preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout': { id: '/(group)/_layout' path: '' fullPath: '' preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } '/(another-group)/onlyrouteinside': { id: '/(another-group)/onlyrouteinside' path: '/onlyrouteinside' fullPath: '/onlyrouteinside' preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport parentRoute: typeof rootRouteImport } '/posts_/$postId/edit': { id: '/posts_/$postId/edit' path: '/posts/$postId/edit' fullPath: '/posts/$postId/edit' preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } '/(group)/subfolder/inside': { id: '/(group)/subfolder/inside' path: '/subfolder/inside' fullPath: '/subfolder/inside' preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout/insidelayout': { id: '/(group)/_layout/insidelayout' path: '/insidelayout' fullPath: '/insidelayout' preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport parentRoute: typeof groupLayoutRoute } } } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) interface groupLayoutRouteChildren { groupLayoutInsidelayoutRoute: typeof groupLayoutInsidelayoutRoute } const groupLayoutRouteChildren: groupLayoutRouteChildren = { groupLayoutInsidelayoutRoute: groupLayoutInsidelayoutRoute, } const groupLayoutRouteWithChildren = groupLayoutRoute._addFileChildren( groupLayoutRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, EditingARoute: EditingARoute, EditingBRoute: EditingBRoute, NotRemountDepsRoute: NotRemountDepsRoute, PostsRoute: PostsRouteWithChildren, RemountDepsRoute: RemountDepsRoute, SfcComponentRoute: SfcComponentRoute, Char45824Char54620Char48124Char44397Route: Char45824Char54620Char48124Char44397Route, anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute, groupLayoutRoute: groupLayoutRouteWithChildren, groupInsideRoute: groupInsideRoute, groupLazyinsideRoute: groupLazyinsideRoute, groupSubfolderInsideRoute: groupSubfolderInsideRoute, PostsPostIdEditRoute: PostsPostIdEditRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/(another-group)/onlyrouteinside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(another-group)/onlyrouteinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: OnlyRouteInsideComponent, }) const routeApi = getRouteApi('/(another-group)/onlyrouteinside') function OnlyRouteInsideComponent() { const searchViaHook = useSearch({ from: '/(another-group)/onlyrouteinside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/(group)/_layout.insidelayout.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/_layout/insidelayout')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: InsideLayoutComponent, }) const routeApi = getRouteApi('/(group)/_layout/insidelayout') function InsideLayoutComponent() { const searchViaHook = useSearch({ from: '/(group)/_layout/insidelayout' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/(group)/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/(group)/_layout')({ component: GroupLayoutComponent, }) function GroupLayoutComponent() { return (
Layout inside group
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/(group)/inside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: InsideComponent, }) const routeApi = getRouteApi('/(group)/inside') function InsideComponent() { const searchViaHook = useSearch({ from: '/(group)/inside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/(group)/lazyinside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/lazyinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: LazyInsideComponent, }) const routeApi = getRouteApi('/(group)/lazyinside') function LazyInsideComponent() { const searchViaHook = useSearch({ from: '/(group)/lazyinside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/(group)/subfolder/inside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/subfolder/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: SubfolderInsideComponent, }) const routeApi = getRouteApi('/(group)/subfolder/inside') function SubfolderInsideComponent() { const searchViaHook = useSearch({ from: '/(group)/subfolder/inside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, useCanGoBack, useRouter, useRouterState, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' import NotFoundComponent from '../components/NotFoundComponent.vue' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: NotFoundComponent, }) function RootComponent() { const router = useRouter() const canGoBack = useCanGoBack() // test useRouterState doesn't crash client side navigation const _state = useRouterState() return ( <>
Home Posts Layout Only Route Inside Group Inside Group Inside Subfolder Inside Group Inside Group Inside Layout Lazy Inside Group unicode path This Route Does Not Exist

) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: Layout2Component, }) function Layout2Component() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/editing-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { EditingAComponent } from '../components/EditingAComponent' export const Route = createFileRoute('/editing-a')({ component: EditingAComponent, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/editing-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { EditingBComponent } from '../components/EditingBComponent' export const Route = createFileRoute('/editing-b')({ component: EditingBComponent, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import VueLogo from '../components/VueLogo.vue' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/notRemountDeps.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { NotRemountDepsComponent } from '../components/NotRemountDepsComponent' export const Route = createFileRoute('/notRemountDeps')({ validateSearch: (search: Record) => ({ searchParam: (search.searchParam as string) || '', }), loaderDeps(opts) { return opts.search }, remountDeps(opts) { return opts.params }, component: NotRemountDepsComponent, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import PostErrorComponent from '../components/PostErrorComponent.vue' import { fetchPost } from '../posts' import type { PostType } from '../posts' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), component: PostComponent, errorComponent: PostErrorComponent, notFoundComponent: () =>

Post not found

, }) function PostComponent() { const post = Route.useLoaderData() return (

{(post.value as PostType).title}

{(post.value as PostType).body}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' import { fetchPosts } from '../posts' import type { PostType } from '../posts' export const Route = createFileRoute('/posts')({ head: () => ({ meta: [ { title: 'Posts page', }, ], }), loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...(posts.value as Array), { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => (
  • {post.title.substring(0, 20)}
  • ))}

) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/posts_.$postId.edit.tsx ================================================ import { createFileRoute, getRouteApi, useParams } from '@tanstack/vue-router' export const Route = createFileRoute('/posts_/$postId/edit')({ component: PostEditComponent, }) const api = getRouteApi('/posts_/$postId/edit') function PostEditComponent() { const paramsViaApi = api.useParams() const paramsViaHook = useParams({ from: '/posts_/$postId/edit' }) const paramsViaRouteHook = api.useParams() return (
{paramsViaHook.value.postId}
{paramsViaRouteHook.value.postId}
{paramsViaApi.value.postId}
) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/remountDeps.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { RemountDepsComponent } from '../components/RemountDepsComponent' export const Route = createFileRoute('/remountDeps')({ validateSearch: (search: Record) => ({ searchParam: (search.searchParam as string) || '', }), loaderDeps(opts) { return opts.search }, remountDeps(opts) { return opts.search }, component: RemountDepsComponent, }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/sfcComponent.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/sfcComponent.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/sfcComponent')({ component: RouteComponent, }) function RouteComponent() { return
Will be overwritten by the SFC component!
} ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/routes/대한민국.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/대한민국')({ component: UnicodeComponent, }) function UnicodeComponent() { return (

Hello "/대한민국"!


) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/styles.css ================================================ @source "./**/*.tsx"; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/src/vue-shims.d.ts ================================================ declare module '*.vue' { import type { DefineComponent } from 'vue' const component: DefineComponent<{}, {}, any> export default component } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test("useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test('useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test("legacy useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('legacy useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('legacy Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('useCanGoBack correctly disables back button', async ({ page }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } expect(await getBackButtonDisabled()).toBe(true) await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goForward() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) test('useCanGoBack correctly disables back button, using router.history and window.history', async ({ page, }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(true) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) const testCases = [ { description: 'Navigating to a route inside a route group', testId: 'link-to-route-inside-group', }, { description: 'Navigating to a route inside a subfolder inside a route group ', testId: 'link-to-route-inside-group-inside-subfolder', }, { description: 'Navigating to a route inside a route group inside a layout', testId: 'link-to-route-inside-group-inside-layout', }, { description: 'Navigating to a lazy route inside a route group', testId: 'link-to-lazy-route-inside-group', }, { description: 'Navigating to the only route inside a route group ', testId: 'link-to-only-route-inside-group', }, ] testCases.forEach(({ description, testId }) => { test(description, async ({ page }) => { await page.getByTestId(testId).click() await expect(page.getByTestId('search-via-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-hook')).toContainText( 'world', ) await expect(page.getByTestId('search-via-route-api')).toContainText( 'world', ) }) }) test('navigating to an unnested route', async ({ page }) => { const postId = 'hello-world' page.goto(`/posts/${postId}/edit`) await expect(page.getByTestId('params-via-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-api')).toContainText(postId) }) test('Should change title on client side navigation', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await expect(page).toHaveTitle('Posts page') }) test('Should change post navigating back and forth', async ({ page }) => { await page.goto('/posts/1') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await page.getByRole('link', { name: 'qui est esse' }).click() await expect(page.getByTestId('post-title')).toContainText('qui est esse') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toContainText('sunt aut facere') }) test('Should not remount deps when remountDeps does not change ', async ({ page, }) => { await page.goto('/notRemountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) }) test('Should remount deps when remountDeps does change ', async ({ page }) => { await page.goto('/remountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 2', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 3', ) }) test.describe('Unicode route rendering', () => { test('should render non-latin route correctly', async ({ page, baseURL }) => { await page.goto('/대한민국') await expect(page.locator('body')).toContainText('Hello "/대한민국"!') expect(page.url()).toBe(`${baseURL}/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD`) }) }) ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-esbuild-file-based/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "strict": true, "moduleResolution": "bundler", "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, "types": ["vite/client"], "jsx": "preserve", "jsxImportSource": "vue" }, "include": ["src", "tests"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/eslint.config.js ================================================ import js from '@eslint/js' import typescript from '@typescript-eslint/eslint-plugin' import typescriptParser from '@typescript-eslint/parser' import vue from 'eslint-plugin-vue' import vueParser from 'vue-eslint-parser' export default [ js.configs.recommended, ...vue.configs['flat/recommended'], { files: ['**/*.{js,jsx,ts,tsx,vue}'], languageOptions: { parser: vueParser, parserOptions: { parser: typescriptParser, ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true, }, }, }, plugins: { '@typescript-eslint': typescript, vue, }, rules: { // Vue specific rules 'vue/multi-word-component-names': 'off', 'vue/no-unused-vars': 'error', // TypeScript rules '@typescript-eslint/no-unused-vars': 'error', '@typescript-eslint/no-explicit-any': 'warn', // General rules 'no-unused-vars': 'off', // Let TypeScript handle this }, }, { files: ['**/*.vue'], languageOptions: { parser: vueParser, parserOptions: { parser: typescriptParser, }, }, }, ] ================================================ FILE: e2e/vue-router/basic-file-based-jsx/index.html ================================================
================================================ FILE: e2e/vue-router/basic-file-based-jsx/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-file-based-jsx", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && vue-tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16", "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.36.0", "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@typescript-eslint/eslint-plugin": "^8.44.1", "@typescript-eslint/parser": "^8.44.1", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "eslint-plugin-vue": "^9.33.0", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-eslint-parser": "^9.4.3", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/EditingAComponent.tsx ================================================ import { ref, defineComponent } from 'vue' import { useBlocker, useNavigate } from '@tanstack/vue-router' export const EditingAComponent = defineComponent({ setup() { const navigate = useNavigate() const input = ref('') const blocker = useBlocker({ shouldBlockFn: ({ next }) => { if (next.fullPath === '/editing-b' && input.value.length > 0) { return true } return false }, withResolver: true, }) return () => (

Editing A

{blocker.value.status === 'blocked' && ( )}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/EditingBComponent.tsx ================================================ import { ref, toValue, defineComponent } from 'vue' import { useBlocker, useNavigate } from '@tanstack/vue-router' export const EditingBComponent = defineComponent({ setup() { const navigate = useNavigate() const input = ref('') const blocker = useBlocker({ shouldBlockFn: () => !!toValue(input), withResolver: true, }) return () => (

Editing B

{blocker.value.status === 'blocked' && ( )}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/NotFoundComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/NotRemountDepsComponent.tsx ================================================ import { ref, onMounted, defineComponent } from 'vue' import { useSearch, useNavigate } from '@tanstack/vue-router' export const NotRemountDepsComponent = defineComponent({ setup() { // Component-scoped ref - will be recreated on component remount const mounts = ref(0) const search = useSearch({ from: '/notRemountDeps' }) const navigate = useNavigate() onMounted(() => { mounts.value++ }) return () => (
Search: {search.value.searchParam}
Page component mounts: {mounts.value}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/PostErrorComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/RemountDepsComponent.tsx ================================================ import { ref, onMounted, defineComponent } from 'vue' import { useSearch, useNavigate } from '@tanstack/vue-router' // Module-scoped ref to persist across component remounts const mounts = ref(0) export const RemountDepsComponent = defineComponent({ setup() { const search = useSearch({ from: '/remountDeps' }) const navigate = useNavigate() onMounted(() => { mounts.value++ }) return () => (
Search: {search.value.searchParam}
Page component mounts: {mounts.value}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/components/VueLogo.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/main.tsx ================================================ import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import './styles.css' import { createApp } from 'vue' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/posts.tsx ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' import { Route as SfcComponentRouteImport } from './routes/sfcComponent' import { Route as RemountDepsRouteImport } from './routes/remountDeps' import { Route as PostsRouteImport } from './routes/posts' import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' import { Route as EditingBRouteImport } from './routes/editing-b' import { Route as EditingARouteImport } from './routes/editing-a' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' import { Route as groupInsideRouteImport } from './routes/(group)/inside' import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' const Char45824Char54620Char48124Char44397Route = Char45824Char54620Char48124Char44397RouteImport.update({ id: '/대한민국', path: '/대한민국', getParentRoute: () => rootRouteImport, } as any) const SfcComponentRoute = SfcComponentRouteImport.update({ id: '/sfcComponent', path: '/sfcComponent', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/sfcComponent.component.vue'), 'default', ), }) const RemountDepsRoute = RemountDepsRouteImport.update({ id: '/remountDeps', path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ id: '/notRemountDeps', path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) const EditingBRoute = EditingBRouteImport.update({ id: '/editing-b', path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) const EditingARoute = EditingARouteImport.update({ id: '/editing-a', path: '/editing-a', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const groupLazyinsideRoute = groupLazyinsideRouteImport.update({ id: '/(group)/lazyinside', path: '/lazyinside', getParentRoute: () => rootRouteImport, } as any) const groupInsideRoute = groupInsideRouteImport.update({ id: '/(group)/inside', path: '/inside', getParentRoute: () => rootRouteImport, } as any) const groupLayoutRoute = groupLayoutRouteImport.update({ id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport.update({ id: '/(another-group)/onlyrouteinside', path: '/onlyrouteinside', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ id: '/posts_/$postId/edit', path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ id: '/(group)/subfolder/inside', path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ id: '/insidelayout', path: '/insidelayout', getParentRoute: () => groupLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/sfcComponent': typeof SfcComponentRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/remountDeps': typeof RemountDepsRoute '/sfcComponent': typeof SfcComponentRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/sfcComponent': typeof SfcComponentRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/(another-group)/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/(group)/_layout': typeof groupLayoutRouteWithChildren '/(group)/inside': typeof groupInsideRoute '/(group)/lazyinside': typeof groupLazyinsideRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/(group)/_layout/insidelayout': typeof groupLayoutInsidelayoutRoute '/(group)/subfolder/inside': typeof groupSubfolderInsideRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute '/posts_/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/sfcComponent' | '/대한민국' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/posts/' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/posts/$postId/edit' fileRoutesByTo: FileRoutesByTo to: | '/' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/remountDeps' | '/sfcComponent' | '/대한민국' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/posts' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/posts/$postId/edit' id: | '__root__' | '/' | '/_layout' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/sfcComponent' | '/대한민국' | '/(another-group)/onlyrouteinside' | '/(group)/_layout' | '/(group)/inside' | '/(group)/lazyinside' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/(group)/_layout/insidelayout' | '/(group)/subfolder/inside' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' | '/posts_/$postId/edit' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren EditingARoute: typeof EditingARoute EditingBRoute: typeof EditingBRoute NotRemountDepsRoute: typeof NotRemountDepsRoute PostsRoute: typeof PostsRouteWithChildren RemountDepsRoute: typeof RemountDepsRoute SfcComponentRoute: typeof SfcComponentRoute Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute groupLayoutRoute: typeof groupLayoutRouteWithChildren groupInsideRoute: typeof groupInsideRoute groupLazyinsideRoute: typeof groupLazyinsideRoute groupSubfolderInsideRoute: typeof groupSubfolderInsideRoute PostsPostIdEditRoute: typeof PostsPostIdEditRoute } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/대한민국': { id: '/대한민국' path: '/대한민국' fullPath: '/대한민국' preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport parentRoute: typeof rootRouteImport } '/sfcComponent': { id: '/sfcComponent' path: '/sfcComponent' fullPath: '/sfcComponent' preLoaderRoute: typeof SfcComponentRouteImport parentRoute: typeof rootRouteImport } '/remountDeps': { id: '/remountDeps' path: '/remountDeps' fullPath: '/remountDeps' preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { id: '/notRemountDeps' path: '/notRemountDeps' fullPath: '/notRemountDeps' preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } '/editing-b': { id: '/editing-b' path: '/editing-b' fullPath: '/editing-b' preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/editing-a': { id: '/editing-a' path: '/editing-a' fullPath: '/editing-a' preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/(group)/lazyinside': { id: '/(group)/lazyinside' path: '/lazyinside' fullPath: '/lazyinside' preLoaderRoute: typeof groupLazyinsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/inside': { id: '/(group)/inside' path: '/inside' fullPath: '/inside' preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout': { id: '/(group)/_layout' path: '' fullPath: '' preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } '/(another-group)/onlyrouteinside': { id: '/(another-group)/onlyrouteinside' path: '/onlyrouteinside' fullPath: '/onlyrouteinside' preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport parentRoute: typeof rootRouteImport } '/posts_/$postId/edit': { id: '/posts_/$postId/edit' path: '/posts/$postId/edit' fullPath: '/posts/$postId/edit' preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } '/(group)/subfolder/inside': { id: '/(group)/subfolder/inside' path: '/subfolder/inside' fullPath: '/subfolder/inside' preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout/insidelayout': { id: '/(group)/_layout/insidelayout' path: '/insidelayout' fullPath: '/insidelayout' preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport parentRoute: typeof groupLayoutRoute } } } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) interface groupLayoutRouteChildren { groupLayoutInsidelayoutRoute: typeof groupLayoutInsidelayoutRoute } const groupLayoutRouteChildren: groupLayoutRouteChildren = { groupLayoutInsidelayoutRoute: groupLayoutInsidelayoutRoute, } const groupLayoutRouteWithChildren = groupLayoutRoute._addFileChildren( groupLayoutRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, EditingARoute: EditingARoute, EditingBRoute: EditingBRoute, NotRemountDepsRoute: NotRemountDepsRoute, PostsRoute: PostsRouteWithChildren, RemountDepsRoute: RemountDepsRoute, SfcComponentRoute: SfcComponentRoute, Char45824Char54620Char48124Char44397Route: Char45824Char54620Char48124Char44397Route, anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute, groupLayoutRoute: groupLayoutRouteWithChildren, groupInsideRoute: groupInsideRoute, groupLazyinsideRoute: groupLazyinsideRoute, groupSubfolderInsideRoute: groupSubfolderInsideRoute, PostsPostIdEditRoute: PostsPostIdEditRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/(another-group)/onlyrouteinside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(another-group)/onlyrouteinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: OnlyRouteInsideComponent, }) const routeApi = getRouteApi('/(another-group)/onlyrouteinside') function OnlyRouteInsideComponent() { const searchViaHook = useSearch({ from: '/(another-group)/onlyrouteinside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/(group)/_layout.insidelayout.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/_layout/insidelayout')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: InsideLayoutComponent, }) const routeApi = getRouteApi('/(group)/_layout/insidelayout') function InsideLayoutComponent() { const searchViaHook = useSearch({ from: '/(group)/_layout/insidelayout' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/(group)/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/(group)/_layout')({ component: GroupLayoutComponent, }) function GroupLayoutComponent() { return (
Layout inside group
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/(group)/inside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: InsideComponent, }) const routeApi = getRouteApi('/(group)/inside') function InsideComponent() { const searchViaHook = useSearch({ from: '/(group)/inside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/(group)/lazyinside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/lazyinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: LazyInsideComponent, }) const routeApi = getRouteApi('/(group)/lazyinside') function LazyInsideComponent() { const searchViaHook = useSearch({ from: '/(group)/lazyinside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/(group)/subfolder/inside.tsx ================================================ import { createFileRoute, getRouteApi, useSearch } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/subfolder/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), component: SubfolderInsideComponent, }) const routeApi = getRouteApi('/(group)/subfolder/inside') function SubfolderInsideComponent() { const searchViaHook = useSearch({ from: '/(group)/subfolder/inside' }) const searchViaRouteHook = routeApi.useSearch() const searchViaRouteApi = routeApi.useSearch() return (
{searchViaHook.value.hello}
{searchViaRouteHook.value.hello}
{searchViaRouteApi.value.hello}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, useCanGoBack, useRouter, useRouterState, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' import NotFoundComponent from '../components/NotFoundComponent.vue' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: NotFoundComponent, }) function RootComponent() { const router = useRouter() const canGoBack = useCanGoBack() // test useRouterState doesn't crash client side navigation const _state = useRouterState() return ( <>
Home Posts Layout Only Route Inside Group Inside Group Inside Subfolder Inside Group Inside Group Inside Layout Lazy Inside Group unicode path This Route Does Not Exist

) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: Layout2Component, }) function Layout2Component() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/editing-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { EditingAComponent } from '../components/EditingAComponent' export const Route = createFileRoute('/editing-a')({ component: EditingAComponent, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/editing-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { EditingBComponent } from '../components/EditingBComponent' export const Route = createFileRoute('/editing-b')({ component: EditingBComponent, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import VueLogo from '../components/VueLogo.vue' export const Route = createFileRoute('/')({ component: IndexComponent, }) function IndexComponent() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/notRemountDeps.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { NotRemountDepsComponent } from '../components/NotRemountDepsComponent' export const Route = createFileRoute('/notRemountDeps')({ validateSearch: (search: Record) => ({ searchParam: (search.searchParam as string) || '', }), loaderDeps(opts) { return opts.search }, remountDeps(opts) { return opts.params }, component: NotRemountDepsComponent, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import PostErrorComponent from '../components/PostErrorComponent.vue' import { fetchPost } from '../posts' import type { PostType } from '../posts' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), component: PostComponent, errorComponent: PostErrorComponent, notFoundComponent: () =>

Post not found

, }) function PostComponent() { const post = Route.useLoaderData() return (

{(post.value as PostType).title}

{(post.value as PostType).body}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' import { fetchPosts } from '../posts' import type { PostType } from '../posts' export const Route = createFileRoute('/posts')({ head: () => ({ meta: [ { title: 'Posts page', }, ], }), loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...(posts.value as Array), { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => (
  • {post.title.substring(0, 20)}
  • ))}

) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/posts_.$postId.edit.tsx ================================================ import { createFileRoute, getRouteApi, useParams } from '@tanstack/vue-router' export const Route = createFileRoute('/posts_/$postId/edit')({ component: PostEditComponent, }) const api = getRouteApi('/posts_/$postId/edit') function PostEditComponent() { const paramsViaApi = api.useParams() const paramsViaHook = useParams({ from: '/posts_/$postId/edit' }) const paramsViaRouteHook = api.useParams() return (
{paramsViaHook.value.postId}
{paramsViaRouteHook.value.postId}
{paramsViaApi.value.postId}
) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/remountDeps.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { RemountDepsComponent } from '../components/RemountDepsComponent' export const Route = createFileRoute('/remountDeps')({ validateSearch: (search: Record) => ({ searchParam: (search.searchParam as string) || '', }), loaderDeps(opts) { return opts.search }, remountDeps(opts) { return opts.search }, component: RemountDepsComponent, }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/sfcComponent.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/sfcComponent.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/sfcComponent')({ component: RouteComponent, }) function RouteComponent() { return
Will be overwritten by the SFC component!
} ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/routes/대한민국.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/대한민국')({ component: UnicodeComponent, }) function UnicodeComponent() { return (

Hello "/대한민국"!


) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source "./**/*.tsx"; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test("useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test('useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test("legacy useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('legacy useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('legacy Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('useCanGoBack correctly disables back button', async ({ page }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } expect(await getBackButtonDisabled()).toBe(true) await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goForward() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) test('useCanGoBack correctly disables back button, using router.history and window.history', async ({ page, }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(true) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) const testCases = [ { description: 'Navigating to a route inside a route group', testId: 'link-to-route-inside-group', }, { description: 'Navigating to a route inside a subfolder inside a route group ', testId: 'link-to-route-inside-group-inside-subfolder', }, { description: 'Navigating to a route inside a route group inside a layout', testId: 'link-to-route-inside-group-inside-layout', }, { description: 'Navigating to a lazy route inside a route group', testId: 'link-to-lazy-route-inside-group', }, { description: 'Navigating to the only route inside a route group ', testId: 'link-to-only-route-inside-group', }, ] testCases.forEach(({ description, testId }) => { test(description, async ({ page }) => { await page.getByTestId(testId).click() await expect(page.getByTestId('search-via-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-hook')).toContainText( 'world', ) await expect(page.getByTestId('search-via-route-api')).toContainText( 'world', ) }) }) test('navigating to an unnested route', async ({ page }) => { const postId = 'hello-world' page.goto(`/posts/${postId}/edit`) await expect(page.getByTestId('params-via-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-api')).toContainText(postId) }) test('Should change title on client side navigation', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await expect(page).toHaveTitle('Posts page') }) test('Should change post navigating back and forth', async ({ page }) => { await page.goto('/posts/1') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await page.getByRole('link', { name: 'qui est esse' }).click() await expect(page.getByTestId('post-title')).toContainText('qui est esse') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toContainText('sunt aut facere') }) test('Should not remount deps when remountDeps does not change ', async ({ page, }) => { await page.goto('/notRemountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) }) test('Should remount deps when remountDeps does change ', async ({ page }) => { await page.goto('/remountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 2', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 3', ) }) test.describe('Unicode route rendering', () => { test('should render non-latin route correctly', async ({ page, baseURL }) => { await page.goto('/대한민국') await expect(page.locator('body')).toContainText('Hello "/대한민국"!') expect(page.url()).toBe(`${baseURL}/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD`) }) }) ================================================ FILE: e2e/vue-router/basic-file-based-jsx/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "strict": true, "moduleResolution": "bundler", "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, "types": ["vite/client"], "jsx": "preserve", "jsxImportSource": "vue" }, "include": ["src", "tests"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-file-based-jsx/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true, }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/eslint.config.js ================================================ import js from '@eslint/js' import typescript from '@typescript-eslint/eslint-plugin' import typescriptParser from '@typescript-eslint/parser' import vue from 'eslint-plugin-vue' import vueParser from 'vue-eslint-parser' export default [ js.configs.recommended, ...vue.configs['flat/recommended'], { files: ['**/*.{js,jsx,ts,tsx,vue}'], languageOptions: { parser: vueParser, parserOptions: { parser: typescriptParser, ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true, }, }, }, plugins: { '@typescript-eslint': typescript, vue, }, rules: { // Vue specific rules 'vue/multi-word-component-names': 'off', 'vue/no-unused-vars': 'error', // TypeScript rules '@typescript-eslint/no-unused-vars': 'error', '@typescript-eslint/no-explicit-any': 'warn', // General rules 'no-unused-vars': 'off', // Let TypeScript handle this }, }, { files: ['**/*.vue'], languageOptions: { parser: vueParser, parserOptions: { parser: typescriptParser, }, }, }, ] ================================================ FILE: e2e/vue-router/basic-file-based-sfc/index.html ================================================
================================================ FILE: e2e/vue-router/basic-file-based-sfc/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-file-sfc", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && vue-tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/EditingAComponent.tsx ================================================ import { ref, defineComponent } from 'vue' import { useBlocker, useNavigate } from '@tanstack/vue-router' export const EditingAComponent = defineComponent({ setup() { const navigate = useNavigate() const input = ref('') const blocker = useBlocker({ shouldBlockFn: ({ next }) => { if (next.fullPath === '/editing-b' && input.value.length > 0) { return true } return false }, withResolver: true, }) return () => (

Editing A

{blocker.value.status === 'blocked' && ( )}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/EditingBComponent.tsx ================================================ import { ref, toValue, defineComponent } from 'vue' import { useBlocker, useNavigate } from '@tanstack/vue-router' export const EditingBComponent = defineComponent({ setup() { const navigate = useNavigate() const input = ref('') const blocker = useBlocker({ shouldBlockFn: () => !!toValue(input), withResolver: true, }) return () => (

Editing B

{blocker.value.status === 'blocked' && ( )}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/NotFoundComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/NotRemountDepsComponent.tsx ================================================ import { ref, onMounted, defineComponent } from 'vue' import { useSearch, useNavigate } from '@tanstack/vue-router' export const NotRemountDepsComponent = defineComponent({ setup() { // Component-scoped ref - will be recreated on component remount const mounts = ref(0) const search = useSearch({ from: '/notRemountDeps' }) const navigate = useNavigate() onMounted(() => { mounts.value++ }) return () => (
Search: {search.value.searchParam}
Page component mounts: {mounts.value}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/PostErrorComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/RemountDepsComponent.tsx ================================================ import { ref, onMounted, defineComponent } from 'vue' import { useSearch, useNavigate } from '@tanstack/vue-router' // Module-scoped ref to persist across component remounts const mounts = ref(0) export const RemountDepsComponent = defineComponent({ setup() { const search = useSearch({ from: '/remountDeps' }) const navigate = useNavigate() onMounted(() => { mounts.value++ }) return () => (
Search: {search.value.searchParam}
Page component mounts: {mounts.value}
) }, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/components/VueLogo.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/main.ts ================================================ import { createApp, h } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const app = createApp({ setup() { return () => h(RouterProvider, { router }) }, }) app.mount('#app') } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/posts.ts ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' import { Route as RemountDepsRouteImport } from './routes/remountDeps' import { Route as PostsRouteImport } from './routes/posts' import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' import { Route as EditingBRouteImport } from './routes/editing-b' import { Route as EditingARouteImport } from './routes/editing-a' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' import { Route as groupInsideRouteImport } from './routes/(group)/inside' import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' const Char45824Char54620Char48124Char44397Route = Char45824Char54620Char48124Char44397RouteImport.update({ id: '/대한민국', path: '/대한민국', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/대한민국.component.vue'), 'default', ), }) const RemountDepsRoute = RemountDepsRouteImport.update({ id: '/remountDeps', path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/remountDeps.component.vue'), 'default', ), }) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/posts.component.vue'), 'default', ), }) const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ id: '/notRemountDeps', path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/notRemountDeps.component.vue'), 'default', ), }) const EditingBRoute = EditingBRouteImport.update({ id: '/editing-b', path: '/editing-b', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/editing-b.component.vue'), 'default', ), }) const EditingARoute = EditingARouteImport.update({ id: '/editing-a', path: '/editing-a', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/editing-a.component.vue'), 'default', ), }) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/_layout.component.vue'), 'default', ), }) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/index.component.vue'), 'default', ), }) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any).update({ component: lazyRouteComponent( () => import('./routes/posts.index.component.vue'), 'default', ), }) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any).update({ component: lazyRouteComponent( () => import('./routes/posts.$postId.component.vue'), 'default', ), errorComponent: lazyRouteComponent( () => import('./routes/posts.$postId.errorComponent.vue'), 'default', ), }) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any).update({ component: lazyRouteComponent( () => import('./routes/_layout/_layout-2.component.vue'), 'default', ), }) const groupLazyinsideRoute = groupLazyinsideRouteImport .update({ id: '/(group)/lazyinside', path: '/lazyinside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( () => import('./routes/(group)/lazyinside.component.vue'), 'default', ), }) const groupInsideRoute = groupInsideRouteImport .update({ id: '/(group)/inside', path: '/inside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( () => import('./routes/(group)/inside.component.vue'), 'default', ), }) const groupLayoutRoute = groupLayoutRouteImport .update({ id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( () => import('./routes/(group)/_layout.component.vue'), 'default', ), }) const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport .update({ id: '/(another-group)/onlyrouteinside', path: '/onlyrouteinside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( () => import('./routes/(another-group)/onlyrouteinside.component.vue'), 'default', ), }) const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ id: '/posts_/$postId/edit', path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( () => import('./routes/posts_.$postId.edit.component.vue'), 'default', ), }) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any).update({ component: lazyRouteComponent( () => import('./routes/_layout/_layout-2/layout-b.component.vue'), 'default', ), }) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any).update({ component: lazyRouteComponent( () => import('./routes/_layout/_layout-2/layout-a.component.vue'), 'default', ), }) const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport .update({ id: '/(group)/subfolder/inside', path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( () => import('./routes/(group)/subfolder/inside.component.vue'), 'default', ), }) const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport .update({ id: '/insidelayout', path: '/insidelayout', getParentRoute: () => groupLayoutRoute, } as any) .update({ component: lazyRouteComponent( () => import('./routes/(group)/_layout.insidelayout.component.vue'), 'default', ), }) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/remountDeps': typeof RemountDepsRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/inside': typeof groupInsideRoute '/lazyinside': typeof groupLazyinsideRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/insidelayout': typeof groupLayoutInsidelayoutRoute '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/editing-a': typeof EditingARoute '/editing-b': typeof EditingBRoute '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/(another-group)/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute '/(group)/_layout': typeof groupLayoutRouteWithChildren '/(group)/inside': typeof groupInsideRoute '/(group)/lazyinside': typeof groupLazyinsideRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/(group)/_layout/insidelayout': typeof groupLayoutInsidelayoutRoute '/(group)/subfolder/inside': typeof groupSubfolderInsideRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute '/posts_/$postId/edit': typeof PostsPostIdEditRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/대한민국' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/posts/' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/posts/$postId/edit' fileRoutesByTo: FileRoutesByTo to: | '/' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/remountDeps' | '/대한민국' | '/onlyrouteinside' | '/inside' | '/lazyinside' | '/posts/$postId' | '/posts' | '/insidelayout' | '/subfolder/inside' | '/layout-a' | '/layout-b' | '/posts/$postId/edit' id: | '__root__' | '/' | '/_layout' | '/editing-a' | '/editing-b' | '/notRemountDeps' | '/posts' | '/remountDeps' | '/대한민국' | '/(another-group)/onlyrouteinside' | '/(group)/_layout' | '/(group)/inside' | '/(group)/lazyinside' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/(group)/_layout/insidelayout' | '/(group)/subfolder/inside' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' | '/posts_/$postId/edit' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren EditingARoute: typeof EditingARoute EditingBRoute: typeof EditingBRoute NotRemountDepsRoute: typeof NotRemountDepsRoute PostsRoute: typeof PostsRouteWithChildren RemountDepsRoute: typeof RemountDepsRoute Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute groupLayoutRoute: typeof groupLayoutRouteWithChildren groupInsideRoute: typeof groupInsideRoute groupLazyinsideRoute: typeof groupLazyinsideRoute groupSubfolderInsideRoute: typeof groupSubfolderInsideRoute PostsPostIdEditRoute: typeof PostsPostIdEditRoute } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/대한민국': { id: '/대한민국' path: '/대한민국' fullPath: '/대한민국' preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport parentRoute: typeof rootRouteImport } '/remountDeps': { id: '/remountDeps' path: '/remountDeps' fullPath: '/remountDeps' preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { id: '/notRemountDeps' path: '/notRemountDeps' fullPath: '/notRemountDeps' preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } '/editing-b': { id: '/editing-b' path: '/editing-b' fullPath: '/editing-b' preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/editing-a': { id: '/editing-a' path: '/editing-a' fullPath: '/editing-a' preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/(group)/lazyinside': { id: '/(group)/lazyinside' path: '/lazyinside' fullPath: '/lazyinside' preLoaderRoute: typeof groupLazyinsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/inside': { id: '/(group)/inside' path: '/inside' fullPath: '/inside' preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout': { id: '/(group)/_layout' path: '' fullPath: '' preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } '/(another-group)/onlyrouteinside': { id: '/(another-group)/onlyrouteinside' path: '/onlyrouteinside' fullPath: '/onlyrouteinside' preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport parentRoute: typeof rootRouteImport } '/posts_/$postId/edit': { id: '/posts_/$postId/edit' path: '/posts/$postId/edit' fullPath: '/posts/$postId/edit' preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } '/(group)/subfolder/inside': { id: '/(group)/subfolder/inside' path: '/subfolder/inside' fullPath: '/subfolder/inside' preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } '/(group)/_layout/insidelayout': { id: '/(group)/_layout/insidelayout' path: '/insidelayout' fullPath: '/insidelayout' preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport parentRoute: typeof groupLayoutRoute } } } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) interface groupLayoutRouteChildren { groupLayoutInsidelayoutRoute: typeof groupLayoutInsidelayoutRoute } const groupLayoutRouteChildren: groupLayoutRouteChildren = { groupLayoutInsidelayoutRoute: groupLayoutInsidelayoutRoute, } const groupLayoutRouteWithChildren = groupLayoutRoute._addFileChildren( groupLayoutRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, EditingARoute: EditingARoute, EditingBRoute: EditingBRoute, NotRemountDepsRoute: NotRemountDepsRoute, PostsRoute: PostsRouteWithChildren, RemountDepsRoute: RemountDepsRoute, Char45824Char54620Char48124Char44397Route: Char45824Char54620Char48124Char44397Route, anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute, groupLayoutRoute: groupLayoutRouteWithChildren, groupInsideRoute: groupInsideRoute, groupLazyinsideRoute: groupLazyinsideRoute, groupSubfolderInsideRoute: groupSubfolderInsideRoute, PostsPostIdEditRoute: PostsPostIdEditRoute, } export const routeTree = rootRouteImport .update({ component: lazyRouteComponent( () => import('./routes/__root.component.vue'), 'default', ), notFoundComponent: lazyRouteComponent( () => import('./routes/__root.notFoundComponent.vue'), 'default', ), }) ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(another-group)/onlyrouteinside.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(another-group)/onlyrouteinside.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(another-group)/onlyrouteinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/_layout.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/_layout.insidelayout.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/_layout.insidelayout.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/_layout/insidelayout')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/_layout.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/(group)/_layout')({}) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/inside.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/inside.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/lazyinside.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/lazyinside.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/lazyinside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/subfolder/inside.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/(group)/subfolder/inside.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' export const Route = createFileRoute('/(group)/subfolder/inside')({ validateSearch: zodValidator(z.object({ hello: z.string().optional() })), }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/__root.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/__root.notFoundComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/__root.ts ================================================ import { createRootRoute } from '@tanstack/vue-router' export const Route = createRootRoute() ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout/_layout-2/layout-a.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout/_layout-2/layout-a.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ // component is loaded from layout-a.component.vue }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout/_layout-2/layout-b.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout/_layout-2/layout-b.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ // component is loaded from layout-b.component.vue }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout/_layout-2.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout/_layout-2.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2')({ // component is loaded from _layout-2.component.vue }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/_layout.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout')({ // component is loaded from _layout.component.vue }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/editing-a.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/editing-a.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/editing-a')({}) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/editing-b.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/editing-b.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/editing-b')({}) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/index.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/index.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ // component is loaded from index.component.vue }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/notRemountDeps.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/notRemountDeps.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/notRemountDeps')({ validateSearch(search: { searchParam: string }) { return { searchParam: search.searchParam } }, loaderDeps(opts) { return opts.search }, remountDeps(opts) { return opts.params }, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.$postId.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.$postId.errorComponent.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.$postId.ts ================================================ import { h } from 'vue' import { createFileRoute } from '@tanstack/vue-router' import { fetchPost } from '../posts' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), notFoundComponent: () => h('p', 'Post not found'), }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.index.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.index.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ // component is loaded from posts.index.component.vue }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ head: () => ({ meta: [ { title: 'Posts page', }, ], }), loader: fetchPosts, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts_.$postId.edit.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/posts_.$postId.edit.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts_/$postId/edit')({}) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/remountDeps.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/remountDeps.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/remountDeps')({ validateSearch(search: { searchParam: string }) { return { searchParam: search.searchParam } }, loaderDeps(opts) { return opts.search }, remountDeps(opts) { return opts.search }, }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/대한민국.component.vue ================================================ ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/routes/대한민국.ts ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/대한민국')({}) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source "./**/*.vue"; @source "./**/*.ts"; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/src/vue-shims.d.ts ================================================ declare module '*.vue' { import type { DefineComponent } from 'vue' const component: DefineComponent<{}, {}, any> export default component } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import type { Page } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) test("useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test('useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-a') await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go to next step' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') }) test("legacy useBlocker doesn't block navigation if condition is not met", async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('legacy useBlocker does block navigation if condition is met', async ({ page, }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await expect(page.getByRole('button', { name: 'Proceed' })).toBeVisible() }) test('legacy Proceeding through blocked navigation works', async ({ page }) => { await page.goto('/editing-b') await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByLabel('Enter your name:').fill('foo') await page.getByRole('button', { name: 'Go back' }).click() await expect(page.getByRole('heading')).toContainText('Editing B') await page.getByRole('button', { name: 'Proceed' }).click() await expect(page.getByRole('heading')).toContainText('Editing A') }) test('useCanGoBack correctly disables back button', async ({ page }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } expect(await getBackButtonDisabled()).toBe(true) await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goForward() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(false) await page.goBack() expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) test('useCanGoBack correctly disables back button, using router.history and window.history', async ({ page, }) => { const getBackButtonDisabled = async () => { const backButton = page.getByTestId('back-button') const isDisabled = (await backButton.getAttribute('disabled')) !== null return isDisabled } await page.getByRole('link', { name: 'Posts' }).click() await expect(page.getByTestId('posts-links')).toBeInViewport() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toBeInViewport() await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(false) await page.reload() expect(await getBackButtonDisabled()).toBe(false) await page.getByTestId('back-button').click() expect(await getBackButtonDisabled()).toBe(true) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.forward()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(false) await page.evaluate('window.history.back()') expect(await getBackButtonDisabled()).toBe(true) await page.reload() expect(await getBackButtonDisabled()).toBe(true) }) const testCases = [ { description: 'Navigating to a route inside a route group', testId: 'link-to-route-inside-group', }, { description: 'Navigating to a route inside a subfolder inside a route group ', testId: 'link-to-route-inside-group-inside-subfolder', }, { description: 'Navigating to a route inside a route group inside a layout', testId: 'link-to-route-inside-group-inside-layout', }, { description: 'Navigating to a lazy route inside a route group', testId: 'link-to-lazy-route-inside-group', }, { description: 'Navigating to the only route inside a route group ', testId: 'link-to-only-route-inside-group', }, ] testCases.forEach(({ description, testId }) => { test(description, async ({ page }) => { await page.getByTestId(testId).click() await expect(page.getByTestId('search-via-hook')).toContainText('world') await expect(page.getByTestId('search-via-route-hook')).toContainText( 'world', ) await expect(page.getByTestId('search-via-route-api')).toContainText( 'world', ) }) }) test('navigating to an unnested route', async ({ page }) => { const postId = 'hello-world' page.goto(`/posts/${postId}/edit`) await expect(page.getByTestId('params-via-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-hook')).toContainText(postId) await expect(page.getByTestId('params-via-route-api')).toContainText(postId) }) test('Should change title on client side navigation', async ({ page }) => { await page.goto('/') await page.getByRole('link', { name: 'Posts' }).click() await expect(page).toHaveTitle('Posts page') }) test('Should change post navigating back and forth', async ({ page }) => { await page.goto('/posts/1') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await page.getByRole('link', { name: 'qui est esse' }).click() await expect(page.getByTestId('post-title')).toContainText('qui est esse') await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByTestId('post-title')).toContainText('sunt aut facere') }) test('Should not remount deps when remountDeps does not change ', async ({ page, }) => { await page.goto('/notRemountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) }) test('Should remount deps when remountDeps does change ', async ({ page }) => { await page.goto('/remountDeps') await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 1', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 2', ) await page.getByRole('button', { name: 'Regenerate search param' }).click() await expect(page.getByTestId('component-mounts')).toContainText( 'Page component mounts: 3', ) }) test.describe('Unicode route rendering', () => { test('should render non-latin route correctly', async ({ page, baseURL }) => { await page.goto('/대한민국') await expect(page.locator('body')).toContainText('Hello "/대한민국"!') expect(page.url()).toBe(`${baseURL}/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD`) }) }) ================================================ FILE: e2e/vue-router/basic-file-based-sfc/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "strict": true, "jsx": "preserve", "jsxImportSource": "vue", "moduleResolution": "bundler", "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, "types": ["vite/client"] }, "include": ["src", "tests"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-file-based-sfc/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true, }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/basic-scroll-restoration/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-scroll-restoration/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/basic-scroll-restoration/index.html ================================================
================================================ FILE: e2e/vue-router/basic-scroll-restoration/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-scroll-restoration", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/vue-virtual": "^3.13.0", "redaxios": "^0.5.1", "vue": "^3.5.16", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-scroll-restoration/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_SERVER_PORT=${PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-scroll-restoration/src/main.tsx ================================================ import { createApp, defineComponent, onMounted, ref } from 'vue' import { HeadContent, Link, Outlet, RouterProvider, createRootRoute, createRoute, createRouter, useElementScrollRestoration, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' import { useVirtualizer } from '@tanstack/vue-virtual' import './styles.css' const rootRoute = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( <>
Home {' '} About About (No Reset) By-Element
) } const IndexComponent = defineComponent({ name: 'IndexComponent', setup() { onMounted(() => { window.invokeOrders.push('index-useLayoutEffect') }) return () => (
) } const routeTree = rootRoute.addChildren([ postsRoute.addChildren([postRoute]), indexRoute, ]) // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: examples/react/deferred-data/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: examples/react/deferred-data/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "lib": ["DOM", "DOM.Iterable", "ES2022"], "skipLibCheck": true } } ================================================ FILE: examples/react/deferred-data/vite.config.js ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), react()], }) ================================================ FILE: examples/react/i18n-paraglide/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: examples/react/i18n-paraglide/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local count.txt .env .nitro .tanstack ================================================ FILE: examples/react/i18n-paraglide/.vscode/settings.json ================================================ { "files.watcherExclude": { "**/routeTree.gen.ts": true }, "search.exclude": { "**/routeTree.gen.ts": true }, "files.readonlyInclude": { "**/routeTree.gen.ts": true } } ================================================ FILE: examples/react/i18n-paraglide/README.md ================================================ # TanStack Router - i18n with Paraglide Example This example shows how to use Paraglide with TanStack Router. - [TanStack Router Docs](https://tanstack.com/router) - [Paraglide Documentation](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) ## Start a new project based on this example To start a new project based on this example, run: ```sh npx gitpick TanStack/router/tree/main/examples/react/i18n-paraglide i18n-paraglide ``` ## Getting started 1. Init Paraglide JS ```bash npx @inlang/paraglide-js@latest init ``` 2. Add the vite plugin to your `vite.config.ts`: ```diff import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' +import { paraglideVitePlugin } from "@inlang/paraglide-js"; export default defineConfig({ plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true }), react(), + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./app/paraglide", + }), ], }); ``` 3. Done :) Run the app and start translating. See the [basics documentation](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/basics) for information on how to use Paraglide's messages, parameters, and locale management. ## Rewrite URL If you want to handle how the URL looks when the user changes the locale, you can rewrite the URL in the router. ```diff import { createRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen"; +import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime.js"; const router = createRouter({ routeTree, + rewrite: { + input: ({ url }) => deLocalizeUrl(url), + output: ({ url }) => localizeUrl(url), }, }); ``` In `__root.tsx` add a `beforeLoad` hook to check if the user should be redirected and set the html `lang` attribute. Intercept the request in `server.ts` with the paraglideMiddleware: ```ts import { paraglideMiddleware } from './paraglide/server.js' import handler from '@tanstack/react-start/server-entry' export default { fetch(req: Request): Promise { return paraglideMiddleware(req, ({ request }) => handler.fetch(request)) }, } ``` In `__root.tsx` change the html lang attribute to the current locale. ```tsx import { getLocale } from '../paraglide/runtime.js' function RootDocument({ children }: { children: React.ReactNode }) { return ( {children} ) } ``` ## Offline redirect If you have an application that needs to work offline, you will need to handle the redirect in the client like this. ```ts import { shouldRedirect } from "../paraglide/runtime"; export const Route = createRootRoute({ beforeLoad: async () => { const decision = await shouldRedirect({ url: window.location.href }); if (decision.redirectUrl) { throw redirect({ href: decision.redirectUrl.href }); } }, ... }); ``` ## Typesafe translated pathnames If you don't want to miss any translated path, you can create a `createTranslatedPathnames` function and pass it to the vite plugin. ```ts import { Locale } from '@/paraglide/runtime' import { FileRoutesByTo } from '../routeTree.gen' type RoutePath = keyof FileRoutesByTo const excludedPaths = ['admin', 'docs', 'api'] as const type PublicRoutePath = Exclude< RoutePath, `${string}${(typeof excludedPaths)[number]}${string}` > type TranslatedPathname = { pattern: string localized: Array<[Locale, string]> } function toUrlPattern(path: string) { return ( path // catch-all .replace(/\/\$$/, '/:path(.*)?') // optional parameters: {-$param} .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ':$1?') // named parameters: $param .replace(/\$([a-zA-Z0-9_]+)/g, ':$1') // remove trailing slash .replace(/\/+$/, '') ) } function createTranslatedPathnames( input: Record>, ): TranslatedPathname[] { return Object.entries(input).map(([pattern, locales]) => ({ pattern: toUrlPattern(pattern), localized: Object.entries(locales).map( ([locale, path]) => [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ Locale, string, ], ), })) } export const translatedPathnames = createTranslatedPathnames({ '/': { en: '/', de: '/', }, '/about': { en: '/about', de: '/ueber', }, }) ``` And import into the Paraglide Vite plugin. ## Server-side rendering For server-side rendering, check out the [TanStack Start guide](https://github.com/TanStack/router/tree/main/examples/react/start-i18n-paraglide). ## Prerender routes You can use the `localizeHref` function to map the routes to localized versions and import into the pages option in the TanStack Start plugin. For this to work you will need to compile paraglide before the build with the CLI. ```ts import { localizeHref } from './paraglide/runtime' export const prerenderRoutes = ['/', '/about'].map((path) => ({ path: localizeHref(path), prerender: { enabled: true, }, })) ``` ## About This Example This example demonstrates: - Multi-language support with Paraglide - Type-safe translations - Locale-based routing - Language switching - i18n best practices ================================================ FILE: examples/react/i18n-paraglide/index.html ================================================ Create TanStack App - i18n-paraglide
================================================ FILE: examples/react/i18n-paraglide/messages/de.json ================================================ { "$schema": "https://inlang.com/schema/inlang-message-format", "example_message": "Guten Tag {username}", "hello_about": "Hallo /ueber!", "home_page": "Startseite", "about_page": "Über uns" } ================================================ FILE: examples/react/i18n-paraglide/messages/en.json ================================================ { "$schema": "https://inlang.com/schema/inlang-message-format", "example_message": "Hello world {username}", "hello_about": "Hello /about!", "home_page": "Home page", "about_page": "About page" } ================================================ FILE: examples/react/i18n-paraglide/package.json ================================================ { "name": "tanstack-router-i18n-paraglide", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "start": "vite --port 3000", "build": "vite build && tsc", "preview": "vite preview", "test": "vitest run" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.168.1", "@tanstack/router-plugin": "^1.167.1", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" }, "devDependencies": { "@types/node": "^22.18.6", "@types/react": "^19.1.13", "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "^6.0.1", "typescript": "^5.9.2", "vite": "^8.0.0", "@inlang/paraglide-js": "^2.4.0" } } ================================================ FILE: examples/react/i18n-paraglide/project.inlang/.gitignore ================================================ cache ================================================ FILE: examples/react/i18n-paraglide/project.inlang/project_id ================================================ 72AJsIR0c0ewzkN33F ================================================ FILE: examples/react/i18n-paraglide/project.inlang/settings.json ================================================ { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", "locales": ["en", "de"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" ], "plugin.inlang.messageFormat": { "pathPattern": "./messages/{locale}.json" } } ================================================ FILE: examples/react/i18n-paraglide/public/manifest.json ================================================ { "short_name": "TanStack App", "name": "Create TanStack App Sample", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" }, { "src": "logo192.png", "type": "image/png", "sizes": "192x192" }, { "src": "logo512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } ================================================ FILE: examples/react/i18n-paraglide/public/robots.txt ================================================ # https://www.robotstxt.org/robotstxt.html User-agent: * Disallow: ================================================ FILE: examples/react/i18n-paraglide/src/main.tsx ================================================ import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import './styles.css' // Import the generated route tree import { routeTree } from './routeTree.gen' import { deLocalizeUrl, localizeUrl } from './paraglide/runtime.js' // Create a new router instance const router = createRouter({ routeTree, context: {}, defaultPreload: 'intent', scrollRestoration: true, defaultStructuralSharing: true, defaultPreloadStaleTime: 0, rewrite: { input: ({ url }) => deLocalizeUrl(url), output: ({ url }) => localizeUrl(url), }, }) // Register the router instance for type safety declare module '@tanstack/react-router' { interface Register { router: typeof router } } // Render the app const rootElement = document.getElementById('app') if (rootElement && !rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( , ) } ================================================ FILE: examples/react/i18n-paraglide/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' const AboutRoute = AboutRouteImport.update({ id: '/about', path: '/about', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/about': typeof AboutRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/about': typeof AboutRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/about' fileRoutesByTo: FileRoutesByTo to: '/' | '/about' id: '__root__' | '/' | '/about' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute AboutRoute: typeof AboutRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/about': { id: '/about' path: '/about' fullPath: '/about' preLoaderRoute: typeof AboutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AboutRoute: AboutRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: examples/react/i18n-paraglide/src/routes/__root.tsx ================================================ import { Link, Outlet, createRootRoute, redirect } from '@tanstack/react-router' import { getLocale, locales, setLocale, shouldRedirect, } from '@/paraglide/runtime' import { m } from '@/paraglide/messages' export const Route = createRootRoute({ beforeLoad: async () => { document.documentElement.setAttribute('lang', getLocale()) const decision = await shouldRedirect({ url: window.location.href }) if (decision.redirectUrl) { throw redirect({ href: decision.redirectUrl.href }) } }, component: () => ( <>
{m.home_page()} {m.about_page()}
{locales.map((locale) => ( ))}

), }) ================================================ FILE: examples/react/i18n-paraglide/src/routes/about.tsx ================================================ import { m } from '@/paraglide/messages' import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/about')({ component: RouteComponent, }) function RouteComponent() { return
{m.hello_about()}
} ================================================ FILE: examples/react/i18n-paraglide/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import { m } from '@/paraglide/messages' export const Route = createFileRoute('/')({ component: App, }) function App() { return (

{m.example_message({ username: 'TanStack Router!', })}

) } ================================================ FILE: examples/react/i18n-paraglide/src/styles.css ================================================ @import 'tailwindcss' source('../'); ================================================ FILE: examples/react/i18n-paraglide/tsconfig.json ================================================ { "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "target": "ES2022", "jsx": "react-jsx", "module": "ESNext", "lib": ["ES2022", "DOM", "DOM.Iterable"], "types": ["vite/client"], "allowJs": true, /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "noEmit": true, /* Linting */ "skipLibCheck": true, "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, "baseUrl": ".", "paths": { "@/*": ["./src/*"] } } } ================================================ FILE: examples/react/i18n-paraglide/vite.config.ts ================================================ import { defineConfig } from 'vite' import viteReact from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import { resolve } from 'node:path' import { paraglideVitePlugin } from '@inlang/paraglide-js' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ tailwindcss(), paraglideVitePlugin({ project: './project.inlang', outdir: './src/paraglide', outputStructure: 'message-modules', cookieName: 'PARAGLIDE_LOCALE', strategy: ['url', 'cookie', 'preferredLanguage', 'baseLocale'], urlPatterns: [ { pattern: '/', localized: [ ['en', '/'], ['de', '/de'], ], }, { pattern: '/about', localized: [ ['en', '/about'], ['de', '/de/ueber'], ], }, { pattern: '/:path(.*)?', localized: [ ['en', '/:path(.*)?'], ['de', '/de/:path(.*)?'], ], }, ], }), tanstackRouter({ autoCodeSplitting: true }), viteReact(), ], resolve: { alias: { '@': resolve(__dirname, './src'), }, }, }) ================================================ FILE: examples/react/kitchen-sink/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: examples/react/kitchen-sink/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local ================================================ FILE: examples/react/kitchen-sink/.vscode/settings.json ================================================ { "files.watcherExclude": { "**/routeTree.gen.ts": true }, "search.exclude": { "**/routeTree.gen.ts": true }, "files.readonlyInclude": { "**/routeTree.gen.ts": true } } ================================================ FILE: examples/react/kitchen-sink/README.md ================================================ # TanStack Router - Kitchen Sink Example A comprehensive example demonstrating many TanStack Router features. - [TanStack Router Docs](https://tanstack.com/router) ## Start a new project based on this example To start a new project based on this example, run: ```sh npx gitpick TanStack/router/tree/main/examples/react/kitchen-sink kitchen-sink ``` ## Getting Started Install dependencies: ```sh pnpm install ``` Start the development server: ```sh pnpm dev ``` ## Build Build for production: ```sh pnpm build ``` ## About This Example This "kitchen sink" example demonstrates: - Advanced routing patterns - Nested routes - Route parameters and search params - Data loading - Error handling - Route guards - And many more TanStack Router features ================================================ FILE: examples/react/kitchen-sink/index.html ================================================ Vite App
================================================ FILE: examples/react/kitchen-sink/package.json ================================================ { "name": "tanstack-router-react-example-kitchen-sink", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.168.1", "@tanstack/react-router-devtools": "^1.166.10", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "typescript": "^5.7.2", "vite": "^8.0.0" } } ================================================ FILE: examples/react/kitchen-sink/src/Expensive.tsx ================================================ import * as React from 'react' export default function Expensive() { return (
I am an "expensive" component... which really just means that I was code-split 😉
) } ================================================ FILE: examples/react/kitchen-sink/src/main.tsx ================================================ /* eslint-disable @typescript-eslint/no-unnecessary-condition */ import * as React from 'react' import ReactDOM from 'react-dom/client' import { ErrorComponent, Link, MatchRoute, Outlet, RouterProvider, createRootRouteWithContext, createRoute, createRouter, lazyRouteComponent, notFound, redirect, retainSearchParams, useNavigate, useRouter, useRouterState, useSearch, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' import { z } from 'zod' import { fetchInvoiceById, fetchInvoices, fetchUserById, fetchUsers, patchInvoice, postInvoice, } from './mockTodos' import { useMutation } from './useMutation' import type { NotFoundRouteProps } from '@tanstack/react-router' import type { Invoice } from './mockTodos' import './styles.css' // type UsersViewSortBy = 'name' | 'id' | 'email' type MissingUserData = { userId: number } function isMissingUserData(data: unknown): data is MissingUserData { return ( typeof data === 'object' && data !== null && typeof (data as { userId?: unknown }).userId === 'number' ) } function UsersNotFoundComponent({ data, routeId }: NotFoundRouteProps) { const userId = isMissingUserData(data) ? data.userId : undefined return (

User not found

{typeof userId === 'number' ? `We couldn't find a user with ID ${userId}.` : "We couldn't find the requested user."}

Rendered by the "{routeId}" route.

Pick another user from the list on the left to continue.

) } const rootRoute = createRootRouteWithContext<{ auth: Auth }>()({ component: RootComponent, }) function RouterSpinner() { const isLoading = useRouterState({ select: (s) => s.status === 'pending' }) return } function RootComponent() { return ( <>

Kitchen Sink

{/* Show a global spinner when the router is transitioning */}
{( [ ['/', 'Home'], ['/dashboard', 'Dashboard'], ['/expensive', 'Expensive'], ['/route-a', 'Pathless Layout A'], ['/route-b', 'Pathless Layout B'], ['/profile', 'Profile'], ...(auth.status === 'loggedOut' ? [['/login', 'Login']] : []), ] as const ).map(([to, label]) => { return (
{label}
) })}
{/* Render our first route match */}
) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: IndexComponent, }) function IndexComponent() { return (
Welcome Home!

1 New Invoice
As you navigate around take note of the UX. It should feel suspense-like, where routes are only rendered once all of their data and elements are ready.
To exaggerate async effects, play with the artificial request delay slider in the bottom-left corner.
The last 2 sliders determine if link-hover preloading is enabled (and how long those preloads stick around) and also whether to cache rendered route data (and for how long). Both of these default to 0 (or off).
) } const dashboardLayoutRoute = createRoute({ getParentRoute: () => rootRoute, path: 'dashboard', component: DashboardLayoutComponent, }) function DashboardLayoutComponent() { return ( <>

Dashboard

{( [ ['/dashboard', 'Summary', true], ['/dashboard/invoices', 'Invoices'], ['/dashboard/users', 'Users'], ] as const ).map(([to, label, exact]) => { return ( {label} ) })}

) } const dashboardIndexRoute = createRoute({ getParentRoute: () => dashboardLayoutRoute, path: '/', loader: () => fetchInvoices(), component: DashboardIndexComponent, }) function DashboardIndexComponent() { const invoices = dashboardIndexRoute.useLoaderData() return (
Welcome to the dashboard! You have{' '} {invoices.length} total invoices.
) } const invoicesLayoutRoute = createRoute({ getParentRoute: () => dashboardLayoutRoute, path: 'invoices', loader: () => fetchInvoices(), component: InvoicesLayoutComponent, }) function InvoicesLayoutComponent() { const invoices = invoicesLayoutRoute.useLoaderData() return (
{invoices.map((invoice) => { return (
                  #{invoice.id} - {invoice.title.slice(0, 10)}{' '}
                  {/* {updateSubmission ? (
                      
                    ) : ( */}
                  
                    {(match) => }
                  
                  {/* )} */}
                
) })}
) } const invoicesIndexRoute = createRoute({ getParentRoute: () => invoicesLayoutRoute, path: '/', component: InvoicesIndexComponent, }) function InvoicesIndexComponent() { const createInvoiceMutation = useMutation({ fn: postInvoice, onSuccess: () => router.invalidate(), }) return ( <>
{ event.preventDefault() event.stopPropagation() const formData = new FormData(event.target as HTMLFormElement) createInvoiceMutation.mutate({ title: formData.get('title') as string, body: formData.get('body') as string, }) }} className="space-y-2" >
Create a new Invoice:
{createInvoiceMutation.status === 'success' ? (
Created!
) : createInvoiceMutation.status === 'error' ? (
Failed to create.
) : null}
) } const invoiceRoute = createRoute({ getParentRoute: () => invoicesLayoutRoute, path: '$invoiceId', params: { parse: (params) => ({ invoiceId: z.number().int().parse(Number(params.invoiceId)), }), stringify: ({ invoiceId }) => ({ invoiceId: `${invoiceId}` }), }, validateSearch: (search) => z .object({ showNotes: z.boolean().optional(), notes: z.string().optional(), }) .parse(search), loader: ({ params: { invoiceId } }) => fetchInvoiceById(invoiceId), component: InvoiceComponent, pendingComponent: () => , }) function InvoiceComponent() { const search = invoiceRoute.useSearch() const navigate = useNavigate({ from: invoiceRoute.fullPath }) const invoice = invoiceRoute.useLoaderData() const updateInvoiceMutation = useMutation({ fn: patchInvoice, onSuccess: () => router.invalidate(), }) const [notes, setNotes] = React.useState(search.notes ?? '') React.useEffect(() => { navigate({ search: (old) => ({ ...old, notes: notes ? notes : undefined, }), params: true, replace: true, }) }, [notes]) return (
{ event.preventDefault() event.stopPropagation() const formData = new FormData(event.target as HTMLFormElement) updateInvoiceMutation.mutate({ id: invoice.id, title: formData.get('title') as string, body: formData.get('body') as string, }) }} className="p-2 space-y-2" >
({ ...old, showNotes: old.showNotes ? undefined : true, })} className="text-blue-700" from={invoiceRoute.fullPath} params={true} > {search.showNotes ? 'Close Notes' : 'Show Notes'}{' '} {search.showNotes ? ( <>

Welcome Home!

{Array.from({ length: 50 }).map((_, i) => (
Home Item {i + 1}
))}
) }, }) const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', loader: () => new Promise((r) => setTimeout(r, 500)), component: IndexComponent, }) const AboutComponent = defineComponent({ name: 'AboutComponent', setup() { onMounted(() => { window.invokeOrders.push('about-useLayoutEffect') }) return () => (

Hello from About!

{Array.from({ length: 50 }).map((_, i) => (
About Item {i + 1}
))}
) }, }) const aboutRoute = createRoute({ getParentRoute: () => rootRoute, path: '/about', loader: () => new Promise((r) => setTimeout(r, 500)), component: AboutComponent, }) const ByElementComponent = defineComponent({ name: 'ByElementComponent', setup() { // We need a unique ID for manual scroll restoration on a specific element // It should be as unique as possible for this element across your app const scrollRestorationId = 'myVirtualizedContent' // We use that ID to get the scroll entry for this element const scrollEntry = useElementScrollRestoration({ id: scrollRestorationId, }) // Let's use TanStack Virtual to virtualize some content! const virtualizerParentRef = ref(null) const virtualizer = useVirtualizer({ count: 10000, getScrollElement: () => virtualizerParentRef.value, estimateSize: () => 100, // We pass the scrollY from the scroll restoration entry to the virtualizer // as the initial offset initialOffset: scrollEntry?.scrollY, }) return () => (
Hello from By-Element!
First Regular List Item
{Array.from({ length: 50 }).map((_, i) => (
Regular List Item {i + 1}
))}
{Array.from({ length: 2 }).map((_, i) => (
{Array.from({ length: 50 }).map((_, i) => (
About Item {i + 1}
))}
))}
Virtualized
{virtualizer.value.getVirtualItems().map((item) => (
Virtualized Item {item.index + 1}
))}
) }, }) const byElementRoute = createRoute({ getParentRoute: () => rootRoute, path: '/by-element', loader: () => new Promise((r) => setTimeout(r, 500)), component: ByElementComponent, }) const fooRoute = createRoute({ getParentRoute: () => rootRoute, path: '/foo', loader: () => new Promise((r) => setTimeout(r, 500)), component: FooComponent, }) function FooComponent() { return (

Hello from Foo!

Go to Bar
{Array.from({ length: 50 }).map((_, i) => (
Foo Item {i + 1}
))}
) } const barRoute = createRoute({ getParentRoute: () => rootRoute, path: '/bar', loader: () => new Promise((r) => setTimeout(r, 500)), component: BarComponent, }) function BarComponent() { return (

Hello from Bar!

{Array.from({ length: 50 }).map((_, i) => (
Bar Item {i + 1}
))}
) } const routeTree = rootRoute.addChildren([ indexRoute, aboutRoute, byElementRoute, fooRoute, barRoute, ]) const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, getScrollRestorationKey: (location) => location.pathname, }) declare global { interface Window { invokeOrders: Array } } let invokeOrders: Array = [] let shouldRecordRouterEvents = true Object.defineProperty(window, 'invokeOrders', { configurable: true, get: () => invokeOrders, set: (next) => { invokeOrders = next // Tests reset this between navigations; ignore any in-flight events from // the previous navigation until the next navigation begins. shouldRecordRouterEvents = false }, }) router.subscribe('onBeforeLoad', () => { shouldRecordRouterEvents = true }) router.subscribe('onBeforeRouteMount', (event) => { if (!shouldRecordRouterEvents) return invokeOrders.push(event.type) }) router.subscribe('onResolved', (event) => { if (!shouldRecordRouterEvents) return invokeOrders.push(event.type) }) declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/basic-scroll-restoration/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-scroll-restoration/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test('restore scroll positions by page, home pages top message should not display on navigating back', async ({ page, }) => { // Step 1: Navigate to the home page await page.goto('/') await expect(page.locator('#greeting')).toContainText('Welcome Home!') await expect(page.locator('#top-message')).toBeInViewport() // Step 2: Scroll to a position that hides the top const targetScrollPosition = 1000 await page.evaluate( (scrollPos: number) => window.scrollTo(0, scrollPos), targetScrollPosition, ) // Verify initial scroll position const scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).toBe(targetScrollPosition) await expect(page.locator('#top-message')).not.toBeInViewport() await page.waitForTimeout(1000) // Step 3: Navigate to the about page await page.getByRole('link', { name: 'About', exact: true }).click() await expect(page.locator('#greeting')).toContainText('Hello from About!') // Step 4: Go back to the home page and immediately check the message await page.goBack() // Wait for the home page to have rendered await page.waitForSelector('#greeting') await page.waitForTimeout(1000) await expect(page.locator('#top-message')).not.toBeInViewport() // Confirm the scroll position was restored correctly const restoredScrollPosition = await page.evaluate(() => window.scrollY) expect(restoredScrollPosition).toBe(targetScrollPosition) }) test('restore scroll positions by element, first regular list item should not display on navigating back', async ({ page, }) => { // Step 1: Navigate to the by-element page await page.goto('/by-element') // Step 2: Scroll to a position that hides the first list item in regular list const targetScrollPosition = 1000 await page.waitForSelector('#RegularList') await expect(page.locator('#first-regular-list-item')).toBeInViewport() await page.evaluate( (scrollPos: number) => document.querySelector('#RegularList')!.scrollTo(0, scrollPos), targetScrollPosition, ) // Verify initial scroll position const scrollPosition = await page.evaluate( () => document.querySelector('#RegularList')!.scrollTop, ) expect(scrollPosition).toBe(targetScrollPosition) await expect(page.locator('#first-regular-list-item')).not.toBeInViewport() // Step 3: Navigate to the about page await page.getByRole('link', { name: 'About', exact: true }).click() await expect(page.locator('#greeting')).toContainText('Hello from About!') // Step 4: Go back to the by-element page and immediately check the message await page.goBack() // TODO: For some reason, this only works in headed mode. // When someone can explain that to me, I'll fix this test. // Confirm the scroll position was restored correctly // const restoredScrollPosition = await page.evaluate( // () => document.querySelector('#RegularList')!.scrollTop, // ) // expect(restoredScrollPosition).toBe(targetScrollPosition) }) test('scroll to top when not scrolled, regression test for #4782', async ({ page, }) => { await page.goto('/foo') await expect(page.getByTestId('foo-route-component')).toBeVisible() let scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).toBe(0) // do not scroll, just navigate to /bar await page.getByTestId('go-to-bar-link').click() await expect(page.getByTestId('bar-route-component')).toBeVisible() const targetScrollPosition = 1000 await page.evaluate( (scrollPos: number) => window.scrollTo(0, scrollPos), targetScrollPosition, ) scrollPosition = await page.evaluate(() => window.scrollY) expect(scrollPosition).toBe(targetScrollPosition) // navigate back to /foo await page.goBack() await expect(page.getByTestId('foo-route-component')).toBeVisible() // check if scroll position is restored to 0 since we did not scroll on /foo const restoredScrollPosition = await page.evaluate(() => window.scrollY) expect(restoredScrollPosition).toBe(0) }) ================================================ FILE: e2e/vue-router/basic-scroll-restoration/tests/router-events.spec.ts ================================================ import { expect, test } from '@playwright/test' test('after a navigation, should have emitted "onBeforeRouteMount","onResolved" and useRenderEffect setup in the correct order', async ({ page, }) => { // Navigate to the Home page await page.goto('/') await expect(page.locator('#greeting')).toContainText('Welcome Home!') let orders = await page.evaluate(() => window.invokeOrders) expectItemOrder(orders, 'onBeforeRouteMount', 'onResolved') expectItemOrder(orders, 'onBeforeRouteMount', 'index-useLayoutEffect') // Clear the invokeOrders array orders = await page.evaluate(() => { window.invokeOrders = [] return window.invokeOrders }) // Navigate to the About page await page.getByRole('link', { name: 'About', exact: true }).click() await expect(page.locator('#greeting')).toContainText('Hello from About!') orders = await page.evaluate(() => window.invokeOrders) expectItemOrder(orders, 'onBeforeRouteMount', 'onResolved') expectItemOrder(orders, 'onBeforeRouteMount', 'about-useLayoutEffect') }) function expectItemOrder( array: Array, firstItem: TItem, secondItem: TItem, ) { const firstIndex = array.findIndex((item) => item === firstItem) const secondIndex = array.findIndex((item) => item === secondItem) if (firstIndex === -1 || secondIndex === -1) { throw new Error('One or both items were not found in the array ' + array) } expect(firstIndex).toBeLessThan(secondIndex) } ================================================ FILE: e2e/vue-router/basic-scroll-restoration/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "vue", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-scroll-restoration/vite.config.js ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), vue(), vueJsx()], }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/basic-virtual-file-based/index.html ================================================
================================================ FILE: e2e/vue-router/basic-virtual-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-virtual-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "@tanstack/vue-query": "^5.90.0", "@tanstack/vue-query-devtools": "^6.1.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('second', 'layout/second-layout.tsx', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/main.tsx ================================================ import { createApp } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/root' import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' import { Route as bRouteImport } from './routes/b' import { Route as aRouteImport } from './routes/a' const postsPostsRoute = postsPostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) const homeRoute = homeRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => postsPostsRoute, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second', getParentRoute: () => layoutFirstLayoutRoute, } as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ id: '/classic/hello', path: '/classic/hello', getParentRoute: () => rootRouteImport, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ id: '/world', path: '/world', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) const bRoute = bRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => layoutSecondLayoutRoute, } as any) const aRoute = aRouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof homeRoute '/posts': typeof postsPostsRouteWithChildren '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/layout-a': typeof aRoute '/layout-b': typeof bRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute } export interface FileRoutesByTo { '/': typeof homeRoute '/posts': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/layout-a': typeof aRoute '/layout-b': typeof bRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello': typeof ClassicHelloIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof homeRoute '/_first': typeof layoutFirstLayoutRouteWithChildren '/posts': typeof postsPostsRouteWithChildren '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/_first/_second': typeof layoutSecondLayoutRouteWithChildren '/posts/$postId': typeof postsPostsDetailRoute '/_first/_second/layout-a': typeof aRoute '/_first/_second/layout-b': typeof bRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/classic/hello' | '/posts/' | '/posts/$postId' | '/layout-a' | '/layout-b' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' fileRoutesByTo: FileRoutesByTo to: | '/' | '/posts' | '/posts/$postId' | '/layout-a' | '/layout-b' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello' id: | '__root__' | '/' | '/_first' | '/posts' | '/classic/hello' | '/posts/' | '/_first/_second' | '/posts/$postId' | '/_first/_second/layout-a' | '/_first/_second/layout-b' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { homeRoute: typeof homeRoute layoutFirstLayoutRoute: typeof layoutFirstLayoutRouteWithChildren postsPostsRoute: typeof postsPostsRouteWithChildren ClassicHelloRouteRoute: typeof ClassicHelloRouteRouteWithChildren } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } '/_first': { id: '/_first' path: '' fullPath: '/' preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof postsPostsDetailRouteImport parentRoute: typeof postsPostsRoute } '/_first/_second': { id: '/_first/_second' path: '' fullPath: '/' preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } '/classic/hello': { id: '/classic/hello' path: '/classic/hello' fullPath: '/classic/hello' preLoaderRoute: typeof ClassicHelloRouteRouteImport parentRoute: typeof rootRouteImport } '/classic/hello/': { id: '/classic/hello/' path: '/' fullPath: '/classic/hello/' preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/world': { id: '/classic/hello/world' path: '/world' fullPath: '/classic/hello/world' preLoaderRoute: typeof ClassicHelloWorldRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' fullPath: '/classic/hello/universe' preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second/layout-b': { id: '/_first/_second/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof bRouteImport parentRoute: typeof layoutSecondLayoutRoute } '/_first/_second/layout-a': { id: '/_first/_second/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } } } declare module './routes/home' { const createFileRoute: CreateFileRoute< '/', FileRoutesByPath['/']['parentRoute'], FileRoutesByPath['/']['id'], FileRoutesByPath['/']['path'], FileRoutesByPath['/']['fullPath'] > } declare module './routes/layout/first-layout' { const createFileRoute: CreateFileRoute< '/_first', FileRoutesByPath['/_first']['parentRoute'], FileRoutesByPath['/_first']['id'], FileRoutesByPath['/_first']['path'], FileRoutesByPath['/_first']['fullPath'] > } declare module './routes/posts/posts' { const createFileRoute: CreateFileRoute< '/posts', FileRoutesByPath['/posts']['parentRoute'], FileRoutesByPath['/posts']['id'], FileRoutesByPath['/posts']['path'], FileRoutesByPath['/posts']['fullPath'] > } declare module './routes/file-based-subtree/hello/route' { const createFileRoute: CreateFileRoute< '/classic/hello', FileRoutesByPath['/classic/hello']['parentRoute'], FileRoutesByPath['/classic/hello']['id'], FileRoutesByPath['/classic/hello']['path'], FileRoutesByPath['/classic/hello']['fullPath'] > } declare module './routes/posts/posts-home' { const createFileRoute: CreateFileRoute< '/posts/', FileRoutesByPath['/posts/']['parentRoute'], FileRoutesByPath['/posts/']['id'], FileRoutesByPath['/posts/']['path'], FileRoutesByPath['/posts/']['fullPath'] > } declare module './routes/layout/second-layout' { const createFileRoute: CreateFileRoute< '/_first/_second', FileRoutesByPath['/_first/_second']['parentRoute'], FileRoutesByPath['/_first/_second']['id'], FileRoutesByPath['/_first/_second']['path'], FileRoutesByPath['/_first/_second']['fullPath'] > } declare module './routes/posts/posts-detail' { const createFileRoute: CreateFileRoute< '/posts/$postId', FileRoutesByPath['/posts/$postId']['parentRoute'], FileRoutesByPath['/posts/$postId']['id'], FileRoutesByPath['/posts/$postId']['path'], FileRoutesByPath['/posts/$postId']['fullPath'] > } declare module './routes/a' { const createFileRoute: CreateFileRoute< '/_first/_second/layout-a', FileRoutesByPath['/_first/_second/layout-a']['parentRoute'], FileRoutesByPath['/_first/_second/layout-a']['id'], FileRoutesByPath['/_first/_second/layout-a']['path'], FileRoutesByPath['/_first/_second/layout-a']['fullPath'] > } declare module './routes/b' { const createFileRoute: CreateFileRoute< '/_first/_second/layout-b', FileRoutesByPath['/_first/_second/layout-b']['parentRoute'], FileRoutesByPath['/_first/_second/layout-b']['id'], FileRoutesByPath['/_first/_second/layout-b']['path'], FileRoutesByPath['/_first/_second/layout-b']['fullPath'] > } declare module './routes/file-based-subtree/hello/universe' { const createFileRoute: CreateFileRoute< '/classic/hello/universe', FileRoutesByPath['/classic/hello/universe']['parentRoute'], FileRoutesByPath['/classic/hello/universe']['id'], FileRoutesByPath['/classic/hello/universe']['path'], FileRoutesByPath['/classic/hello/universe']['fullPath'] > } declare module './routes/file-based-subtree/hello/world' { const createFileRoute: CreateFileRoute< '/classic/hello/world', FileRoutesByPath['/classic/hello/world']['parentRoute'], FileRoutesByPath['/classic/hello/world']['id'], FileRoutesByPath['/classic/hello/world']['path'], FileRoutesByPath['/classic/hello/world']['fullPath'] > } declare module './routes/file-based-subtree/hello/index' { const createFileRoute: CreateFileRoute< '/classic/hello/', FileRoutesByPath['/classic/hello/']['parentRoute'], FileRoutesByPath['/classic/hello/']['id'], FileRoutesByPath['/classic/hello/']['path'], FileRoutesByPath['/classic/hello/']['fullPath'] > } interface layoutSecondLayoutRouteChildren { aRoute: typeof aRoute bRoute: typeof bRoute } const layoutSecondLayoutRouteChildren: layoutSecondLayoutRouteChildren = { aRoute: aRoute, bRoute: bRoute, } const layoutSecondLayoutRouteWithChildren = layoutSecondLayoutRoute._addFileChildren(layoutSecondLayoutRouteChildren) interface layoutFirstLayoutRouteChildren { layoutSecondLayoutRoute: typeof layoutSecondLayoutRouteWithChildren } const layoutFirstLayoutRouteChildren: layoutFirstLayoutRouteChildren = { layoutSecondLayoutRoute: layoutSecondLayoutRouteWithChildren, } const layoutFirstLayoutRouteWithChildren = layoutFirstLayoutRoute._addFileChildren(layoutFirstLayoutRouteChildren) interface postsPostsRouteChildren { postsPostsHomeRoute: typeof postsPostsHomeRoute postsPostsDetailRoute: typeof postsPostsDetailRoute } const postsPostsRouteChildren: postsPostsRouteChildren = { postsPostsHomeRoute: postsPostsHomeRoute, postsPostsDetailRoute: postsPostsDetailRoute, } const postsPostsRouteWithChildren = postsPostsRoute._addFileChildren( postsPostsRouteChildren, ) interface ClassicHelloRouteRouteChildren { ClassicHelloUniverseRoute: typeof ClassicHelloUniverseRoute ClassicHelloWorldRoute: typeof ClassicHelloWorldRoute ClassicHelloIndexRoute: typeof ClassicHelloIndexRoute } const ClassicHelloRouteRouteChildren: ClassicHelloRouteRouteChildren = { ClassicHelloUniverseRoute: ClassicHelloUniverseRoute, ClassicHelloWorldRoute: ClassicHelloWorldRoute, ClassicHelloIndexRoute: ClassicHelloIndexRoute, } const ClassicHelloRouteRouteWithChildren = ClassicHelloRouteRoute._addFileChildren(ClassicHelloRouteRouteChildren) const rootRouteChildren: RootRouteChildren = { homeRoute: homeRoute, layoutFirstLayoutRoute: layoutFirstLayoutRouteWithChildren, postsPostsRoute: postsPostsRouteWithChildren, ClassicHelloRouteRoute: ClassicHelloRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ export const Route = createFileRoute({ component: () =>
This is the index
, }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet } from '@tanstack/vue-router' export const Route = createFileRoute({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/home.tsx ================================================ export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/layout/first-layout.tsx ================================================ import { Outlet } from '@tanstack/vue-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet } from '@tanstack/vue-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/posts/posts-detail.tsx ================================================ import { ErrorComponent } from '@tanstack/vue-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/vue-router' export function PostErrorComponent({ error }: ErrorComponentProps) { return } export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) function PostComponent() { const post = Route.useLoaderData() return (

{post.value?.title}

{post.value?.body}
) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/posts/posts-home.tsx ================================================ export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/posts/posts.tsx ================================================ import { Link, Outlet } from '@tanstack/vue-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/routes/root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/basic-virtual-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "vue", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-virtual-file-based/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import { routes } from './routes' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true, verboseFileRoutes: false, virtualRouteConfig: routes, }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ src/routeTree.gen.ts ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/index.html ================================================
================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-virtual-named-export-config-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "@tanstack/vue-query": "^5.90.0", "@tanstack/vue-query-devtools": "^6.1.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('second', 'layout/second-layout.tsx', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/main.tsx ================================================ import { createApp } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ export const Route = createFileRoute({ component: () =>
This is the index
, }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet } from '@tanstack/vue-router' export const Route = createFileRoute({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/home.tsx ================================================ export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx ================================================ import { Outlet } from '@tanstack/vue-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet } from '@tanstack/vue-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx ================================================ import { ErrorComponent } from '@tanstack/vue-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/vue-router' export function PostErrorComponent({ error }: ErrorComponentProps) { return } export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) function PostComponent() { const post = Route.useLoaderData() return (

{post.value?.title}

{post.value?.body}
) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx ================================================ export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx ================================================ import { Link, Outlet } from '@tanstack/vue-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/routes/root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source './**/*.{js,jsx,ts,tsx}'; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "vue", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "resolveJsonModule": true, "allowJs": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-virtual-named-export-config-file-based/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true, verboseFileRoutes: false, virtualRouteConfig: './routes.ts', }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/basic-vue-query/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-vue-query/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/basic-vue-query/index.html ================================================ Vite App
================================================ FILE: e2e/vue-router/basic-vue-query/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-vue-query", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && vue-tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/vue-query": "^5.90.0", "@tanstack/vue-query-devtools": "^6.1.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-vue-query/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-vue-query/src/main.tsx ================================================ import { createApp } from 'vue' import { ErrorComponent, Link, Outlet, RouterProvider, createRootRouteWithContext, createRoute, createRouter, useRouter, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' import { VueQueryDevtools } from '@tanstack/vue-query-devtools' import { QueryClient, VueQueryPlugin, useQuery } from '@tanstack/vue-query' import { NotFoundError, postQueryOptions, postsQueryOptions } from './posts' import type { ErrorComponentProps } from '@tanstack/vue-router' import './styles.css' const rootRoute = createRootRouteWithContext<{ queryClient: QueryClient }>()({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: IndexRouteComponent, }) function IndexRouteComponent() { return (

Welcome Home!

) } const postsRoute = createRoute({ getParentRoute: () => rootRoute, path: 'posts', loader: ({ context: { queryClient } }) => queryClient.ensureQueryData(postsQueryOptions), }).lazy(() => import('./posts.lazy').then((d) => d.Route)) const postsIndexRoute = createRoute({ getParentRoute: () => postsRoute, path: '/', component: PostsIndexRouteComponent, }) function PostsIndexRouteComponent() { return
Select a post.
} const postRoute = createRoute({ getParentRoute: () => postsRoute, path: '$postId', errorComponent: PostErrorComponent, loader: ({ context: { queryClient }, params: { postId } }) => queryClient.ensureQueryData(postQueryOptions(postId)), component: PostRouteComponent, }) function PostErrorComponent({ error }: ErrorComponentProps) { const router = useRouter() if (error instanceof NotFoundError) { return
{error.message}
} return (
) } function PostRouteComponent() { const postId = postRoute.useParams().value.postId useQuery(postQueryOptions(postId)) const post = postRoute.useLoaderData() return (

{post.value.title}

{post.value.body}
) } const layoutRoute = createRoute({ getParentRoute: () => rootRoute, id: '_layout', component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } const layout2Route = createRoute({ getParentRoute: () => layoutRoute, id: '_layout-2', component: Layout2Component, }) function Layout2Component() { return (
I'm a nested layout
Layout A Layout B
) } const layoutARoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-a', component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} const layoutBRoute = createRoute({ getParentRoute: () => layout2Route, path: '/layout-b', component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} const routeTree = rootRoute.addChildren([ postsRoute.addChildren([postRoute, postsIndexRoute]), layoutRoute.addChildren([ layout2Route.addChildren([layoutARoute, layoutBRoute]), ]), indexRoute, ]) const queryClient = new QueryClient() // Set up a Router instance const router = createRouter({ routeTree, scrollRestoration: true, defaultPreload: 'intent', // Since we're using Vue Query, we don't want loader calls to ever be stale // This will ensure that the loader is always called when the route is preloaded or visited defaultPreloadStaleTime: 0, context: { queryClient, }, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }) .use(VueQueryPlugin, { queryClient }) .mount('#app') } ================================================ FILE: e2e/vue-router/basic-vue-query/src/posts.lazy.tsx ================================================ import { Link, Outlet, createLazyRoute } from '@tanstack/vue-router' import { useQuery } from '@tanstack/vue-query' import { postsQueryOptions } from './posts' export const Route = createLazyRoute('/posts')({ component: PostsComponent, }) function PostsComponent() { const postsQuery = useQuery(postsQueryOptions) const posts = postsQuery.data return (
    {[ ...((posts.value ?? []) as Array<{ id: string; title: string }>), { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}
) } ================================================ FILE: e2e/vue-router/basic-vue-query/src/posts.ts ================================================ import axios from 'redaxios' import { queryOptions } from '@tanstack/vue-query' export class NotFoundError extends Error {} type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } export const postQueryOptions = (postId: string) => queryOptions({ queryKey: ['posts', { postId }], queryFn: () => fetchPost(postId), }) export const postsQueryOptions = queryOptions({ queryKey: ['posts'], queryFn: () => fetchPosts(), }) ================================================ FILE: e2e/vue-router/basic-vue-query/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-vue-query/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/basic-vue-query/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-vue-query/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-vue-query/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "jsx": "preserve", "jsxImportSource": "vue", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, "types": ["vite/client"] }, "include": ["src"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-vue-query/vite.config.js ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), vue(), vueJsx()], }) ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/vue-router/basic-vue-query-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-basic-vue-query-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && vue-tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/vue-query": "^5.90.0", "@tanstack/vue-query-devtools": "^6.1.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/router-plugin": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/main.tsx ================================================ import { createApp } from 'vue' import { RouterProvider, createRouter } from '@tanstack/vue-router' import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query' import { routeTree } from './routeTree.gen' import './styles.css' const queryClient = new QueryClient() // Set up a Router instance const router = createRouter({ routeTree, context: { queryClient, }, scrollRestoration: true, defaultPreload: 'intent', // Since we're using Vue Query, we don't want loader calls to ever be stale // This will ensure that the loader is always called when the route is preloaded or visited defaultPreloadStaleTime: 0, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }) .use(VueQueryPlugin, { queryClient }) .mount('#app') } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/postQueryOptions.tsx ================================================ import { queryOptions } from '@tanstack/vue-query' import { fetchPost } from './posts' export const postQueryOptions = (postId: string) => queryOptions({ queryKey: ['posts', { postId }], queryFn: () => fetchPost(postId), }) ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/posts.tsx ================================================ import axios from 'redaxios' export type PostType = { id: string title: string body: string } export class PostNotFoundError extends Error {} let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw new PostNotFoundError(`Post with id "${postId}" not found!`) } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/postsQueryOptions.tsx ================================================ import { queryOptions } from '@tanstack/vue-query' import { fetchPosts } from './posts' export const postsQueryOptions = queryOptions({ queryKey: ['posts'], queryFn: () => fetchPosts(), }) ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PostsRouteImport } from './routes/posts' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/posts': typeof PostsRouteWithChildren '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/posts/$postId' | '/posts/' | '/layout-a' | '/layout-b' fileRoutesByTo: FileRoutesByTo to: '/' | '/posts/$postId' | '/posts' | '/layout-a' | '/layout-b' id: | '__root__' | '/' | '/_layout' | '/posts' | '/_layout/_layout-2' | '/posts/$postId' | '/posts/' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren PostsRoute: typeof PostsRouteWithChildren } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } } } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteChildren: PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, PostsRoute: PostsRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/__root.tsx ================================================ import { Link, Outlet, createRootRouteWithContext } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' import { VueQueryDevtools } from '@tanstack/vue-query-devtools' import type { QueryClient } from '@tanstack/vue-query' export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/posts.$postId.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { ErrorComponent, useRouter } from '@tanstack/vue-router' import { useQuery } from '@tanstack/vue-query' import { PostNotFoundError } from '../posts' import { postQueryOptions } from '../postQueryOptions' import type { ErrorComponentProps } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/$postId')({ loader: ({ context: { queryClient }, params: { postId } }) => { return queryClient.ensureQueryData(postQueryOptions(postId)) }, errorComponent: PostErrorComponent, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { const router = useRouter() if (error instanceof PostNotFoundError) { return
{error.message}
} return (
) } function PostComponent() { const postId = Route.useParams().value.postId useQuery(postQueryOptions(postId)) const post = Route.useLoaderData() return (

{post.value.title}

{post.value.body}
) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/routes/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' import { useQuery } from '@tanstack/vue-query' import { postsQueryOptions } from '../postsQueryOptions' export const Route = createFileRoute('/posts')({ loader: ({ context: { queryClient } }) => queryClient.ensureQueryData(postsQueryOptions), component: PostsComponent, }) function PostsComponent() { useQuery(postsQueryOptions) const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' import { getDummyServerPort } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } test.beforeEach(async ({ page }) => { await page.goto('/') }) test('GetPosts', async () => { const port = await getDummyServerPort(packageJson.name) const res = await fetch(`http://localhost:${port}/posts`) expect(res.status).toBe(200) const posts = await res.json() expect(posts.length).toBeGreaterThan(0) const postRes = await fetch(`http://localhost:${port}/posts/1`) expect(postRes.status).toBe(200) const post = await postRes.json() expect(post).toEqual(posts[0]) }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a layout") await expect(page.locator('#app')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "jsx": "preserve", "jsxImportSource": "vue", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, "types": ["vite/client"] }, "include": ["src", "tests"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/basic-vue-query-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/generator-cli-only/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/generator-cli-only/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/generator-cli-only/index.html ================================================ Vite App
================================================ FILE: e2e/vue-router/generator-cli-only/package.json ================================================ { "name": "tanstack-router-e2e-vue-generator-cli-only", "private": true, "type": "module", "scripts": { "dev": "tsr generate && vite --port 3000", "dev:e2e": "tsr generate && vite", "build": "tsr generate && vite build && tsc --noEmit", "preview": "vite preview", "start": "tsr generate && vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/router-cli": "workspace:^", "redaxios": "^0.5.1", "vue": "^3.5.16", "tailwindcss": "^4.2.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/generator-cli-only/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/generator-cli-only/src/main.tsx ================================================ import { RouterProvider, createRouter } from '@tanstack/vue-router' import { createApp } from 'vue' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/generator-cli-only/src/posts.ts ================================================ import axios from 'redaxios' export class NotFoundError extends Error {} type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } ================================================ FILE: e2e/vue-router/generator-cli-only/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRouteRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutRouteImport.update({ id: '/_nested-layout', getParentRoute: () => PathlessLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/posts': typeof PostsRouteRouteWithChildren '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/posts/$postId' | '/posts/' | '/route-a' | '/route-b' fileRoutesByTo: FileRoutesByTo to: '/' | '/posts/$postId' | '/posts' | '/route-a' | '/route-b' id: | '__root__' | '/' | '/posts' | '/_pathlessLayout' | '/_pathlessLayout/_nested-layout' | '/posts/$postId' | '/posts/' | '/_pathlessLayout/_nested-layout/route-a' | '/_pathlessLayout/_nested-layout/route-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute PostsRouteRoute: typeof PostsRouteRouteWithChildren PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/_pathlessLayout': { id: '/_pathlessLayout' path: '' fullPath: '/' preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRouteRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } '/_pathlessLayout/_nested-layout': { id: '/_pathlessLayout/_nested-layout' path: '' fullPath: '/' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport parentRoute: typeof PathlessLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' path: '/route-b' fullPath: '/route-b' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-a': { id: '/_pathlessLayout/_nested-layout/route-a' path: '/route-a' fullPath: '/route-a' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } } } interface PostsRouteRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteRouteChildren: PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) interface PathlessLayoutNestedLayoutRouteChildren { PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute } const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = { PathlessLayoutNestedLayoutRouteARoute: PathlessLayoutNestedLayoutRouteARoute, PathlessLayoutNestedLayoutRouteBRoute: PathlessLayoutNestedLayoutRouteBRoute, } const PathlessLayoutNestedLayoutRouteWithChildren = PathlessLayoutNestedLayoutRoute._addFileChildren( PathlessLayoutNestedLayoutRouteChildren, ) interface PathlessLayoutRouteChildren { PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren } const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, } const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( PathlessLayoutRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, PostsRouteRoute: PostsRouteRouteWithChildren, PathlessLayoutRoute: PathlessLayoutRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/__root.tsx ================================================ import { Link, Outlet, createRootRoute } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Pathless Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( { component: LayoutAComponent, }, ) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( { component: LayoutBComponent, }, ) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested pathless layout
Go to route A Go to route B
) } ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a pathless layout
) } ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/posts.$postId.tsx ================================================ import { ErrorComponent, createFileRoute } from '@tanstack/vue-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.value.title}

{post.value.body}
) } ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsLayoutComponent, }) function PostsLayoutComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/generator-cli-only/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/generator-cli-only/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested pathless layouts', async ({ page }) => { await page.getByRole('link', { name: 'Pathless Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a pathless layout") await expect(page.locator('#app')).toContainText( "I'm a nested pathless layout", ) await page.getByRole('link', { name: 'Go to route A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Go to route B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/generator-cli-only/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/generator-cli-only/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/generator-cli-only/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "vue", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "resolveJsonModule": true, "allowJs": true, "skipLibCheck": true, "types": ["vite/client"] }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/generator-cli-only/tsr.config.json ================================================ { "routesDirectory": "./src/routes", "generatedRouteTree": "./src/routeTree.gen.ts", "target": "vue" } ================================================ FILE: e2e/vue-router/generator-cli-only/vite.config.js ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [tailwindcss(), vue(), vueJsx()], }) ================================================ FILE: e2e/vue-router/js-only-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/js-only-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-hash dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/js-only-file-based/index.html ================================================ Vite App
================================================ FILE: e2e/vue-router/js-only-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-js-only-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/vue-query": "^5.90.0", "@tanstack/vue-query-devtools": "^6.1.2", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "vite": "^8.0.0" } } ================================================ FILE: e2e/vue-router/js-only-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/js-only-file-based/src/main.jsx ================================================ import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import { createApp } from 'vue' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) const rootElement = document.getElementById('app') if (!rootElement.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/js-only-file-based/src/posts.js ================================================ import axios from 'redaxios' export class NotFoundError extends Error {} let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPosts = async () => { console.info('Fetching posts...') return axios.get(`${queryURL}/posts`).then((r) => r.data.slice(0, 10)) } export const fetchPost = async (postId) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) if (!post) { throw new NotFoundError(`Post with id "${postId}" not found!`) } return post } ================================================ FILE: e2e/vue-router/js-only-file-based/src/routeTree.gen.js ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, }) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, }) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, }) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRouteRoute, }) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRouteRoute, }) const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutRouteImport.update({ id: '/_nested-layout', getParentRoute: () => PathlessLayoutRoute, }) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) const PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) const PathlessLayoutNestedLayoutRouteChildren = { PathlessLayoutNestedLayoutRouteARoute: PathlessLayoutNestedLayoutRouteARoute, PathlessLayoutNestedLayoutRouteBRoute: PathlessLayoutNestedLayoutRouteBRoute, } const PathlessLayoutNestedLayoutRouteWithChildren = PathlessLayoutNestedLayoutRoute._addFileChildren( PathlessLayoutNestedLayoutRouteChildren, ) const PathlessLayoutRouteChildren = { PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, } const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( PathlessLayoutRouteChildren, ) const rootRouteChildren = { IndexRoute: IndexRoute, PostsRouteRoute: PostsRouteRouteWithChildren, PathlessLayoutRoute: PathlessLayoutRouteWithChildren, } export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren) ================================================ FILE: e2e/vue-router/js-only-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRouteRoute, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutRouteImport.update({ id: '/_nested-layout', getParentRoute: () => PathlessLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts/$postId': typeof PostsPostIdRoute '/posts': typeof PostsIndexRoute '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/posts': typeof PostsRouteRouteWithChildren '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute '/posts/': typeof PostsIndexRoute '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/posts/$postId' | '/posts/' | '/route-a' | '/route-b' fileRoutesByTo: FileRoutesByTo to: '/' | '/posts/$postId' | '/posts' | '/route-a' | '/route-b' id: | '__root__' | '/' | '/posts' | '/_pathlessLayout' | '/_pathlessLayout/_nested-layout' | '/posts/$postId' | '/posts/' | '/_pathlessLayout/_nested-layout/route-a' | '/_pathlessLayout/_nested-layout/route-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute PostsRouteRoute: typeof PostsRouteRouteWithChildren PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/_pathlessLayout': { id: '/_pathlessLayout' path: '' fullPath: '/' preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRouteRoute } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } '/_pathlessLayout/_nested-layout': { id: '/_pathlessLayout/_nested-layout' path: '' fullPath: '/' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport parentRoute: typeof PathlessLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' path: '/route-b' fullPath: '/route-b' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-a': { id: '/_pathlessLayout/_nested-layout/route-a' path: '/route-a' fullPath: '/route-a' preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } } } interface PostsRouteRouteChildren { PostsPostIdRoute: typeof PostsPostIdRoute PostsIndexRoute: typeof PostsIndexRoute } const PostsRouteRouteChildren: PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, PostsIndexRoute: PostsIndexRoute, } const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) interface PathlessLayoutNestedLayoutRouteChildren { PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute } const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = { PathlessLayoutNestedLayoutRouteARoute: PathlessLayoutNestedLayoutRouteARoute, PathlessLayoutNestedLayoutRouteBRoute: PathlessLayoutNestedLayoutRouteBRoute, } const PathlessLayoutNestedLayoutRouteWithChildren = PathlessLayoutNestedLayoutRoute._addFileChildren( PathlessLayoutNestedLayoutRouteChildren, ) interface PathlessLayoutRouteChildren { PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren } const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, } const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( PathlessLayoutRouteChildren, ) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, PostsRouteRoute: PostsRouteRouteWithChildren, PathlessLayoutRoute: PathlessLayoutRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/__root.jsx ================================================ import { Link, Outlet, createRootRoute } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Pathless Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/_pathlessLayout/_nested-layout/route-a.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( { component: LayoutAComponent, }, ) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/_pathlessLayout/_nested-layout/route-b.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( { component: LayoutBComponent, }, ) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/_pathlessLayout/_nested-layout.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { Link, Outlet } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested pathless layout
Go to route A Go to route B
) } ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/_pathlessLayout.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { Outlet } from '@tanstack/vue-router' export const Route = createFileRoute('/_pathlessLayout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a pathless layout
) } ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/index.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/posts.$postId.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { ErrorComponent } from '@tanstack/vue-router' import { fetchPost } from '../posts' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.value.title}

{post.value.body}
) } ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/posts.index.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/js-only-file-based/src/routes/posts.route.jsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { Link, Outlet } from '@tanstack/vue-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsLayoutComponent, }) function PostsLayoutComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/js-only-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/js-only-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts', exact: true }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested pathless layouts', async ({ page }) => { await page.getByRole('link', { name: 'Pathless Layout', exact: true }).click() await expect(page.locator('#app')).toContainText("I'm a pathless layout") await expect(page.locator('#app')).toContainText( "I'm a nested pathless layout", ) await page.getByRole('link', { name: 'Go to route A' }).click() await expect(page.locator('#app')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Go to route B' }).click() await expect(page.locator('#app')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/js-only-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/js-only-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/js-only-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "vue", "target": "ESNext", "moduleResolution": "Bundler", "module": "ESNext", "resolveJsonModule": true, "allowJs": true, "skipLibCheck": true }, "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/js-only-file-based/vite.config.js ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'vue', autoCodeSplitting: true }), vue(), vueJsx(), ], }) ================================================ FILE: e2e/vue-router/rspack-basic-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/.gitignore ================================================ # Local .DS_Store *.local *.log* # Dist node_modules dist/ dist-hash/ # IDE .vscode/* !.vscode/extensions.json .idea # E2E src/routeTree.gen.ts test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/rspack-basic-file-based/README.md ================================================ # Example To run this example: - `pnpm install` - `pnpm dev` ================================================ FILE: e2e/vue-router/rspack-basic-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-rspack-basic-file-based", "private": true, "type": "module", "scripts": { "dev": "rsbuild dev --port 3000", "build": "rsbuild build && tsc --noEmit", "preview": "rsbuild preview", "start": "rsbuild preview", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@rsbuild/core": "^1.2.4", "@rsbuild/plugin-babel": "^1.0.6", "@rsbuild/plugin-vue": "^1.2.2", "@rsbuild/plugin-vue-jsx": "^1.1.1", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "postcss": "^8.5.1", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `PUBLIC_NODE_ENV="test" PUBLIC_EXTERNAL_PORT=${EXTERNAL_PORT} PUBLIC_SERVER_PORT=${PORT} pnpm build && PUBLIC_NODE_ENV="test" PUBLIC_EXTERNAL_PORT=${EXTERNAL_PORT} PUBLIC_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/rspack-basic-file-based/postcss.config.mjs ================================================ export default { plugins: { '@tailwindcss/postcss': {}, }, } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/rsbuild.config.ts ================================================ import { defineConfig } from '@rsbuild/core' import { pluginVue } from '@rsbuild/plugin-vue' import { pluginVueJsx } from '@rsbuild/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/rspack' import { pluginBabel } from '@rsbuild/plugin-babel' export default defineConfig({ plugins: [ pluginBabel({ include: /\.(?:jsx|tsx)$/, }), pluginVue(), pluginVueJsx(), ], source: { define: { // Rsbuild only defines `import.meta.env.PUBLIC_*` when the env var exists. // When it doesn't, rspack can transform `import.meta.env` to `void 0`, which // makes `import.meta.env.PUBLIC_*` crash at runtime. 'import.meta.env.PUBLIC_NODE_ENV': JSON.stringify( process.env.PUBLIC_NODE_ENV ?? '', ), 'import.meta.env.PUBLIC_EXTERNAL_PORT': JSON.stringify( process.env.PUBLIC_EXTERNAL_PORT ?? '', ), }, }, tools: { rspack: { plugins: [tanstackRouter({ target: 'vue', autoCodeSplitting: true })], }, }, }) ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/app.tsx ================================================ import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const App = () => { return } export default App ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/env.d.ts ================================================ /// ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/index.tsx ================================================ import { createApp } from 'vue' import App from './app' const rootEl = document.getElementById('root') if (!rootEl?.innerHTML) { createApp({ setup() { return () => }, }).mount('#root') } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.PUBLIC_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.PUBLIC_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/_layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/posts.$postId.tsx ================================================ import { ErrorComponent, createFileRoute } from '@tanstack/vue-router' import { fetchPost } from '../posts' import type { ErrorComponentProps } from '@tanstack/vue-router' export function PostErrorComponent({ error }: ErrorComponentProps) { return } export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) function PostComponent() { const post = Route.useLoaderData() return (

{post.value.title}

{post.value.body}
) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/posts.index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/routes/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source './**/*.{js,jsx,ts,tsx}'; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#root')).toContainText("I'm a layout") await expect(page.locator('#root')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#root')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#root')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/rspack-basic-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/rspack-basic-file-based/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2020", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "module": "ESNext", "jsx": "preserve", "jsxImportSource": "vue", "strict": true, "skipLibCheck": true, "isolatedModules": true, "resolveJsonModule": true, "useDefineForClassFields": true, "allowJs": true }, "include": ["src", "playwright.config.ts", "tests"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/.gitignore ================================================ # Local .DS_Store *.local *.log* # Dist node_modules dist/ dist-hash/ # IDE .vscode/* !.vscode/extensions.json .idea # E2E src/routeTree.gen.ts test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/README.md ================================================ # Example To run this example: - `pnpm install` - `pnpm dev` ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json ================================================ { "name": "tanstack-router-e2e-vue-rspack-basic-virtual-named-export-config-file-based", "private": true, "type": "module", "scripts": { "dev": "rsbuild dev --port 3000", "build": "rsbuild build && tsc --noEmit", "preview": "rsbuild preview", "start": "rsbuild preview", "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" }, "dependencies": { "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "redaxios": "^0.5.1", "vue": "^3.5.16" }, "devDependencies": { "@playwright/test": "^1.50.1", "@rsbuild/core": "^1.2.4", "@rsbuild/plugin-babel": "^1.0.6", "@rsbuild/plugin-vue": "^1.2.2", "@rsbuild/plugin-vue-jsx": "^1.1.1", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@tanstack/virtual-file-routes": "workspace:^", "postcss": "^8.5.1", "tailwindcss": "^4.2.2", "typescript": "^5.7.2", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `PUBLIC_NODE_ENV="test" PUBLIC_EXTERNAL_PORT=${EXTERNAL_PORT} PUBLIC_SERVER_PORT=${PORT} pnpm build && PUBLIC_SERVER_PORT=${PORT} pnpm start --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/postcss.config.mjs ================================================ export default { plugins: { '@tailwindcss/postcss': {}, }, } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('second', 'layout/second-layout.tsx', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/rsbuild.config.ts ================================================ import { defineConfig } from '@rsbuild/core' import { pluginVue } from '@rsbuild/plugin-vue' import { pluginVueJsx } from '@rsbuild/plugin-vue-jsx' import { tanstackRouter } from '@tanstack/router-plugin/rspack' import { pluginBabel } from '@rsbuild/plugin-babel' export default defineConfig({ plugins: [ pluginBabel({ include: /\.(?:jsx|tsx)$/, }), pluginVue(), pluginVueJsx(), ], source: { define: { // Rsbuild only defines `import.meta.env.PUBLIC_*` when the env var exists. // When it doesn't, rspack can transform `import.meta.env` to `void 0`, which // makes `import.meta.env.PUBLIC_*` crash at runtime. 'import.meta.env.PUBLIC_NODE_ENV': JSON.stringify( process.env.PUBLIC_NODE_ENV ?? '', ), 'import.meta.env.PUBLIC_EXTERNAL_PORT': JSON.stringify( process.env.PUBLIC_EXTERNAL_PORT ?? '', ), }, }, tools: { rspack: { plugins: [ tanstackRouter({ target: 'vue', autoCodeSplitting: true, virtualRouteConfig: './routes.ts', }), ], }, }, }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/app.tsx ================================================ import { RouterProvider, createRouter } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const App = () => { return } export default App ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/env.d.ts ================================================ /// ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/index.tsx ================================================ import { createApp } from 'vue' import App from './app' const rootEl = document.getElementById('root') if (!rootEl?.innerHTML) { createApp({ setup() { return () => }, }).mount('#root') } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/vue-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.PUBLIC_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.PUBLIC_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/a.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_first/_second/layout-a')({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/b.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_first/_second/layout-b')({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/classic/hello/')({ component: () =>
This is the index
, }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/classic/hello')({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/classic/hello/universe')({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/classic/hello/world')({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/home.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/')({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx ================================================ import { Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_first')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/_first/_second')({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx ================================================ import { ErrorComponent, createFileRoute } from '@tanstack/vue-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/vue-router' export function PostErrorComponent({ error }: ErrorComponentProps) { return } export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) function PostComponent() { const post = Route.useLoaderData() return (

{post.value.title}

{post.value.body}
) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx ================================================ import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute('/posts')({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[ ...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }, ].map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}

) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/routes/root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @source './**/*.{js,jsx,ts,tsx}'; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/tests/app.spec.ts ================================================ import { expect, test } from '@playwright/test' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('Navigating to a post page', async ({ page }) => { await page.getByRole('link', { name: 'Posts' }).click() await page.getByRole('link', { name: 'sunt aut facere repe' }).click() await expect(page.getByRole('heading')).toContainText('sunt aut facere') }) test('Navigating nested layouts', async ({ page }) => { await page.getByRole('link', { name: 'Layout', exact: true }).click() await expect(page.locator('#root')).toContainText("I'm a layout") await expect(page.locator('#root')).toContainText("I'm a nested layout") await page.getByRole('link', { name: 'Layout A' }).click() await expect(page.locator('#root')).toContainText("I'm layout A!") await page.getByRole('link', { name: 'Layout B' }).click() await expect(page.locator('#root')).toContainText("I'm layout B!") }) test('Navigating to a not-found route', async ({ page }) => { await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() await expect(page.getByRole('paragraph')).toContainText( 'This is the notFoundComponent configured on root route', ) await page.getByRole('link', { name: 'Start Over' }).click() await expect(page.getByRole('heading')).toContainText('Welcome Home!') }) ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/tests/setup/global.setup.ts ================================================ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function setup() { await e2eStartDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/tests/setup/global.teardown.ts ================================================ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' import packageJson from '../../package.json' with { type: 'json' } export default async function teardown() { await e2eStopDummyServer(packageJson.name) } ================================================ FILE: e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2020", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "module": "ESNext", "jsx": "preserve", "jsxImportSource": "vue", "strict": true, "skipLibCheck": true, "isolatedModules": true, "resolveJsonModule": true, "useDefineForClassFields": true, "allowJs": true }, "include": ["src", "playwright.config.ts", "tests", "./routes.ts"], "exclude": ["node_modules", "dist"] } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/.gitignore ================================================ node_modules .DS_Store dist dist-ssr dist-hash *.local test-results ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/README.md ================================================ # Scroll Restoration Testing Sandbox with Vite To run this example: - `npm install` - `npm start` This sandbox is for testing the scroll restoration behavior. ## Setup - Create your files in `src/routes` directory. - Make sure you update the arrays in the following files with the expected routes - `tests/app.spec.ts` > routes array - `src/routes/__root.tsx` > Nav component, routes array - `src/routes/index.tsx` > Navigation test suite, routes array ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/index.html ================================================
================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/package.json ================================================ { "name": "tanstack-router-e2e-vue-scroll-restoration-sandbox-vite", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "dev:hash": "VITE_APP_HISTORY=hash vite --port 3000", "dev:e2e": "vite", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite", "test:e2e:browser": "VITE_APP_HISTORY=browser playwright test --config=playwright.browser.config.ts --project=chromium", "test:e2e:hash": "VITE_APP_HISTORY=hash playwright test --config=playwright.hash.config.ts --project=chromium", "test:e2e": "rm -rf port*.txt; pnpm run test:e2e:browser && pnpm run test:e2e:hash" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/router-plugin": "workspace:^", "@tanstack/vue-router": "workspace:^", "@tanstack/vue-router-devtools": "workspace:^", "@tanstack/zod-adapter": "workspace:^", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16", "zod": "^3.24.2" }, "devDependencies": { "@playwright/test": "^1.50.1", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", "vite": "^8.0.0", "vue-tsc": "^3.1.5" } } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/playwright.browser.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, resolveRuntimeSuffix, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort( packageJson.name + `-${resolveRuntimeSuffix('browser')}`, ) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=browser pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=browser pnpm preview --port ${PORT}`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/playwright.hash.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, getTestServerPort, resolveRuntimeSuffix, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } const PORT = await getTestServerPort( packageJson.name + `-${resolveRuntimeSuffix('hash')}`, ) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL, }, webServer: { command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=hash vite build --outDir dist-hash && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} VITE_APP_HISTORY=hash pnpm preview --port ${PORT} --outDir dist-hash`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], }) ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/main.tsx ================================================ import { createApp } from 'vue' import { RouterProvider, createHashHistory, createRouter, } from '@tanstack/vue-router' import { routeTree } from './routeTree.gen' import type { RouterHistory } from '@tanstack/vue-router' import './styles.css' let history: RouterHistory | undefined if (import.meta.env.VITE_APP_HISTORY === 'hash') { history = createHashHistory() } // Set up a Router instance const router = createRouter({ routeTree, history, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/vue-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app') if (!rootElement?.innerHTML) { createApp({ setup() { return () => }, }).mount('#app') } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/posts.tsx ================================================ import axios from 'redaxios' export function sleep(ms: number) { return new Promise((r) => setTimeout(r, ms)) } export type PostType = { id: string title: string body: string } export class PostNotFoundError extends Error {} let queryURL = 'https://jsonplaceholder.typicode.com' if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`${queryURL}/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw new PostNotFoundError(`Post with id "${postId}" not found!`) } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>(`${queryURL}/posts`) .then((r) => r.data.slice(0, 10)) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { createFileRoute } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' import { Route as testsLazyPageRouteImport } from './routes/(tests)/lazy-page' const testsVirtualPageLazyRouteImport = createFileRoute( '/(tests)/virtual-page', )() const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport .update({ id: '/(tests)/virtual-page', path: '/virtual-page', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ id: '/(tests)/page-with-search', path: '/page-with-search', getParentRoute: () => rootRouteImport, } as any) const testsNormalPageRoute = testsNormalPageRouteImport.update({ id: '/(tests)/normal-page', path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .update({ id: '/(tests)/lazy-with-loader-page', path: '/lazy-with-loader-page', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(tests)/lazy-with-loader-page.lazy').then((d) => d.Route), ) const testsLazyPageRoute = testsLazyPageRouteImport .update({ id: '/(tests)/lazy-page', path: '/lazy-page', getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/lazy-page': typeof testsLazyPageRoute '/lazy-with-loader-page': typeof testsLazyWithLoaderPageRoute '/normal-page': typeof testsNormalPageRoute '/page-with-search': typeof testsPageWithSearchRoute '/virtual-page': typeof testsVirtualPageLazyRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/lazy-page': typeof testsLazyPageRoute '/lazy-with-loader-page': typeof testsLazyWithLoaderPageRoute '/normal-page': typeof testsNormalPageRoute '/page-with-search': typeof testsPageWithSearchRoute '/virtual-page': typeof testsVirtualPageLazyRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/(tests)/lazy-page': typeof testsLazyPageRoute '/(tests)/lazy-with-loader-page': typeof testsLazyWithLoaderPageRoute '/(tests)/normal-page': typeof testsNormalPageRoute '/(tests)/page-with-search': typeof testsPageWithSearchRoute '/(tests)/virtual-page': typeof testsVirtualPageLazyRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/lazy-page' | '/lazy-with-loader-page' | '/normal-page' | '/page-with-search' | '/virtual-page' fileRoutesByTo: FileRoutesByTo to: | '/' | '/lazy-page' | '/lazy-with-loader-page' | '/normal-page' | '/page-with-search' | '/virtual-page' id: | '__root__' | '/' | '/(tests)/lazy-page' | '/(tests)/lazy-with-loader-page' | '/(tests)/normal-page' | '/(tests)/page-with-search' | '/(tests)/virtual-page' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute testsLazyPageRoute: typeof testsLazyPageRoute testsLazyWithLoaderPageRoute: typeof testsLazyWithLoaderPageRoute testsNormalPageRoute: typeof testsNormalPageRoute testsPageWithSearchRoute: typeof testsPageWithSearchRoute testsVirtualPageLazyRoute: typeof testsVirtualPageLazyRoute } declare module '@tanstack/vue-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/(tests)/virtual-page': { id: '/(tests)/virtual-page' path: '/virtual-page' fullPath: '/virtual-page' preLoaderRoute: typeof testsVirtualPageLazyRouteImport parentRoute: typeof rootRouteImport } '/(tests)/page-with-search': { id: '/(tests)/page-with-search' path: '/page-with-search' fullPath: '/page-with-search' preLoaderRoute: typeof testsPageWithSearchRouteImport parentRoute: typeof rootRouteImport } '/(tests)/normal-page': { id: '/(tests)/normal-page' path: '/normal-page' fullPath: '/normal-page' preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/lazy-with-loader-page': { id: '/(tests)/lazy-with-loader-page' path: '/lazy-with-loader-page' fullPath: '/lazy-with-loader-page' preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/lazy-page': { id: '/(tests)/lazy-page' path: '/lazy-page' fullPath: '/lazy-page' preLoaderRoute: typeof testsLazyPageRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, testsLazyPageRoute: testsLazyPageRoute, testsLazyWithLoaderPageRoute: testsLazyWithLoaderPageRoute, testsNormalPageRoute: testsNormalPageRoute, testsPageWithSearchRoute: testsPageWithSearchRoute, testsVirtualPageLazyRoute: testsVirtualPageLazyRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-page.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/vue-router' import { ScrollBlock } from '../-components/scroll-block' export const Route = createLazyFileRoute('/(tests)/lazy-page')({ component: Component, }) function Component() { return (

lazy-page


) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-page.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/(tests)/lazy-page')({}) ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-with-loader-page.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/vue-router' import { ScrollBlock } from '../-components/scroll-block' export const Route = createLazyFileRoute('/(tests)/lazy-with-loader-page')({ component: Component, }) function Component() { return (

lazy-with-loader-page


) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/lazy-with-loader-page.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { sleep } from '../../posts' export const Route = createFileRoute('/(tests)/lazy-with-loader-page')({ loader: async () => { await sleep(1000) return { foo: 'bar' } }, }) ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/normal-page.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/normal-page')({ component: Component, }) function Component() { return (

normal-page


) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/page-with-search.tsx ================================================ import { createFileRoute } from '@tanstack/vue-router' import { z } from 'zod' import { zodValidator } from '@tanstack/zod-adapter' import { ScrollBlock } from '../-components/scroll-block' export const Route = createFileRoute('/(tests)/page-with-search')({ validateSearch: zodValidator(z.object({ where: z.string() })), component: Component, }) function Component() { return (

page-with-search


) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/(tests)/virtual-page.lazy.tsx ================================================ import { createLazyFileRoute } from '@tanstack/vue-router' import { ScrollBlock } from '../-components/scroll-block' export const Route = createLazyFileRoute('/(tests)/virtual-page')({ component: Component, }) function Component() { return (

virtual-page


) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/-components/scroll-block.tsx ================================================ export const atTheTopId = 'at-the-top' export const atTheBottomId = 'at-the-bottom' export function ScrollBlock({ number = 100 }: { number?: number }) { return ( <>
{Array.from({ length: number }).map((_, i) => (
{i}
))}
At the bottom
) } ================================================ FILE: e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/__root.tsx ================================================ import { HeadContent, Link, Outlet, createRootRoute, linkOptions, } from '@tanstack/vue-router' import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( <>
) } ================================================ FILE: examples/react/basic-ssr-streaming-file-based/src/routes/posts/index.tsx ================================================ import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, wrapInSuspense: true, errorComponent: ({ error }) => { return (
Failed to load post: {error.message}
) }, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: examples/react/basic-ssr-streaming-file-based/src/routes/posts/route.tsx ================================================ import * as React from 'react' import { Link, Outlet, createFileRoute } from '@tanstack/react-router' export type PostType = { id: string title: string body: string } export const Route = createFileRoute('/posts')({ loader: async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 300 + Math.round(Math.random() * 300)), ) return fetch('https://jsonplaceholder.typicode.com/posts') .then((d) => d.json() as Promise>) .then((d) => d.slice(0, 10)) }, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {posts.map((post) => { return (
  • {post.title.substring(0, 20)}
  • ) })}
  • This post does not exist

) } ================================================ FILE: examples/react/basic-ssr-streaming-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "esnext", "module": "esnext", "types": ["vite/client"], "moduleResolution": "bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "skipLibCheck": true }, "include": ["src", "vite.config.ts"] } ================================================ FILE: examples/react/basic-ssr-streaming-file-based/vite.config.ts ================================================ import path from 'node:path' import url from 'node:url' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import type { BuildEnvironmentOptions } from 'vite' const __filename = url.fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) // SSR configuration const ssrBuildConfig: BuildEnvironmentOptions = { ssr: true, outDir: 'dist/server', ssrEmitAssets: true, copyPublicDir: false, emptyOutDir: true, rolldownOptions: { input: path.resolve(__dirname, 'src/entry-server.tsx'), output: { entryFileNames: '[name].js', chunkFileNames: 'assets/[name]-[hash].js', assetFileNames: 'assets/[name]-[hash][extname]', }, }, } // Client-specific configuration const clientBuildConfig: BuildEnvironmentOptions = { outDir: 'dist/client', emitAssets: true, copyPublicDir: true, emptyOutDir: true, rolldownOptions: { input: path.resolve(__dirname, 'src/entry-client.tsx'), output: { entryFileNames: 'static/[name].js', chunkFileNames: 'static/assets/[name]-[hash].js', assetFileNames: 'static/assets/[name]-[hash][extname]', }, }, } // https://vitejs.dev/config/ export default defineConfig((configEnv) => { return { plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true }), react(), ], build: configEnv.isSsrBuild ? ssrBuildConfig : clientBuildConfig, } }) ================================================ FILE: examples/react/basic-virtual-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: examples/react/basic-virtual-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: examples/react/basic-virtual-file-based/.vscode/settings.json ================================================ { "files.watcherExclude": { "**/routeTree.gen.ts": true }, "search.exclude": { "**/routeTree.gen.ts": true }, "files.readonlyInclude": { "**/routeTree.gen.ts": true } } ================================================ FILE: examples/react/basic-virtual-file-based/README.md ================================================ # TanStack Router - Virtual File-Based Routing Example An example demonstrating virtual file-based routing. - [TanStack Router Docs](https://tanstack.com/router) ## Start a new project based on this example To start a new project based on this example, run: ```sh npx gitpick TanStack/router/tree/main/examples/react/basic-virtual-file-based basic-virtual-file-based ``` ## Getting Started Install dependencies: ```sh pnpm install ``` Start the development server: ```sh pnpm dev ``` ## Build Build for production: ```sh pnpm build ``` ## About This Example This example demonstrates: - Virtual file-based routing - Dynamic route generation - Programmatic route creation - Type-safe virtual routes ================================================ FILE: examples/react/basic-virtual-file-based/index.html ================================================ Vite App
================================================ FILE: examples/react/basic-virtual-file-based/package.json ================================================ { "name": "tanstack-router-react-example-basic-virtual-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.168.1", "@tanstack/react-router-devtools": "^1.166.10", "@tanstack/router-plugin": "^1.167.1", "@tanstack/virtual-file-routes": "^1.161.7", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "typescript": "^5.7.2", "vite": "^8.0.0" } } ================================================ FILE: examples/react/basic-virtual-file-based/routes.ts ================================================ import { index, layout, physical, rootRoute, route, } from '@tanstack/virtual-file-routes' export const routes = rootRoute('root.tsx', [ index('home.tsx'), route('/posts', 'posts/posts.tsx', [ index('posts/posts-home.tsx'), route('$postId', 'posts/posts-detail.tsx'), ]), layout('first', 'layout/first-layout.tsx', [ layout('layout/second-layout.tsx', [ route('route-without-file', [ route('/layout-a', 'a.tsx'), route('/layout-b', 'b.tsx'), ]), ]), ]), physical('/classic', 'file-based-subtree'), ]) ================================================ FILE: examples/react/basic-virtual-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: examples/react/basic-virtual-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>('https://jsonplaceholder.typicode.com/posts') .then((r) => r.data.slice(0, 10)) } ================================================ FILE: examples/react/basic-virtual-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/root' import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' import { Route as bRouteImport } from './routes/b' import { Route as aRouteImport } from './routes/a' const postsPostsRoute = postsPostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) const homeRoute = homeRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => postsPostsRoute, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ id: '/classic/hello', path: '/classic/hello', getParentRoute: () => rootRouteImport, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ id: '/world', path: '/world', getParentRoute: () => ClassicHelloRouteRoute, } as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) const bRoute = bRouteImport.update({ id: '/route-without-file/layout-b', path: '/route-without-file/layout-b', getParentRoute: () => layoutSecondLayoutRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) export interface FileRoutesByFullPath { '/': typeof homeRoute '/posts': typeof postsPostsRouteWithChildren '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute '/route-without-file/layout-a': typeof aRoute '/route-without-file/layout-b': typeof bRoute } export interface FileRoutesByTo { '/': typeof homeRoute '/posts': typeof postsPostsHomeRoute '/posts/$postId': typeof postsPostsDetailRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello': typeof ClassicHelloIndexRoute '/route-without-file/layout-a': typeof aRoute '/route-without-file/layout-b': typeof bRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof homeRoute '/_first': typeof layoutFirstLayoutRouteWithChildren '/posts': typeof postsPostsRouteWithChildren '/classic/hello': typeof ClassicHelloRouteRouteWithChildren '/posts/': typeof postsPostsHomeRoute '/_first/_second-layout': typeof layoutSecondLayoutRouteWithChildren '/posts/$postId': typeof postsPostsDetailRoute '/classic/hello/universe': typeof ClassicHelloUniverseRoute '/classic/hello/world': typeof ClassicHelloWorldRoute '/classic/hello/': typeof ClassicHelloIndexRoute '/_first/_second-layout/route-without-file/layout-a': typeof aRoute '/_first/_second-layout/route-without-file/layout-b': typeof bRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/classic/hello' | '/posts/' | '/posts/$postId' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' | '/route-without-file/layout-a' | '/route-without-file/layout-b' fileRoutesByTo: FileRoutesByTo to: | '/' | '/posts' | '/posts/$postId' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello' | '/route-without-file/layout-a' | '/route-without-file/layout-b' id: | '__root__' | '/' | '/_first' | '/posts' | '/classic/hello' | '/posts/' | '/_first/_second-layout' | '/posts/$postId' | '/classic/hello/universe' | '/classic/hello/world' | '/classic/hello/' | '/_first/_second-layout/route-without-file/layout-a' | '/_first/_second-layout/route-without-file/layout-b' fileRoutesById: FileRoutesById } export interface RootRouteChildren { homeRoute: typeof homeRoute layoutFirstLayoutRoute: typeof layoutFirstLayoutRouteWithChildren postsPostsRoute: typeof postsPostsRouteWithChildren ClassicHelloRouteRoute: typeof ClassicHelloRouteRouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } '/_first': { id: '/_first' path: '' fullPath: '/' preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof postsPostsDetailRouteImport parentRoute: typeof postsPostsRoute } '/_first/_second-layout': { id: '/_first/_second-layout' path: '' fullPath: '/' preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } '/classic/hello': { id: '/classic/hello' path: '/classic/hello' fullPath: '/classic/hello' preLoaderRoute: typeof ClassicHelloRouteRouteImport parentRoute: typeof rootRouteImport } '/classic/hello/': { id: '/classic/hello/' path: '/' fullPath: '/classic/hello/' preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/world': { id: '/classic/hello/world' path: '/world' fullPath: '/classic/hello/world' preLoaderRoute: typeof ClassicHelloWorldRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' fullPath: '/classic/hello/universe' preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-b': { id: '/_first/_second-layout/route-without-file/layout-b' path: '/route-without-file/layout-b' fullPath: '/route-without-file/layout-b' preLoaderRoute: typeof bRouteImport parentRoute: typeof layoutSecondLayoutRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' path: '/route-without-file/layout-a' fullPath: '/route-without-file/layout-a' preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } } } declare module './routes/home' { const createFileRoute: CreateFileRoute< '/', FileRoutesByPath['/']['parentRoute'], FileRoutesByPath['/']['id'], FileRoutesByPath['/']['path'], FileRoutesByPath['/']['fullPath'] > } declare module './routes/layout/first-layout' { const createFileRoute: CreateFileRoute< '/_first', FileRoutesByPath['/_first']['parentRoute'], FileRoutesByPath['/_first']['id'], FileRoutesByPath['/_first']['path'], FileRoutesByPath['/_first']['fullPath'] > } declare module './routes/posts/posts' { const createFileRoute: CreateFileRoute< '/posts', FileRoutesByPath['/posts']['parentRoute'], FileRoutesByPath['/posts']['id'], FileRoutesByPath['/posts']['path'], FileRoutesByPath['/posts']['fullPath'] > } declare module './routes/file-based-subtree/hello/route' { const createFileRoute: CreateFileRoute< '/classic/hello', FileRoutesByPath['/classic/hello']['parentRoute'], FileRoutesByPath['/classic/hello']['id'], FileRoutesByPath['/classic/hello']['path'], FileRoutesByPath['/classic/hello']['fullPath'] > } declare module './routes/posts/posts-home' { const createFileRoute: CreateFileRoute< '/posts/', FileRoutesByPath['/posts/']['parentRoute'], FileRoutesByPath['/posts/']['id'], FileRoutesByPath['/posts/']['path'], FileRoutesByPath['/posts/']['fullPath'] > } declare module './routes/layout/second-layout' { const createFileRoute: CreateFileRoute< '/_first/_second-layout', FileRoutesByPath['/_first/_second-layout']['parentRoute'], FileRoutesByPath['/_first/_second-layout']['id'], FileRoutesByPath['/_first/_second-layout']['path'], FileRoutesByPath['/_first/_second-layout']['fullPath'] > } declare module './routes/posts/posts-detail' { const createFileRoute: CreateFileRoute< '/posts/$postId', FileRoutesByPath['/posts/$postId']['parentRoute'], FileRoutesByPath['/posts/$postId']['id'], FileRoutesByPath['/posts/$postId']['path'], FileRoutesByPath['/posts/$postId']['fullPath'] > } declare module './routes/file-based-subtree/hello/universe' { const createFileRoute: CreateFileRoute< '/classic/hello/universe', FileRoutesByPath['/classic/hello/universe']['parentRoute'], FileRoutesByPath['/classic/hello/universe']['id'], FileRoutesByPath['/classic/hello/universe']['path'], FileRoutesByPath['/classic/hello/universe']['fullPath'] > } declare module './routes/file-based-subtree/hello/world' { const createFileRoute: CreateFileRoute< '/classic/hello/world', FileRoutesByPath['/classic/hello/world']['parentRoute'], FileRoutesByPath['/classic/hello/world']['id'], FileRoutesByPath['/classic/hello/world']['path'], FileRoutesByPath['/classic/hello/world']['fullPath'] > } declare module './routes/file-based-subtree/hello/index' { const createFileRoute: CreateFileRoute< '/classic/hello/', FileRoutesByPath['/classic/hello/']['parentRoute'], FileRoutesByPath['/classic/hello/']['id'], FileRoutesByPath['/classic/hello/']['path'], FileRoutesByPath['/classic/hello/']['fullPath'] > } declare module './routes/a' { const createFileRoute: CreateFileRoute< '/_first/_second-layout/route-without-file/layout-a', FileRoutesByPath['/_first/_second-layout/route-without-file/layout-a']['parentRoute'], FileRoutesByPath['/_first/_second-layout/route-without-file/layout-a']['id'], FileRoutesByPath['/_first/_second-layout/route-without-file/layout-a']['path'], FileRoutesByPath['/_first/_second-layout/route-without-file/layout-a']['fullPath'] > } declare module './routes/b' { const createFileRoute: CreateFileRoute< '/_first/_second-layout/route-without-file/layout-b', FileRoutesByPath['/_first/_second-layout/route-without-file/layout-b']['parentRoute'], FileRoutesByPath['/_first/_second-layout/route-without-file/layout-b']['id'], FileRoutesByPath['/_first/_second-layout/route-without-file/layout-b']['path'], FileRoutesByPath['/_first/_second-layout/route-without-file/layout-b']['fullPath'] > } interface layoutSecondLayoutRouteChildren { aRoute: typeof aRoute bRoute: typeof bRoute } const layoutSecondLayoutRouteChildren: layoutSecondLayoutRouteChildren = { aRoute: aRoute, bRoute: bRoute, } const layoutSecondLayoutRouteWithChildren = layoutSecondLayoutRoute._addFileChildren(layoutSecondLayoutRouteChildren) interface layoutFirstLayoutRouteChildren { layoutSecondLayoutRoute: typeof layoutSecondLayoutRouteWithChildren } const layoutFirstLayoutRouteChildren: layoutFirstLayoutRouteChildren = { layoutSecondLayoutRoute: layoutSecondLayoutRouteWithChildren, } const layoutFirstLayoutRouteWithChildren = layoutFirstLayoutRoute._addFileChildren(layoutFirstLayoutRouteChildren) interface postsPostsRouteChildren { postsPostsHomeRoute: typeof postsPostsHomeRoute postsPostsDetailRoute: typeof postsPostsDetailRoute } const postsPostsRouteChildren: postsPostsRouteChildren = { postsPostsHomeRoute: postsPostsHomeRoute, postsPostsDetailRoute: postsPostsDetailRoute, } const postsPostsRouteWithChildren = postsPostsRoute._addFileChildren( postsPostsRouteChildren, ) interface ClassicHelloRouteRouteChildren { ClassicHelloUniverseRoute: typeof ClassicHelloUniverseRoute ClassicHelloWorldRoute: typeof ClassicHelloWorldRoute ClassicHelloIndexRoute: typeof ClassicHelloIndexRoute } const ClassicHelloRouteRouteChildren: ClassicHelloRouteRouteChildren = { ClassicHelloUniverseRoute: ClassicHelloUniverseRoute, ClassicHelloWorldRoute: ClassicHelloWorldRoute, ClassicHelloIndexRoute: ClassicHelloIndexRoute, } const ClassicHelloRouteRouteWithChildren = ClassicHelloRouteRoute._addFileChildren(ClassicHelloRouteRouteChildren) const rootRouteChildren: RootRouteChildren = { homeRoute: homeRoute, layoutFirstLayoutRoute: layoutFirstLayoutRouteWithChildren, postsPostsRoute: postsPostsRouteWithChildren, ClassicHelloRouteRoute: ClassicHelloRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/file-based-subtree/hello/index.tsx ================================================ export const Route = createFileRoute({ component: () =>
This is the index
, }) ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/file-based-subtree/hello/route.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: () => (
Hello!
{' '} say hello to the universe {' '} say hello to the world
), }) ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/file-based-subtree/hello/universe.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/universe!
, }) ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/file-based-subtree/hello/world.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /classic/hello/world!
, }) ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/layout/first-layout.tsx ================================================ import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/layout/second-layout.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/posts/posts-detail.tsx ================================================ import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent as any, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/posts/posts-home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/posts/posts.tsx ================================================ import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: examples/react/basic-virtual-file-based/src/routes/root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} Subtree {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: examples/react/basic-virtual-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: examples/react/basic-virtual-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "module": "Preserve", "moduleResolution": "Bundler", "target": "ESNext", "lib": ["DOM", "DOM.Iterable", "ES2022"], "skipLibCheck": true } } ================================================ FILE: examples/react/basic-virtual-file-based/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', autoCodeSplitting: true, verboseFileRoutes: false, virtualRouteConfig: './routes.ts', }), react(), ], }) ================================================ FILE: examples/react/basic-virtual-inside-file-based/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: examples/react/basic-virtual-inside-file-based/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ ================================================ FILE: examples/react/basic-virtual-inside-file-based/.vscode/settings.json ================================================ { "files.watcherExclude": { "**/routeTree.gen.ts": true }, "search.exclude": { "**/routeTree.gen.ts": true }, "files.readonlyInclude": { "**/routeTree.gen.ts": true } } ================================================ FILE: examples/react/basic-virtual-inside-file-based/README.md ================================================ # TanStack Router - Virtual Inside File-Based Routing Example An example demonstrating virtual routes within a file-based routing structure. - [TanStack Router Docs](https://tanstack.com/router) ## Start a new project based on this example To start a new project based on this example, run: ```sh npx gitpick TanStack/router/tree/main/examples/react/basic-virtual-inside-file-based basic-virtual-inside-file-based ``` ## Getting Started Install dependencies: ```sh pnpm install ``` Start the development server: ```sh pnpm dev ``` ## Build Build for production: ```sh pnpm build ``` ## About This Example This example demonstrates: - Combining virtual and file-based routing - Hybrid routing approach - Dynamic route generation within file-based structure - Flexible route configuration ================================================ FILE: examples/react/basic-virtual-inside-file-based/index.html ================================================ Vite App
================================================ FILE: examples/react/basic-virtual-inside-file-based/package.json ================================================ { "name": "tanstack-router-react-example-basic-virtual-inside-file-based", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.168.1", "@tanstack/react-router-devtools": "^1.166.10", "@tanstack/router-plugin": "^1.167.1", "@tanstack/virtual-file-routes": "^1.161.7", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "typescript": "^5.7.2", "vite": "^8.0.0" } } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' // Set up a Router instance const router = createRouter({ routeTree, defaultPreload: 'intent', defaultStaleTime: 5000, scrollRestoration: true, }) // Register things for typesafety declare module '@tanstack/react-router' { interface Register { router: typeof router } } const rootElement = document.getElementById('app')! if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render() } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/posts.tsx ================================================ import { notFound } from '@tanstack/react-router' import axios from 'redaxios' export type PostType = { id: string title: string body: string } export const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) await new Promise((r) => setTimeout(r, 500)) const post = await axios .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) .then((r) => r.data) .catch((err) => { if (err.status === 404) { throw notFound() } throw err }) return post } export const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 500)) return axios .get>('https://jsonplaceholder.typicode.com/posts') .then((r) => r.data.slice(0, 10)) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routeTree.gen.ts ================================================ /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' import { Route as PostsRouteImport } from './routes/posts' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' import { Route as postsDetailsRouteImport } from './routes/posts/details' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as postsHomeRouteImport } from './routes/posts/home' import { Route as postsLetsGoIndexRouteImport } from './routes/posts/lets-go/index' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as postsLetsGoDeeperHomeRouteImport } from './routes/posts/lets-go/deeper/home' const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const postsDetailsRoute = postsDetailsRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, } as any) const postsHomeRoute = postsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) const postsLetsGoIndexRoute = postsLetsGoIndexRouteImport.update({ id: '/inception/', path: '/inception/', getParentRoute: () => PostsRoute, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ id: '/layout-a', path: '/layout-a', getParentRoute: () => LayoutLayout2Route, } as any) const postsLetsGoDeeperHomeRoute = postsLetsGoDeeperHomeRouteImport.update({ id: '/inception/deeper/', path: '/inception/deeper/', getParentRoute: () => PostsRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/posts': typeof PostsRouteWithChildren '/posts/': typeof postsHomeRoute '/posts/$postId': typeof postsDetailsRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/inception/': typeof postsLetsGoIndexRoute '/posts/inception/deeper/': typeof postsLetsGoDeeperHomeRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/posts': typeof postsHomeRoute '/posts/$postId': typeof postsDetailsRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/inception': typeof postsLetsGoIndexRoute '/posts/inception/deeper': typeof postsLetsGoDeeperHomeRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_layout': typeof LayoutRouteWithChildren '/posts': typeof PostsRouteWithChildren '/posts/': typeof postsHomeRoute '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/posts/$postId': typeof postsDetailsRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute '/posts/inception/': typeof postsLetsGoIndexRoute '/posts/inception/deeper/': typeof postsLetsGoDeeperHomeRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/posts' | '/posts/' | '/posts/$postId' | '/layout-a' | '/layout-b' | '/posts/inception/' | '/posts/inception/deeper/' fileRoutesByTo: FileRoutesByTo to: | '/' | '/posts' | '/posts/$postId' | '/layout-a' | '/layout-b' | '/posts/inception' | '/posts/inception/deeper' id: | '__root__' | '/' | '/_layout' | '/posts' | '/posts/' | '/_layout/_layout-2' | '/posts/$postId' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' | '/posts/inception/' | '/posts/inception/deeper/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute LayoutRoute: typeof LayoutRouteWithChildren PostsRoute: typeof PostsRouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/posts': { id: '/posts' path: '/posts' fullPath: '/posts' preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/_layout': { id: '/_layout' path: '' fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' fullPath: '/posts/$postId' preLoaderRoute: typeof postsDetailsRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } '/posts/': { id: '/posts/' path: '/' fullPath: '/posts/' preLoaderRoute: typeof postsHomeRouteImport parentRoute: typeof PostsRoute } '/posts/inception/': { id: '/posts/inception/' path: '/inception' fullPath: '/posts/inception/' preLoaderRoute: typeof postsLetsGoIndexRouteImport parentRoute: typeof PostsRoute } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' fullPath: '/layout-b' preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-a': { id: '/_layout/_layout-2/layout-a' path: '/layout-a' fullPath: '/layout-a' preLoaderRoute: typeof LayoutLayout2LayoutARouteImport parentRoute: typeof LayoutLayout2Route } '/posts/inception/deeper/': { id: '/posts/inception/deeper/' path: '/inception/deeper' fullPath: '/posts/inception/deeper/' preLoaderRoute: typeof postsLetsGoDeeperHomeRouteImport parentRoute: typeof PostsRoute } } } declare module './routes/index' { const createFileRoute: CreateFileRoute< '/', FileRoutesByPath['/']['parentRoute'], FileRoutesByPath['/']['id'], FileRoutesByPath['/']['path'], FileRoutesByPath['/']['fullPath'] > } declare module './routes/_layout' { const createFileRoute: CreateFileRoute< '/_layout', FileRoutesByPath['/_layout']['parentRoute'], FileRoutesByPath['/_layout']['id'], FileRoutesByPath['/_layout']['path'], FileRoutesByPath['/_layout']['fullPath'] > } declare module './routes/posts' { const createFileRoute: CreateFileRoute< '/posts', FileRoutesByPath['/posts']['parentRoute'], FileRoutesByPath['/posts']['id'], FileRoutesByPath['/posts']['path'], FileRoutesByPath['/posts']['fullPath'] > } declare module './routes/posts/home' { const createFileRoute: CreateFileRoute< '/posts/', FileRoutesByPath['/posts/']['parentRoute'], FileRoutesByPath['/posts/']['id'], FileRoutesByPath['/posts/']['path'], FileRoutesByPath['/posts/']['fullPath'] > } declare module './routes/_layout/_layout-2' { const createFileRoute: CreateFileRoute< '/_layout/_layout-2', FileRoutesByPath['/_layout/_layout-2']['parentRoute'], FileRoutesByPath['/_layout/_layout-2']['id'], FileRoutesByPath['/_layout/_layout-2']['path'], FileRoutesByPath['/_layout/_layout-2']['fullPath'] > } declare module './routes/posts/details' { const createFileRoute: CreateFileRoute< '/posts/$postId', FileRoutesByPath['/posts/$postId']['parentRoute'], FileRoutesByPath['/posts/$postId']['id'], FileRoutesByPath['/posts/$postId']['path'], FileRoutesByPath['/posts/$postId']['fullPath'] > } declare module './routes/_layout/_layout-2/layout-a' { const createFileRoute: CreateFileRoute< '/_layout/_layout-2/layout-a', FileRoutesByPath['/_layout/_layout-2/layout-a']['parentRoute'], FileRoutesByPath['/_layout/_layout-2/layout-a']['id'], FileRoutesByPath['/_layout/_layout-2/layout-a']['path'], FileRoutesByPath['/_layout/_layout-2/layout-a']['fullPath'] > } declare module './routes/_layout/_layout-2/layout-b' { const createFileRoute: CreateFileRoute< '/_layout/_layout-2/layout-b', FileRoutesByPath['/_layout/_layout-2/layout-b']['parentRoute'], FileRoutesByPath['/_layout/_layout-2/layout-b']['id'], FileRoutesByPath['/_layout/_layout-2/layout-b']['path'], FileRoutesByPath['/_layout/_layout-2/layout-b']['fullPath'] > } declare module './routes/posts/lets-go/index' { const createFileRoute: CreateFileRoute< '/posts/inception/', FileRoutesByPath['/posts/inception/']['parentRoute'], FileRoutesByPath['/posts/inception/']['id'], FileRoutesByPath['/posts/inception/']['path'], FileRoutesByPath['/posts/inception/']['fullPath'] > } declare module './routes/posts/lets-go/deeper/home' { const createFileRoute: CreateFileRoute< '/posts/inception/deeper/', FileRoutesByPath['/posts/inception/deeper/']['parentRoute'], FileRoutesByPath['/posts/inception/deeper/']['id'], FileRoutesByPath['/posts/inception/deeper/']['path'], FileRoutesByPath['/posts/inception/deeper/']['fullPath'] > } interface LayoutLayout2RouteChildren { LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute } const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, } const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( LayoutLayout2RouteChildren, ) interface LayoutRouteChildren { LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { LayoutLayout2Route: LayoutLayout2RouteWithChildren, } const LayoutRouteWithChildren = LayoutRoute._addFileChildren(LayoutRouteChildren) interface PostsRouteChildren { postsHomeRoute: typeof postsHomeRoute postsDetailsRoute: typeof postsDetailsRoute postsLetsGoIndexRoute: typeof postsLetsGoIndexRoute postsLetsGoDeeperHomeRoute: typeof postsLetsGoDeeperHomeRoute } const PostsRouteChildren: PostsRouteChildren = { postsHomeRoute: postsHomeRoute, postsDetailsRoute: postsDetailsRoute, postsLetsGoIndexRoute: postsLetsGoIndexRoute, postsLetsGoDeeperHomeRoute: postsLetsGoDeeperHomeRoute, } const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LayoutRoute: LayoutRouteWithChildren, PostsRoute: PostsRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/__root.tsx ================================================ import * as React from 'react' import { Link, Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, notFoundComponent: () => { return (

This is the notFoundComponent configured on root route

Start Over
) }, }) function RootComponent() { return ( <>
Home {' '} Posts {' '} Layout {' '} This Route Does Not Exist

{/* Start rendering router matches */} ) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/_layout/_layout-2/layout-a.tsx ================================================ export const Route = createFileRoute({ component: LayoutAComponent, }) function LayoutAComponent() { return
I'm layout A!
} ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/_layout/_layout-2/layout-b.tsx ================================================ export const Route = createFileRoute({ component: LayoutBComponent, }) function LayoutBComponent() { return
I'm layout B!
} ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/_layout/_layout-2.tsx ================================================ import { Link, Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a nested layout
Layout A Layout B
) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/_layout.tsx ================================================ import { Outlet } from '@tanstack/react-router' export const Route = createFileRoute({ component: LayoutComponent, }) function LayoutComponent() { return (
I'm a layout
) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/index.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: Home, }) function Home() { return (

Welcome Home!

) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts/__virtual.ts ================================================ import { defineVirtualSubtreeConfig, index, physical, route, } from '@tanstack/virtual-file-routes' // this just shows that you can use an async function to define your virtual routes export default defineVirtualSubtreeConfig(async () => [ index('home.tsx'), route('$postId', 'details.tsx'), physical('/inception', 'lets-go'), ]) ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts/details.tsx ================================================ import * as React from 'react' import { ErrorComponent } from '@tanstack/react-router' import { fetchPost } from '../../posts' import type { ErrorComponentProps } from '@tanstack/react-router' export const Route = createFileRoute({ loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, notFoundComponent: () => { return

Post not found

}, component: PostComponent, }) export function PostErrorComponent({ error }: ErrorComponentProps) { return } function PostComponent() { const post = Route.useLoaderData() return (

{post.title}

{post.body}
) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts/home.tsx ================================================ import * as React from 'react' export const Route = createFileRoute({ component: PostsIndexComponent, }) function PostsIndexComponent() { return
Select a post.
} ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts/lets-go/deeper/__virtual.ts ================================================ import { defineVirtualSubtreeConfig, index, } from '@tanstack/virtual-file-routes' export default defineVirtualSubtreeConfig([index('home.tsx')]) ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts/lets-go/deeper/home.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /posts/inception/deeper/!
, }) ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts/lets-go/index.tsx ================================================ export const Route = createFileRoute({ component: () =>
Hello /posts/inception/!
, }) ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/routes/posts.tsx ================================================ import * as React from 'react' import { Link, Outlet } from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute({ loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = Route.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
  • ) }, )}

) } ================================================ FILE: examples/react/basic-virtual-inside-file-based/src/styles.css ================================================ @import 'tailwindcss' source('../'); @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } body { @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; } ================================================ FILE: examples/react/basic-virtual-inside-file-based/tsconfig.json ================================================ { "compilerOptions": { "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "target": "ESNext", "module": "Preserve", "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "skipLibCheck": true } } ================================================ FILE: examples/react/basic-virtual-inside-file-based/vite.config.ts ================================================ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: 'react', autoCodeSplitting: true, verboseFileRoutes: false, }), react(), ], }) ================================================ FILE: examples/react/deferred-data/.devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/typescript-node:24" } ================================================ FILE: examples/react/deferred-data/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local ================================================ FILE: examples/react/deferred-data/.vscode/settings.json ================================================ { "files.watcherExclude": { "**/routeTree.gen.ts": true }, "search.exclude": { "**/routeTree.gen.ts": true }, "files.readonlyInclude": { "**/routeTree.gen.ts": true } } ================================================ FILE: examples/react/deferred-data/README.md ================================================ # TanStack Router - Deferred Data Example An example demonstrating deferred data loading. - [TanStack Router Docs](https://tanstack.com/router) ## Start a new project based on this example To start a new project based on this example, run: ```sh npx gitpick TanStack/router/tree/main/examples/react/deferred-data deferred-data ``` ## Getting Started Install dependencies: ```sh pnpm install ``` Start the development server: ```sh pnpm dev ``` ## Build Build for production: ```sh pnpm build ``` ## About This Example This example demonstrates: - Deferred data loading - Progressive enhancement - Streaming data - Suspense boundaries - Optimized loading states ================================================ FILE: examples/react/deferred-data/index.html ================================================ Vite App
================================================ FILE: examples/react/deferred-data/package.json ================================================ { "name": "tanstack-router-react-example-deferred-data", "private": true, "type": "module", "scripts": { "dev": "vite --port 3000", "build": "vite build && tsc --noEmit", "preview": "vite preview", "start": "vite" }, "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.168.1", "@tanstack/react-router-devtools": "^1.166.10", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "zod": "^3.24.2" }, "devDependencies": { "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", "typescript": "^5.7.2", "vite": "^8.0.0" } } ================================================ FILE: examples/react/deferred-data/src/main.tsx ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import { Await, ErrorComponent, Link, MatchRoute, Outlet, RouterProvider, createRootRoute, createRoute, createRouter, defer, } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' import axios from 'redaxios' import type { ErrorComponentProps } from '@tanstack/react-router' import './styles.css' type PostType = { id: string title: string body: string } type CommentType = { id: string postId: string name: string email: string body: string } const fetchPosts = async () => { console.info('Fetching posts...') await new Promise((r) => setTimeout(r, 100)) return axios .get>('https://jsonplaceholder.typicode.com/posts') .then((r) => r.data.slice(0, 10)) } const fetchPost = async (postId: string) => { console.info(`Fetching post with id ${postId}...`) const commentsPromise = new Promise((r) => setTimeout(r, 2000)) .then(() => axios.get>( `https://jsonplaceholder.typicode.com/comments?postId=${postId}`, ), ) .then((r) => r.data) const post = await new Promise((r) => setTimeout(r, 1000)) .then(() => axios.get( `https://jsonplaceholder.typicode.com/posts/${postId}`, ), ) .catch((err) => { if (err.status === 404) { throw new NotFoundError(`Post with id "${postId}" not found!`) } throw err }) .then((r) => r.data) return { post, commentsPromise: defer(commentsPromise), } } function Spinner({ show, wait }: { show?: boolean; wait?: `delay-${number}` }) { return (
) } const rootRoute = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( <>
Home {' '} Posts

{/* Start rendering router matches */} ) } const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', }).update({ component: IndexComponent, }) function IndexComponent() { return (

Welcome Home!

) } const postsRoute = createRoute({ getParentRoute: () => rootRoute, path: 'posts', loader: fetchPosts, component: PostsComponent, }) function PostsComponent() { const posts = postsRoute.useLoaderData() return (
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { return (
  • {post.title.substring(0, 20)}
    {(match) => { return }}
  • ) }, )}

) } class NotFoundError extends Error {} const postRoute = createRoute({ getParentRoute: () => postsRoute, path: '$postId', loader: async ({ params: { postId } }) => fetchPost(postId), errorComponent: PostErrorComponent, component: PostComponent, }) function PostErrorComponent({ error }: ErrorComponentProps) { if (error instanceof NotFoundError) { return
{error.message}
} return } function PostComponent() { const { post, commentsPromise } = postRoute.useLoaderData() return (

{post.title}

{post.body}
Loading comments...
} key={post.id} > {(comments) => { return (
Comments
{comments.map((comment) => { return (
{comment.name}
{comment.email}
{comment.body}
) })}
) }}