diff --git a/e2e/solid-start/basic-solid-query/.gitignore b/e2e/solid-start/basic-solid-query/.gitignore
new file mode 100644
index 0000000000..be342025da
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/.gitignore
@@ -0,0 +1,22 @@
+node_modules
+package-lock.json
+yarn.lock
+
+.DS_Store
+.cache
+.env
+.vercel
+.output
+.vinxi
+
+/build/
+/api/
+/server/build
+/public/build
+.vinxi
+# Sentry Config File
+.env.sentry-build-plugin
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/e2e/solid-start/basic-solid-query/.prettierignore b/e2e/solid-start/basic-solid-query/.prettierignore
new file mode 100644
index 0000000000..2be5eaa6ec
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/.prettierignore
@@ -0,0 +1,4 @@
+**/build
+**/public
+pnpm-lock.yaml
+routeTree.gen.ts
\ No newline at end of file
diff --git a/e2e/solid-start/basic-solid-query/app.config.ts b/e2e/solid-start/basic-solid-query/app.config.ts
new file mode 100644
index 0000000000..2a06e3d3f0
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/app.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@tanstack/solid-start/config'
+import tsConfigPaths from 'vite-tsconfig-paths'
+
+export default defineConfig({
+ tsr: {
+ appDirectory: 'src',
+ },
+ vite: {
+ plugins: [
+ tsConfigPaths({
+ projects: ['./tsconfig.json'],
+ }),
+ ],
+ },
+})
diff --git a/e2e/solid-start/basic-solid-query/package.json b/e2e/solid-start/basic-solid-query/package.json
new file mode 100644
index 0000000000..9072a0833b
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "tanstack-solid-start-e2e-basic-solid-query",
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "scripts": {
+ "dev": "vinxi dev --port 3000",
+ "dev:e2e": "vinxi dev",
+ "build": "vinxi build && tsc --noEmit",
+ "start": "vinxi start",
+ "test:e2e": "playwright test --project=chromium"
+ },
+ "dependencies": {
+ "@tanstack/solid-query": "^5.66.0",
+ "@tanstack/solid-query-devtools": "^5.66.0",
+ "@tanstack/solid-router": "workspace:^",
+ "@tanstack/solid-router-with-query": "workspace:^",
+ "@tanstack/solid-router-devtools": "workspace:^",
+ "@tanstack/solid-start": "workspace:^",
+ "solid-js": "^1.9.5",
+ "redaxios": "^0.5.1",
+ "tailwind-merge": "^2.6.0",
+ "vinxi": "0.5.3"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.50.1",
+ "@tanstack/router-e2e-utils": "workspace:^",
+ "@types/node": "^22.10.2",
+ "@vitejs/plugin-react": "^4.3.4",
+ "postcss": "^8.5.1",
+ "autoprefixer": "^10.4.20",
+ "tailwindcss": "^3.4.17",
+ "typescript": "^5.7.2",
+ "vite-tsconfig-paths": "^5.1.4"
+ }
+}
diff --git a/e2e/solid-start/basic-solid-query/playwright.config.ts b/e2e/solid-start/basic-solid-query/playwright.config.ts
new file mode 100644
index 0000000000..bb77d0cf70
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/playwright.config.ts
@@ -0,0 +1,34 @@
+import { defineConfig, devices } from '@playwright/test'
+import { derivePort } from '@tanstack/router-e2e-utils'
+import packageJson from './package.json' with { type: 'json' }
+
+const PORT = derivePort(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 start --port ${PORT}`,
+ url: baseURL,
+ reuseExistingServer: !process.env.CI,
+ stdout: 'pipe',
+ },
+
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ ],
+})
diff --git a/e2e/solid-start/basic-solid-query/postcss.config.mjs b/e2e/solid-start/basic-solid-query/postcss.config.mjs
new file mode 100644
index 0000000000..2e7af2b7f1
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/postcss.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/e2e/solid-start/basic-solid-query/public/android-chrome-192x192.png b/e2e/solid-start/basic-solid-query/public/android-chrome-192x192.png
new file mode 100644
index 0000000000..09c8324f8c
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/android-chrome-192x192.png differ
diff --git a/e2e/solid-start/basic-solid-query/public/android-chrome-512x512.png b/e2e/solid-start/basic-solid-query/public/android-chrome-512x512.png
new file mode 100644
index 0000000000..11d626ea3d
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/android-chrome-512x512.png differ
diff --git a/e2e/solid-start/basic-solid-query/public/apple-touch-icon.png b/e2e/solid-start/basic-solid-query/public/apple-touch-icon.png
new file mode 100644
index 0000000000..5a9423cc02
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/apple-touch-icon.png differ
diff --git a/e2e/solid-start/basic-solid-query/public/favicon-16x16.png b/e2e/solid-start/basic-solid-query/public/favicon-16x16.png
new file mode 100644
index 0000000000..e3389b0044
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/favicon-16x16.png differ
diff --git a/e2e/solid-start/basic-solid-query/public/favicon-32x32.png b/e2e/solid-start/basic-solid-query/public/favicon-32x32.png
new file mode 100644
index 0000000000..900c77d444
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/favicon-32x32.png differ
diff --git a/e2e/solid-start/basic-solid-query/public/favicon.ico b/e2e/solid-start/basic-solid-query/public/favicon.ico
new file mode 100644
index 0000000000..1a1751676f
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/favicon.ico differ
diff --git a/e2e/solid-start/basic-solid-query/public/favicon.png b/e2e/solid-start/basic-solid-query/public/favicon.png
new file mode 100644
index 0000000000..1e77bc0609
Binary files /dev/null and b/e2e/solid-start/basic-solid-query/public/favicon.png differ
diff --git a/e2e/solid-start/basic-solid-query/public/site.webmanifest b/e2e/solid-start/basic-solid-query/public/site.webmanifest
new file mode 100644
index 0000000000..fa99de77db
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/public/site.webmanifest
@@ -0,0 +1,19 @@
+{
+ "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"
+}
diff --git a/e2e/solid-start/basic-solid-query/src/api.ts b/e2e/solid-start/basic-solid-query/src/api.ts
new file mode 100644
index 0000000000..ed511bcd26
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/api.ts
@@ -0,0 +1,6 @@
+import {
+ createStartAPIHandler,
+ defaultAPIFileRouteHandler,
+} from '@tanstack/solid-start/api'
+
+export default createStartAPIHandler(defaultAPIFileRouteHandler)
diff --git a/e2e/solid-start/basic-solid-query/src/client.tsx b/e2e/solid-start/basic-solid-query/src/client.tsx
new file mode 100644
index 0000000000..071dbdf4ad
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/client.tsx
@@ -0,0 +1,8 @@
+///
+import { hydrate } from 'solid-js/web'
+import { StartClient } from '@tanstack/solid-start'
+import { createRouter } from './router'
+
+const router = createRouter()
+
+hydrate(() => , document!)
diff --git a/e2e/solid-start/basic-solid-query/src/components/DefaultCatchBoundary.tsx b/e2e/solid-start/basic-solid-query/src/components/DefaultCatchBoundary.tsx
new file mode 100644
index 0000000000..32aed20e67
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/components/DefaultCatchBoundary.tsx
@@ -0,0 +1,53 @@
+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
+
+ )}
+
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/components/NotFound.tsx b/e2e/solid-start/basic-solid-query/src/components/NotFound.tsx
new file mode 100644
index 0000000000..ca4c1960fa
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/components/NotFound.tsx
@@ -0,0 +1,25 @@
+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
+
+
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts b/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts
new file mode 100644
index 0000000000..55b94eb5b5
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts
@@ -0,0 +1,470 @@
+/* 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 Routes
+
+import { Route as rootRoute } from './routes/__root'
+import { Route as UsersImport } from './routes/users'
+import { Route as RedirectImport } from './routes/redirect'
+import { Route as PostsImport } from './routes/posts'
+import { Route as DeferredImport } from './routes/deferred'
+import { Route as LayoutImport } from './routes/_layout'
+import { Route as IndexImport } from './routes/index'
+import { Route as UsersIndexImport } from './routes/users.index'
+import { Route as PostsIndexImport } from './routes/posts.index'
+import { Route as UsersUserIdImport } from './routes/users.$userId'
+import { Route as PostsPostIdImport } from './routes/posts.$postId'
+import { Route as LayoutLayout2Import } from './routes/_layout/_layout-2'
+import { Route as PostsPostIdDeepImport } from './routes/posts_.$postId.deep'
+import { Route as LayoutLayout2LayoutBImport } from './routes/_layout/_layout-2/layout-b'
+import { Route as LayoutLayout2LayoutAImport } from './routes/_layout/_layout-2/layout-a'
+
+// Create/Update Routes
+
+const UsersRoute = UsersImport.update({
+ id: '/users',
+ path: '/users',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const RedirectRoute = RedirectImport.update({
+ id: '/redirect',
+ path: '/redirect',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const PostsRoute = PostsImport.update({
+ id: '/posts',
+ path: '/posts',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const DeferredRoute = DeferredImport.update({
+ id: '/deferred',
+ path: '/deferred',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const LayoutRoute = LayoutImport.update({
+ id: '/_layout',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const IndexRoute = IndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const UsersIndexRoute = UsersIndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => UsersRoute,
+} as any)
+
+const PostsIndexRoute = PostsIndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => PostsRoute,
+} as any)
+
+const UsersUserIdRoute = UsersUserIdImport.update({
+ id: '/$userId',
+ path: '/$userId',
+ getParentRoute: () => UsersRoute,
+} as any)
+
+const PostsPostIdRoute = PostsPostIdImport.update({
+ id: '/$postId',
+ path: '/$postId',
+ getParentRoute: () => PostsRoute,
+} as any)
+
+const LayoutLayout2Route = LayoutLayout2Import.update({
+ id: '/_layout-2',
+ getParentRoute: () => LayoutRoute,
+} as any)
+
+const PostsPostIdDeepRoute = PostsPostIdDeepImport.update({
+ id: '/posts_/$postId/deep',
+ path: '/posts/$postId/deep',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBImport.update({
+ id: '/layout-b',
+ path: '/layout-b',
+ getParentRoute: () => LayoutLayout2Route,
+} as any)
+
+const LayoutLayout2LayoutARoute = LayoutLayout2LayoutAImport.update({
+ id: '/layout-a',
+ path: '/layout-a',
+ getParentRoute: () => LayoutLayout2Route,
+} as any)
+
+// Populate the FileRoutesByPath interface
+
+declare module '@tanstack/solid-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexImport
+ parentRoute: typeof rootRoute
+ }
+ '/_layout': {
+ id: '/_layout'
+ path: ''
+ fullPath: ''
+ preLoaderRoute: typeof LayoutImport
+ parentRoute: typeof rootRoute
+ }
+ '/deferred': {
+ id: '/deferred'
+ path: '/deferred'
+ fullPath: '/deferred'
+ preLoaderRoute: typeof DeferredImport
+ parentRoute: typeof rootRoute
+ }
+ '/posts': {
+ id: '/posts'
+ path: '/posts'
+ fullPath: '/posts'
+ preLoaderRoute: typeof PostsImport
+ parentRoute: typeof rootRoute
+ }
+ '/redirect': {
+ id: '/redirect'
+ path: '/redirect'
+ fullPath: '/redirect'
+ preLoaderRoute: typeof RedirectImport
+ parentRoute: typeof rootRoute
+ }
+ '/users': {
+ id: '/users'
+ path: '/users'
+ fullPath: '/users'
+ preLoaderRoute: typeof UsersImport
+ parentRoute: typeof rootRoute
+ }
+ '/_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
+ }
+ '/users/$userId': {
+ id: '/users/$userId'
+ path: '/$userId'
+ fullPath: '/users/$userId'
+ preLoaderRoute: typeof UsersUserIdImport
+ parentRoute: typeof UsersImport
+ }
+ '/posts/': {
+ id: '/posts/'
+ path: '/'
+ fullPath: '/posts/'
+ preLoaderRoute: typeof PostsIndexImport
+ parentRoute: typeof PostsImport
+ }
+ '/users/': {
+ id: '/users/'
+ path: '/'
+ fullPath: '/users/'
+ preLoaderRoute: typeof UsersIndexImport
+ parentRoute: typeof UsersImport
+ }
+ '/_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
+ }
+ '/posts_/$postId/deep': {
+ id: '/posts_/$postId/deep'
+ path: '/posts/$postId/deep'
+ fullPath: '/posts/$postId/deep'
+ preLoaderRoute: typeof PostsPostIdDeepImport
+ parentRoute: typeof rootRoute
+ }
+ }
+}
+
+// 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 UsersRouteChildren {
+ UsersUserIdRoute: typeof UsersUserIdRoute
+ UsersIndexRoute: typeof UsersIndexRoute
+}
+
+const UsersRouteChildren: UsersRouteChildren = {
+ UsersUserIdRoute: UsersUserIdRoute,
+ UsersIndexRoute: UsersIndexRoute,
+}
+
+const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren)
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '': typeof LayoutLayout2RouteWithChildren
+ '/deferred': typeof DeferredRoute
+ '/posts': typeof PostsRouteWithChildren
+ '/redirect': typeof RedirectRoute
+ '/users': typeof UsersRouteWithChildren
+ '/posts/$postId': typeof PostsPostIdRoute
+ '/users/$userId': typeof UsersUserIdRoute
+ '/posts/': typeof PostsIndexRoute
+ '/users/': typeof UsersIndexRoute
+ '/layout-a': typeof LayoutLayout2LayoutARoute
+ '/layout-b': typeof LayoutLayout2LayoutBRoute
+ '/posts/$postId/deep': typeof PostsPostIdDeepRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '': typeof LayoutLayout2RouteWithChildren
+ '/deferred': typeof DeferredRoute
+ '/redirect': typeof RedirectRoute
+ '/posts/$postId': typeof PostsPostIdRoute
+ '/users/$userId': typeof UsersUserIdRoute
+ '/posts': typeof PostsIndexRoute
+ '/users': typeof UsersIndexRoute
+ '/layout-a': typeof LayoutLayout2LayoutARoute
+ '/layout-b': typeof LayoutLayout2LayoutBRoute
+ '/posts/$postId/deep': typeof PostsPostIdDeepRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+ '/_layout': typeof LayoutRouteWithChildren
+ '/deferred': typeof DeferredRoute
+ '/posts': typeof PostsRouteWithChildren
+ '/redirect': typeof RedirectRoute
+ '/users': typeof UsersRouteWithChildren
+ '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren
+ '/posts/$postId': typeof PostsPostIdRoute
+ '/users/$userId': typeof UsersUserIdRoute
+ '/posts/': typeof PostsIndexRoute
+ '/users/': typeof UsersIndexRoute
+ '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute
+ '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute
+ '/posts_/$postId/deep': typeof PostsPostIdDeepRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths:
+ | '/'
+ | ''
+ | '/deferred'
+ | '/posts'
+ | '/redirect'
+ | '/users'
+ | '/posts/$postId'
+ | '/users/$userId'
+ | '/posts/'
+ | '/users/'
+ | '/layout-a'
+ | '/layout-b'
+ | '/posts/$postId/deep'
+ fileRoutesByTo: FileRoutesByTo
+ to:
+ | '/'
+ | ''
+ | '/deferred'
+ | '/redirect'
+ | '/posts/$postId'
+ | '/users/$userId'
+ | '/posts'
+ | '/users'
+ | '/layout-a'
+ | '/layout-b'
+ | '/posts/$postId/deep'
+ id:
+ | '__root__'
+ | '/'
+ | '/_layout'
+ | '/deferred'
+ | '/posts'
+ | '/redirect'
+ | '/users'
+ | '/_layout/_layout-2'
+ | '/posts/$postId'
+ | '/users/$userId'
+ | '/posts/'
+ | '/users/'
+ | '/_layout/_layout-2/layout-a'
+ | '/_layout/_layout-2/layout-b'
+ | '/posts_/$postId/deep'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ LayoutRoute: typeof LayoutRouteWithChildren
+ DeferredRoute: typeof DeferredRoute
+ PostsRoute: typeof PostsRouteWithChildren
+ RedirectRoute: typeof RedirectRoute
+ UsersRoute: typeof UsersRouteWithChildren
+ PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ LayoutRoute: LayoutRouteWithChildren,
+ DeferredRoute: DeferredRoute,
+ PostsRoute: PostsRouteWithChildren,
+ RedirectRoute: RedirectRoute,
+ UsersRoute: UsersRouteWithChildren,
+ PostsPostIdDeepRoute: PostsPostIdDeepRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/_layout",
+ "/deferred",
+ "/posts",
+ "/redirect",
+ "/users",
+ "/posts_/$postId/deep"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/_layout": {
+ "filePath": "_layout.tsx",
+ "children": [
+ "/_layout/_layout-2"
+ ]
+ },
+ "/deferred": {
+ "filePath": "deferred.tsx"
+ },
+ "/posts": {
+ "filePath": "posts.tsx",
+ "children": [
+ "/posts/$postId",
+ "/posts/"
+ ]
+ },
+ "/redirect": {
+ "filePath": "redirect.tsx"
+ },
+ "/users": {
+ "filePath": "users.tsx",
+ "children": [
+ "/users/$userId",
+ "/users/"
+ ]
+ },
+ "/_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"
+ },
+ "/users/$userId": {
+ "filePath": "users.$userId.tsx",
+ "parent": "/users"
+ },
+ "/posts/": {
+ "filePath": "posts.index.tsx",
+ "parent": "/posts"
+ },
+ "/users/": {
+ "filePath": "users.index.tsx",
+ "parent": "/users"
+ },
+ "/_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"
+ },
+ "/posts_/$postId/deep": {
+ "filePath": "posts_.$postId.deep.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/e2e/solid-start/basic-solid-query/src/router.tsx b/e2e/solid-start/basic-solid-query/src/router.tsx
new file mode 100644
index 0000000000..684b6a60b0
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/router.tsx
@@ -0,0 +1,32 @@
+import { QueryClient } from '@tanstack/solid-query'
+import { createRouter as createTanStackRouter } from '@tanstack/solid-router'
+import { routerWithQueryClient } from '@tanstack/solid-router-with-query'
+import { routeTree } from './routeTree.gen'
+import { DefaultCatchBoundary } from './components/DefaultCatchBoundary'
+import { NotFound } from './components/NotFound'
+
+// NOTE: Most of the integration code found here is experimental and will
+// definitely end up in a more streamlined API in the future. This is just
+// to show what's possible with the current APIs.
+
+export function createRouter() {
+ const queryClient = new QueryClient()
+
+ return routerWithQueryClient(
+ createTanStackRouter({
+ routeTree,
+ context: { queryClient },
+ scrollRestoration: true,
+ defaultPreload: 'intent',
+ defaultErrorComponent: DefaultCatchBoundary,
+ defaultNotFoundComponent: () => ,
+ }),
+ queryClient,
+ )
+}
+
+declare module '@tanstack/solid-router' {
+ interface Register {
+ router: ReturnType
+ }
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/__root.tsx b/e2e/solid-start/basic-solid-query/src/routes/__root.tsx
new file mode 100644
index 0000000000..857ecea572
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/__root.tsx
@@ -0,0 +1,141 @@
+import {
+ HeadContent,
+ Link,
+ Outlet,
+ Scripts,
+ createRootRouteWithContext,
+} from '@tanstack/solid-router'
+import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
+import { TanStackRouterDevtoolsInProd } from '@tanstack/solid-router-devtools'
+import * as React from 'react'
+import type { QueryClient } from '@tanstack/solid-query'
+import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'
+import { NotFound } from '~/components/NotFound'
+import appCss from '~/styles/app.css?url'
+import { seo } from '~/utils/seo'
+import * as Solid from 'solid-js'
+
+export const Route = createRootRouteWithContext<{
+ queryClient: QueryClient
+}>()({
+ 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 (
+
+
+
+ )
+ },
+ notFoundComponent: () => ,
+ component: RootComponent,
+})
+
+function RootComponent() {
+ return (
+
+
+
+ )
+}
+
+function RootDocument({ children }: { children: Solid.JSX.Element }) {
+ return (
+ <>
+
+
+
+ Home
+ {' '}
+
+ Posts
+ {' '}
+
+ Users
+ {' '}
+
+ Layout
+ {' '}
+
+ Deferred
+ {' '}
+
+ This Route Does Not Exist
+
+
+
+ {children}
+
+
+
+ >
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/_layout.tsx b/e2e/solid-start/basic-solid-query/src/routes/_layout.tsx
new file mode 100644
index 0000000000..6cda4a6954
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/_layout.tsx
@@ -0,0 +1,16 @@
+import { Outlet, createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_layout')({
+ component: LayoutComponent,
+})
+
+function LayoutComponent() {
+ return (
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2.tsx b/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2.tsx
new file mode 100644
index 0000000000..a0c65e6fe7
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2.tsx
@@ -0,0 +1,34 @@
+import { Link, Outlet, createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_layout/_layout-2')({
+ component: LayoutComponent,
+})
+
+function LayoutComponent() {
+ return (
+
+
I'm a nested layout
+
+
+ Layout A
+
+
+ Layout B
+
+
+
+
+
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2/layout-a.tsx b/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2/layout-a.tsx
new file mode 100644
index 0000000000..139c891946
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2/layout-a.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_layout/_layout-2/layout-a')({
+ component: LayoutAComponent,
+})
+
+function LayoutAComponent() {
+ return I'm A!
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2/layout-b.tsx b/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2/layout-b.tsx
new file mode 100644
index 0000000000..c9b5d7fffd
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/_layout/_layout-2/layout-b.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_layout/_layout-2/layout-b')({
+ component: LayoutBComponent,
+})
+
+function LayoutBComponent() {
+ return I'm B!
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/api.users.ts b/e2e/solid-start/basic-solid-query/src/routes/api.users.ts
new file mode 100644
index 0000000000..f37bf0db1b
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/api.users.ts
@@ -0,0 +1,17 @@
+import { json } from '@tanstack/solid-start'
+import { createAPIFileRoute } from '@tanstack/solid-start/api'
+import axios from 'redaxios'
+import type { User } from '../utils/users'
+
+export const APIRoute = createAPIFileRoute('/api/users')({
+ GET: async ({ request }) => {
+ console.info('Fetching users... @', request.url)
+ const res = await axios.get>(
+ 'https://jsonplaceholder.typicode.com/users',
+ )
+
+ const list = res.data.slice(0, 10)
+
+ return json(list.map((u) => ({ id: u.id, name: u.name, email: u.email })))
+ },
+})
diff --git a/e2e/solid-start/basic-solid-query/src/routes/api/users.$id.ts b/e2e/solid-start/basic-solid-query/src/routes/api/users.$id.ts
new file mode 100644
index 0000000000..b1786f6a30
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/api/users.$id.ts
@@ -0,0 +1,24 @@
+import { json } from '@tanstack/solid-start'
+import { createAPIFileRoute } from '@tanstack/solid-start/api'
+import axios from 'redaxios'
+import type { User } from '../../utils/users'
+
+export const APIRoute = createAPIFileRoute('/api/users/$id')({
+ GET: async ({ request, params }) => {
+ console.info(`Fetching users by id=${params.id}... @`, request.url)
+ try {
+ const res = await axios.get(
+ 'https://jsonplaceholder.typicode.com/users/' + params.id,
+ )
+
+ return json({
+ id: res.data.id,
+ name: res.data.name,
+ email: res.data.email,
+ })
+ } catch (e) {
+ console.error(e)
+ return json({ error: 'User not found' }, { status: 404 })
+ }
+ },
+})
diff --git a/e2e/solid-start/basic-solid-query/src/routes/deferred.tsx b/e2e/solid-start/basic-solid-query/src/routes/deferred.tsx
new file mode 100644
index 0000000000..7ce060840f
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/deferred.tsx
@@ -0,0 +1,53 @@
+import { queryOptions, createQuery } from '@tanstack/solid-query'
+import { createFileRoute } from '@tanstack/solid-router'
+import { Suspense, createSignal } from 'solid-js'
+
+const deferredQueryOptions = () =>
+ queryOptions({
+ queryKey: ['deferred'],
+ queryFn: async () => {
+ await new Promise((r) => setTimeout(r, 3000))
+ return {
+ message: `Hello deferred from the server!`,
+ status: 'success',
+ time: new Date(),
+ }
+ },
+ })
+
+export const Route = createFileRoute('/deferred')({
+ loader: ({ context }) => {
+ // Kick off loading as early as possible!
+ context.queryClient.prefetchQuery(deferredQueryOptions())
+ },
+ component: Deferred,
+})
+
+function Deferred() {
+ const [count, setCount] = createSignal(0)
+
+ return (
+
+
+
+
+
Count: {count()}
+
+
+
+
+ )
+}
+
+function DeferredQuery() {
+ const deferredQuery = createQuery(() => deferredQueryOptions())
+
+ return (
+
+
Deferred Query
+
Status: {deferredQuery.data?.status}
+
Message: {deferredQuery.data?.message}
+
Time: {deferredQuery.data?.time.toISOString()}
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/index.tsx b/e2e/solid-start/basic-solid-query/src/routes/index.tsx
new file mode 100644
index 0000000000..a128aeca0e
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/index.tsx
@@ -0,0 +1,13 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/')({
+ component: Home,
+})
+
+function Home() {
+ return (
+
+
Welcome Home!!!
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/posts.$postId.tsx b/e2e/solid-start/basic-solid-query/src/routes/posts.$postId.tsx
new file mode 100644
index 0000000000..331775f0d4
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/posts.$postId.tsx
@@ -0,0 +1,52 @@
+import { ErrorComponent, Link, createFileRoute } from '@tanstack/solid-router'
+import { createQuery } from '@tanstack/solid-query'
+import type { ErrorComponentProps } from '@tanstack/solid-router'
+
+import { postQueryOptions } from '~/utils/posts'
+import { NotFound } from '~/components/NotFound'
+
+export const Route = createFileRoute('/posts/$postId')({
+ loader: async ({ params: { postId }, context }) => {
+ const data = await context.queryClient.ensureQueryData(
+ postQueryOptions(postId),
+ )
+
+ return {
+ title: data.title,
+ }
+ },
+ head: ({ loaderData }) => ({
+ meta: loaderData ? [{ title: loaderData.title }] : undefined,
+ }),
+ errorComponent: PostErrorComponent,
+ notFoundComponent: () => {
+ return Post not found
+ },
+ component: PostComponent,
+})
+
+export function PostErrorComponent({ error }: ErrorComponentProps) {
+ return
+}
+
+function PostComponent() {
+ const params = Route.useParams()
+ const postQuery = createQuery(() => postQueryOptions(params().postId))
+
+ return (
+
+
{postQuery.data?.title}
+
{postQuery.data?.body}
+
+ Deep View
+
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/posts.index.tsx b/e2e/solid-start/basic-solid-query/src/routes/posts.index.tsx
new file mode 100644
index 0000000000..33d0386c19
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/posts.index.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/posts/')({
+ component: PostsIndexComponent,
+})
+
+function PostsIndexComponent() {
+ return Select a post.
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/posts.tsx b/e2e/solid-start/basic-solid-query/src/routes/posts.tsx
new file mode 100644
index 0000000000..3c61a1a929
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/posts.tsx
@@ -0,0 +1,44 @@
+import { createQuery } from '@tanstack/solid-query'
+import { Link, Outlet, createFileRoute } from '@tanstack/solid-router'
+
+import { postsQueryOptions } from '~/utils/posts'
+
+export const Route = createFileRoute('/posts')({
+ loader: async ({ context }) => {
+ await context.queryClient.ensureQueryData(postsQueryOptions())
+ },
+ head: () => ({ meta: [{ title: 'Posts' }] }),
+ component: PostsComponent,
+})
+
+function PostsComponent() {
+ const postsQuery = createQuery(() => postsQueryOptions())
+
+ return (
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/posts_.$postId.deep.tsx b/e2e/solid-start/basic-solid-query/src/routes/posts_.$postId.deep.tsx
new file mode 100644
index 0000000000..0d84e1f2d7
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/posts_.$postId.deep.tsx
@@ -0,0 +1,36 @@
+import { Link, createFileRoute } from '@tanstack/solid-router'
+import { createQuery } from '@tanstack/solid-query'
+import { postQueryOptions } from '../utils/posts'
+import { PostErrorComponent } from './posts.$postId'
+
+export const Route = createFileRoute('/posts_/$postId/deep')({
+ loader: async ({ params: { postId }, context }) => {
+ const data = await context.queryClient.ensureQueryData(
+ postQueryOptions(postId),
+ )
+
+ return {
+ title: data.title,
+ }
+ },
+ head: ({ loaderData }) => ({
+ meta: loaderData ? [{ title: loaderData.title }] : undefined,
+ }),
+ errorComponent: PostErrorComponent,
+ component: PostDeepComponent,
+})
+
+function PostDeepComponent() {
+ const params = Route.useParams()
+ const postQuery = createQuery(() => postQueryOptions(params().postId))
+
+ return (
+
+
+ ← All Posts
+
+
{postQuery.data?.title}
+
{postQuery.data?.body}
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/redirect.tsx b/e2e/solid-start/basic-solid-query/src/routes/redirect.tsx
new file mode 100644
index 0000000000..ca017f0635
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/redirect.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute, redirect } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/redirect')({
+ beforeLoad: async () => {
+ throw redirect({
+ to: '/posts',
+ })
+ },
+})
diff --git a/e2e/solid-start/basic-solid-query/src/routes/users.$userId.tsx b/e2e/solid-start/basic-solid-query/src/routes/users.$userId.tsx
new file mode 100644
index 0000000000..fe9f58faf3
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/users.$userId.tsx
@@ -0,0 +1,34 @@
+import { createQuery } 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
+ },
+})
+
+export function UserErrorComponent({ error }: ErrorComponentProps) {
+ return
+}
+
+function UserComponent() {
+ const params = Route.useParams()
+ const userQuery = createQuery(() => userQueryOptions(params().userId))
+ const user = userQuery.data
+
+ return (
+
+
{user?.name}
+
{user?.email}
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/users.index.tsx b/e2e/solid-start/basic-solid-query/src/routes/users.index.tsx
new file mode 100644
index 0000000000..bbc96801a9
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/users.index.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/users/')({
+ component: UsersIndexComponent,
+})
+
+function UsersIndexComponent() {
+ return Select a user.
+}
diff --git a/e2e/solid-start/basic-solid-query/src/routes/users.tsx b/e2e/solid-start/basic-solid-query/src/routes/users.tsx
new file mode 100644
index 0000000000..92f595b171
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/routes/users.tsx
@@ -0,0 +1,43 @@
+import { createQuery } from '@tanstack/solid-query'
+import { Link, Outlet, createFileRoute } from '@tanstack/solid-router'
+
+import { usersQueryOptions } from '~/utils/users'
+
+export const Route = createFileRoute('/users')({
+ loader: async ({ context }) => {
+ await context.queryClient.ensureQueryData(usersQueryOptions())
+ },
+ component: UsersComponent,
+})
+
+function UsersComponent() {
+ const usersQuery = createQuery(() => usersQueryOptions())
+
+ return (
+
+
+ {[
+ ...usersQuery.data!,
+ { id: 'i-do-not-exist', name: 'Non-existent User', email: '' },
+ ].map((user) => {
+ return (
+ -
+
+
{user.name}
+
+
+ )
+ })}
+
+
+
+
+ )
+}
diff --git a/e2e/solid-start/basic-solid-query/src/ssr.tsx b/e2e/solid-start/basic-solid-query/src/ssr.tsx
new file mode 100644
index 0000000000..6d10bea05f
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/ssr.tsx
@@ -0,0 +1,12 @@
+import {
+ createStartHandler,
+ defaultStreamHandler,
+} from '@tanstack/solid-start/server'
+import { getRouterManifest } from '@tanstack/solid-start/router-manifest'
+
+import { createRouter } from './router'
+
+export default createStartHandler({
+ createRouter,
+ getRouterManifest,
+})(defaultStreamHandler)
diff --git a/e2e/solid-start/basic-solid-query/src/styles/app.css b/e2e/solid-start/basic-solid-query/src/styles/app.css
new file mode 100644
index 0000000000..c53c870665
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/styles/app.css
@@ -0,0 +1,22 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@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;
+ }
+}
diff --git a/e2e/solid-start/basic-solid-query/src/utils/posts.tsx b/e2e/solid-start/basic-solid-query/src/utils/posts.tsx
new file mode 100644
index 0000000000..d3ab71d525
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/utils/posts.tsx
@@ -0,0 +1,49 @@
+import { queryOptions } from '@tanstack/solid-query'
+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
+}
+
+export const fetchPosts = createServerFn({ method: 'GET' }).handler(
+ async () => {
+ console.info('Fetching posts...')
+ return axios
+ .get>('https://jsonplaceholder.typicode.com/posts')
+ .then((r) => r.data.slice(0, 10))
+ },
+)
+
+export const postsQueryOptions = () =>
+ queryOptions({
+ queryKey: ['posts'],
+ queryFn: () => fetchPosts(),
+ })
+
+export const fetchPost = createServerFn({ method: 'GET' })
+ .validator((postId: string) => postId)
+ .handler(async ({ data: postId }) => {
+ console.info(`Fetching post with id ${postId}...`)
+ const post = await axios
+ .get(`https://jsonplaceholder.typicode.com/posts/${postId}`)
+ .then((r) => r.data)
+ .catch((err) => {
+ console.error(err)
+ if (err.status === 404) {
+ throw notFound()
+ }
+ throw err
+ })
+
+ return post
+ })
+
+export const postQueryOptions = (postId: string) =>
+ queryOptions({
+ queryKey: ['post', postId],
+ queryFn: () => fetchPost({ data: postId }),
+ })
diff --git a/e2e/solid-start/basic-solid-query/src/utils/seo.ts b/e2e/solid-start/basic-solid-query/src/utils/seo.ts
new file mode 100644
index 0000000000..d18ad84b74
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/utils/seo.ts
@@ -0,0 +1,33 @@
+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
+}
diff --git a/e2e/solid-start/basic-solid-query/src/utils/users.tsx b/e2e/solid-start/basic-solid-query/src/utils/users.tsx
new file mode 100644
index 0000000000..52d659b001
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/src/utils/users.tsx
@@ -0,0 +1,37 @@
+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')
+ }),
+ })
diff --git a/e2e/solid-start/basic-solid-query/tailwind.config.mjs b/e2e/solid-start/basic-solid-query/tailwind.config.mjs
new file mode 100644
index 0000000000..e49f4eb776
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/tailwind.config.mjs
@@ -0,0 +1,4 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./src/**/*.{js,jsx,ts,tsx}'],
+}
diff --git a/e2e/solid-start/basic-solid-query/tests/app.spec.ts b/e2e/solid-start/basic-solid-query/tests/app.spec.ts
new file mode 100644
index 0000000000..af128bfcb4
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/tests/app.spec.ts
@@ -0,0 +1,36 @@
+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!')
+})
diff --git a/e2e/solid-start/basic-solid-query/tsconfig.json b/e2e/solid-start/basic-solid-query/tsconfig.json
new file mode 100644
index 0000000000..a40235b863
--- /dev/null
+++ b/e2e/solid-start/basic-solid-query/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "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
+ }
+}
diff --git a/examples/solid/start-basic-solid-query/.gitignore b/examples/solid/start-basic-solid-query/.gitignore
new file mode 100644
index 0000000000..be342025da
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/.gitignore
@@ -0,0 +1,22 @@
+node_modules
+package-lock.json
+yarn.lock
+
+.DS_Store
+.cache
+.env
+.vercel
+.output
+.vinxi
+
+/build/
+/api/
+/server/build
+/public/build
+.vinxi
+# Sentry Config File
+.env.sentry-build-plugin
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/examples/solid/start-basic-solid-query/.prettierignore b/examples/solid/start-basic-solid-query/.prettierignore
new file mode 100644
index 0000000000..2be5eaa6ec
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/.prettierignore
@@ -0,0 +1,4 @@
+**/build
+**/public
+pnpm-lock.yaml
+routeTree.gen.ts
\ No newline at end of file
diff --git a/examples/solid/start-basic-solid-query/.vscode/settings.json b/examples/solid/start-basic-solid-query/.vscode/settings.json
new file mode 100644
index 0000000000..00b5278e58
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/.vscode/settings.json
@@ -0,0 +1,11 @@
+{
+ "files.watcherExclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "search.exclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "files.readonlyInclude": {
+ "**/routeTree.gen.ts": true
+ }
+}
diff --git a/examples/solid/start-basic-solid-query/README.md b/examples/solid/start-basic-solid-query/README.md
new file mode 100644
index 0000000000..90cba4aac1
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/README.md
@@ -0,0 +1,72 @@
+# 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!
diff --git a/examples/solid/start-basic-solid-query/app.config.ts b/examples/solid/start-basic-solid-query/app.config.ts
new file mode 100644
index 0000000000..2a06e3d3f0
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/app.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@tanstack/solid-start/config'
+import tsConfigPaths from 'vite-tsconfig-paths'
+
+export default defineConfig({
+ tsr: {
+ appDirectory: 'src',
+ },
+ vite: {
+ plugins: [
+ tsConfigPaths({
+ projects: ['./tsconfig.json'],
+ }),
+ ],
+ },
+})
diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json
new file mode 100644
index 0000000000..5a16d749dd
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "tanstack-solid-start-example-basic-solid-query",
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "scripts": {
+ "dev": "vinxi dev",
+ "build": "vinxi build",
+ "start": "vinxi start"
+ },
+ "dependencies": {
+ "@tanstack/solid-query": "^5.66.0",
+ "@tanstack/solid-query-devtools": "^5.66.0",
+ "@tanstack/solid-router": "^1.114.29",
+ "@tanstack/solid-router-with-query": "^1.114.29",
+ "@tanstack/solid-router-devtools": "^1.114.29",
+ "@tanstack/solid-start": "^1.114.30",
+ "solid-js": "^1.9.5",
+ "redaxios": "^0.5.1",
+ "tailwind-merge": "^2.6.0",
+ "vinxi": "0.5.3"
+ },
+ "devDependencies": {
+ "@types/node": "^22.5.4",
+ "postcss": "^8.5.1",
+ "autoprefixer": "^10.4.20",
+ "tailwindcss": "^3.4.17",
+ "typescript": "^5.7.2",
+ "vite-tsconfig-paths": "^5.1.4"
+ }
+}
diff --git a/examples/solid/start-basic-solid-query/postcss.config.mjs b/examples/solid/start-basic-solid-query/postcss.config.mjs
new file mode 100644
index 0000000000..2e7af2b7f1
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/postcss.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/examples/solid/start-basic-solid-query/public/android-chrome-192x192.png b/examples/solid/start-basic-solid-query/public/android-chrome-192x192.png
new file mode 100644
index 0000000000..09c8324f8c
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/android-chrome-192x192.png differ
diff --git a/examples/solid/start-basic-solid-query/public/android-chrome-512x512.png b/examples/solid/start-basic-solid-query/public/android-chrome-512x512.png
new file mode 100644
index 0000000000..11d626ea3d
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/android-chrome-512x512.png differ
diff --git a/examples/solid/start-basic-solid-query/public/apple-touch-icon.png b/examples/solid/start-basic-solid-query/public/apple-touch-icon.png
new file mode 100644
index 0000000000..5a9423cc02
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/apple-touch-icon.png differ
diff --git a/examples/solid/start-basic-solid-query/public/favicon-16x16.png b/examples/solid/start-basic-solid-query/public/favicon-16x16.png
new file mode 100644
index 0000000000..e3389b0044
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/favicon-16x16.png differ
diff --git a/examples/solid/start-basic-solid-query/public/favicon-32x32.png b/examples/solid/start-basic-solid-query/public/favicon-32x32.png
new file mode 100644
index 0000000000..900c77d444
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/favicon-32x32.png differ
diff --git a/examples/solid/start-basic-solid-query/public/favicon.ico b/examples/solid/start-basic-solid-query/public/favicon.ico
new file mode 100644
index 0000000000..1a1751676f
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/favicon.ico differ
diff --git a/examples/solid/start-basic-solid-query/public/favicon.png b/examples/solid/start-basic-solid-query/public/favicon.png
new file mode 100644
index 0000000000..1e77bc0609
Binary files /dev/null and b/examples/solid/start-basic-solid-query/public/favicon.png differ
diff --git a/examples/solid/start-basic-solid-query/public/site.webmanifest b/examples/solid/start-basic-solid-query/public/site.webmanifest
new file mode 100644
index 0000000000..fa99de77db
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/public/site.webmanifest
@@ -0,0 +1,19 @@
+{
+ "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"
+}
diff --git a/examples/solid/start-basic-solid-query/src/api.ts b/examples/solid/start-basic-solid-query/src/api.ts
new file mode 100644
index 0000000000..ed511bcd26
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/api.ts
@@ -0,0 +1,6 @@
+import {
+ createStartAPIHandler,
+ defaultAPIFileRouteHandler,
+} from '@tanstack/solid-start/api'
+
+export default createStartAPIHandler(defaultAPIFileRouteHandler)
diff --git a/examples/solid/start-basic-solid-query/src/client.tsx b/examples/solid/start-basic-solid-query/src/client.tsx
new file mode 100644
index 0000000000..b8d415d9a8
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/client.tsx
@@ -0,0 +1,8 @@
+///
+import { hydrateRoot } from 'solid-dom/client'
+import { StartClient } from '@tanstack/solid-start'
+import { createRouter } from './router'
+
+const router = createRouter()
+
+hydrateRoot(document, )
diff --git a/examples/solid/start-basic-solid-query/src/components/DefaultCatchBoundary.tsx b/examples/solid/start-basic-solid-query/src/components/DefaultCatchBoundary.tsx
new file mode 100644
index 0000000000..32aed20e67
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/components/DefaultCatchBoundary.tsx
@@ -0,0 +1,53 @@
+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
+
+ )}
+
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/components/NotFound.tsx b/examples/solid/start-basic-solid-query/src/components/NotFound.tsx
new file mode 100644
index 0000000000..ca4c1960fa
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/components/NotFound.tsx
@@ -0,0 +1,25 @@
+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
+
+
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routeTree.gen.ts b/examples/solid/start-basic-solid-query/src/routeTree.gen.ts
new file mode 100644
index 0000000000..3a3b2216ad
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routeTree.gen.ts
@@ -0,0 +1,483 @@
+/* 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 Routes
+
+import { Route as rootRoute } from './routes/__root'
+import { Route as RedirectImport } from './routes/redirect'
+import { Route as DeferredImport } from './routes/deferred'
+import { Route as PathlessLayoutImport } from './routes/_pathlessLayout'
+import { Route as UsersRouteImport } from './routes/users.route'
+import { Route as PostsRouteImport } from './routes/posts.route'
+import { Route as IndexImport } from './routes/index'
+import { Route as UsersIndexImport } from './routes/users.index'
+import { Route as PostsIndexImport } from './routes/posts.index'
+import { Route as UsersUserIdImport } from './routes/users.$userId'
+import { Route as PostsPostIdImport } from './routes/posts.$postId'
+import { Route as PathlessLayoutNestedLayoutImport } from './routes/_pathlessLayout/_nested-layout'
+import { Route as PostsPostIdDeepImport } from './routes/posts_.$postId.deep'
+import { Route as PathlessLayoutNestedLayoutRouteBImport } from './routes/_pathlessLayout/_nested-layout/route-b'
+import { Route as PathlessLayoutNestedLayoutRouteAImport } from './routes/_pathlessLayout/_nested-layout/route-a'
+
+// Create/Update Routes
+
+const RedirectRoute = RedirectImport.update({
+ id: '/redirect',
+ path: '/redirect',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const DeferredRoute = DeferredImport.update({
+ id: '/deferred',
+ path: '/deferred',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const PathlessLayoutRoute = PathlessLayoutImport.update({
+ id: '/_pathlessLayout',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const UsersRouteRoute = UsersRouteImport.update({
+ id: '/users',
+ path: '/users',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const PostsRouteRoute = PostsRouteImport.update({
+ id: '/posts',
+ path: '/posts',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const IndexRoute = IndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const UsersIndexRoute = UsersIndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => UsersRouteRoute,
+} as any)
+
+const PostsIndexRoute = PostsIndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => PostsRouteRoute,
+} as any)
+
+const UsersUserIdRoute = UsersUserIdImport.update({
+ id: '/$userId',
+ path: '/$userId',
+ getParentRoute: () => UsersRouteRoute,
+} as any)
+
+const PostsPostIdRoute = PostsPostIdImport.update({
+ id: '/$postId',
+ path: '/$postId',
+ getParentRoute: () => PostsRouteRoute,
+} as any)
+
+const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutImport.update(
+ {
+ id: '/_nested-layout',
+ getParentRoute: () => PathlessLayoutRoute,
+ } as any,
+)
+
+const PostsPostIdDeepRoute = PostsPostIdDeepImport.update({
+ id: '/posts_/$postId/deep',
+ path: '/posts/$postId/deep',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const PathlessLayoutNestedLayoutRouteBRoute =
+ PathlessLayoutNestedLayoutRouteBImport.update({
+ id: '/route-b',
+ path: '/route-b',
+ getParentRoute: () => PathlessLayoutNestedLayoutRoute,
+ } as any)
+
+const PathlessLayoutNestedLayoutRouteARoute =
+ PathlessLayoutNestedLayoutRouteAImport.update({
+ id: '/route-a',
+ path: '/route-a',
+ getParentRoute: () => PathlessLayoutNestedLayoutRoute,
+ } as any)
+
+// Populate the FileRoutesByPath interface
+
+declare module '@tanstack/solid-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexImport
+ parentRoute: typeof rootRoute
+ }
+ '/posts': {
+ id: '/posts'
+ path: '/posts'
+ fullPath: '/posts'
+ preLoaderRoute: typeof PostsRouteImport
+ parentRoute: typeof rootRoute
+ }
+ '/users': {
+ id: '/users'
+ path: '/users'
+ fullPath: '/users'
+ preLoaderRoute: typeof UsersRouteImport
+ parentRoute: typeof rootRoute
+ }
+ '/_pathlessLayout': {
+ id: '/_pathlessLayout'
+ path: ''
+ fullPath: ''
+ preLoaderRoute: typeof PathlessLayoutImport
+ parentRoute: typeof rootRoute
+ }
+ '/deferred': {
+ id: '/deferred'
+ path: '/deferred'
+ fullPath: '/deferred'
+ preLoaderRoute: typeof DeferredImport
+ parentRoute: typeof rootRoute
+ }
+ '/redirect': {
+ id: '/redirect'
+ path: '/redirect'
+ fullPath: '/redirect'
+ preLoaderRoute: typeof RedirectImport
+ parentRoute: typeof rootRoute
+ }
+ '/_pathlessLayout/_nested-layout': {
+ id: '/_pathlessLayout/_nested-layout'
+ path: ''
+ fullPath: ''
+ preLoaderRoute: typeof PathlessLayoutNestedLayoutImport
+ parentRoute: typeof PathlessLayoutImport
+ }
+ '/posts/$postId': {
+ id: '/posts/$postId'
+ path: '/$postId'
+ fullPath: '/posts/$postId'
+ preLoaderRoute: typeof PostsPostIdImport
+ parentRoute: typeof PostsRouteImport
+ }
+ '/users/$userId': {
+ id: '/users/$userId'
+ path: '/$userId'
+ fullPath: '/users/$userId'
+ preLoaderRoute: typeof UsersUserIdImport
+ parentRoute: typeof UsersRouteImport
+ }
+ '/posts/': {
+ id: '/posts/'
+ path: '/'
+ fullPath: '/posts/'
+ preLoaderRoute: typeof PostsIndexImport
+ parentRoute: typeof PostsRouteImport
+ }
+ '/users/': {
+ id: '/users/'
+ path: '/'
+ fullPath: '/users/'
+ preLoaderRoute: typeof UsersIndexImport
+ parentRoute: typeof UsersRouteImport
+ }
+ '/_pathlessLayout/_nested-layout/route-a': {
+ id: '/_pathlessLayout/_nested-layout/route-a'
+ path: '/route-a'
+ fullPath: '/route-a'
+ preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteAImport
+ parentRoute: typeof PathlessLayoutNestedLayoutImport
+ }
+ '/_pathlessLayout/_nested-layout/route-b': {
+ id: '/_pathlessLayout/_nested-layout/route-b'
+ path: '/route-b'
+ fullPath: '/route-b'
+ preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBImport
+ parentRoute: typeof PathlessLayoutNestedLayoutImport
+ }
+ '/posts_/$postId/deep': {
+ id: '/posts_/$postId/deep'
+ path: '/posts/$postId/deep'
+ fullPath: '/posts/$postId/deep'
+ preLoaderRoute: typeof PostsPostIdDeepImport
+ parentRoute: typeof rootRoute
+ }
+ }
+}
+
+// Create and export the route tree
+
+interface PostsRouteRouteChildren {
+ PostsPostIdRoute: typeof PostsPostIdRoute
+ PostsIndexRoute: typeof PostsIndexRoute
+}
+
+const PostsRouteRouteChildren: PostsRouteRouteChildren = {
+ PostsPostIdRoute: PostsPostIdRoute,
+ PostsIndexRoute: PostsIndexRoute,
+}
+
+const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren(
+ PostsRouteRouteChildren,
+)
+
+interface UsersRouteRouteChildren {
+ UsersUserIdRoute: typeof UsersUserIdRoute
+ UsersIndexRoute: typeof UsersIndexRoute
+}
+
+const UsersRouteRouteChildren: UsersRouteRouteChildren = {
+ UsersUserIdRoute: UsersUserIdRoute,
+ UsersIndexRoute: UsersIndexRoute,
+}
+
+const UsersRouteRouteWithChildren = UsersRouteRoute._addFileChildren(
+ UsersRouteRouteChildren,
+)
+
+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,
+)
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/posts': typeof PostsRouteRouteWithChildren
+ '/users': typeof UsersRouteRouteWithChildren
+ '': typeof PathlessLayoutNestedLayoutRouteWithChildren
+ '/deferred': typeof DeferredRoute
+ '/redirect': typeof RedirectRoute
+ '/posts/$postId': typeof PostsPostIdRoute
+ '/users/$userId': typeof UsersUserIdRoute
+ '/posts/': typeof PostsIndexRoute
+ '/users/': typeof UsersIndexRoute
+ '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
+ '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
+ '/posts/$postId/deep': typeof PostsPostIdDeepRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '': typeof PathlessLayoutNestedLayoutRouteWithChildren
+ '/deferred': typeof DeferredRoute
+ '/redirect': typeof RedirectRoute
+ '/posts/$postId': typeof PostsPostIdRoute
+ '/users/$userId': typeof UsersUserIdRoute
+ '/posts': typeof PostsIndexRoute
+ '/users': typeof UsersIndexRoute
+ '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
+ '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
+ '/posts/$postId/deep': typeof PostsPostIdDeepRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+ '/posts': typeof PostsRouteRouteWithChildren
+ '/users': typeof UsersRouteRouteWithChildren
+ '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren
+ '/deferred': typeof DeferredRoute
+ '/redirect': typeof RedirectRoute
+ '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren
+ '/posts/$postId': typeof PostsPostIdRoute
+ '/users/$userId': typeof UsersUserIdRoute
+ '/posts/': typeof PostsIndexRoute
+ '/users/': typeof UsersIndexRoute
+ '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
+ '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
+ '/posts_/$postId/deep': typeof PostsPostIdDeepRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths:
+ | '/'
+ | '/posts'
+ | '/users'
+ | ''
+ | '/deferred'
+ | '/redirect'
+ | '/posts/$postId'
+ | '/users/$userId'
+ | '/posts/'
+ | '/users/'
+ | '/route-a'
+ | '/route-b'
+ | '/posts/$postId/deep'
+ fileRoutesByTo: FileRoutesByTo
+ to:
+ | '/'
+ | ''
+ | '/deferred'
+ | '/redirect'
+ | '/posts/$postId'
+ | '/users/$userId'
+ | '/posts'
+ | '/users'
+ | '/route-a'
+ | '/route-b'
+ | '/posts/$postId/deep'
+ id:
+ | '__root__'
+ | '/'
+ | '/posts'
+ | '/users'
+ | '/_pathlessLayout'
+ | '/deferred'
+ | '/redirect'
+ | '/_pathlessLayout/_nested-layout'
+ | '/posts/$postId'
+ | '/users/$userId'
+ | '/posts/'
+ | '/users/'
+ | '/_pathlessLayout/_nested-layout/route-a'
+ | '/_pathlessLayout/_nested-layout/route-b'
+ | '/posts_/$postId/deep'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ PostsRouteRoute: typeof PostsRouteRouteWithChildren
+ UsersRouteRoute: typeof UsersRouteRouteWithChildren
+ PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren
+ DeferredRoute: typeof DeferredRoute
+ RedirectRoute: typeof RedirectRoute
+ PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ PostsRouteRoute: PostsRouteRouteWithChildren,
+ UsersRouteRoute: UsersRouteRouteWithChildren,
+ PathlessLayoutRoute: PathlessLayoutRouteWithChildren,
+ DeferredRoute: DeferredRoute,
+ RedirectRoute: RedirectRoute,
+ PostsPostIdDeepRoute: PostsPostIdDeepRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/posts",
+ "/users",
+ "/_pathlessLayout",
+ "/deferred",
+ "/redirect",
+ "/posts_/$postId/deep"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/posts": {
+ "filePath": "posts.route.tsx",
+ "children": [
+ "/posts/$postId",
+ "/posts/"
+ ]
+ },
+ "/users": {
+ "filePath": "users.route.tsx",
+ "children": [
+ "/users/$userId",
+ "/users/"
+ ]
+ },
+ "/_pathlessLayout": {
+ "filePath": "_pathlessLayout.tsx",
+ "children": [
+ "/_pathlessLayout/_nested-layout"
+ ]
+ },
+ "/deferred": {
+ "filePath": "deferred.tsx"
+ },
+ "/redirect": {
+ "filePath": "redirect.tsx"
+ },
+ "/_pathlessLayout/_nested-layout": {
+ "filePath": "_pathlessLayout/_nested-layout.tsx",
+ "parent": "/_pathlessLayout",
+ "children": [
+ "/_pathlessLayout/_nested-layout/route-a",
+ "/_pathlessLayout/_nested-layout/route-b"
+ ]
+ },
+ "/posts/$postId": {
+ "filePath": "posts.$postId.tsx",
+ "parent": "/posts"
+ },
+ "/users/$userId": {
+ "filePath": "users.$userId.tsx",
+ "parent": "/users"
+ },
+ "/posts/": {
+ "filePath": "posts.index.tsx",
+ "parent": "/posts"
+ },
+ "/users/": {
+ "filePath": "users.index.tsx",
+ "parent": "/users"
+ },
+ "/_pathlessLayout/_nested-layout/route-a": {
+ "filePath": "_pathlessLayout/_nested-layout/route-a.tsx",
+ "parent": "/_pathlessLayout/_nested-layout"
+ },
+ "/_pathlessLayout/_nested-layout/route-b": {
+ "filePath": "_pathlessLayout/_nested-layout/route-b.tsx",
+ "parent": "/_pathlessLayout/_nested-layout"
+ },
+ "/posts_/$postId/deep": {
+ "filePath": "posts_.$postId.deep.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/examples/solid/start-basic-solid-query/src/router.tsx b/examples/solid/start-basic-solid-query/src/router.tsx
new file mode 100644
index 0000000000..91891f3193
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/router.tsx
@@ -0,0 +1,31 @@
+import { QueryClient } from '@tanstack/solid-query'
+import { createRouter as createTanStackRouter } from '@tanstack/solid-router'
+import { routerWithQueryClient } from '@tanstack/solid-router-with-query'
+import { routeTree } from './routeTree.gen'
+import { DefaultCatchBoundary } from './components/DefaultCatchBoundary'
+import { NotFound } from './components/NotFound'
+
+// NOTE: Most of the integration code found here is experimental and will
+// definitely end up in a more streamlined API in the future. This is just
+// to show what's possible with the current APIs.
+
+export function createRouter() {
+ const queryClient = new QueryClient()
+
+ return routerWithQueryClient(
+ createTanStackRouter({
+ routeTree,
+ context: { queryClient },
+ defaultPreload: 'intent',
+ defaultErrorComponent: DefaultCatchBoundary,
+ defaultNotFoundComponent: () => ,
+ }),
+ queryClient,
+ )
+}
+
+declare module '@tanstack/solid-router' {
+ interface Register {
+ router: ReturnType
+ }
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/__root.tsx b/examples/solid/start-basic-solid-query/src/routes/__root.tsx
new file mode 100644
index 0000000000..053962a196
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/__root.tsx
@@ -0,0 +1,136 @@
+import {
+ HeadContent,
+ Link,
+ Outlet,
+ Scripts,
+ createRootRouteWithContext,
+} from '@tanstack/solid-router'
+import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
+import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
+import * as Solid from 'solid-js'
+import type { QueryClient } from '@tanstack/solid-query'
+import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'
+import { NotFound } from '~/components/NotFound'
+import appCss from '~/styles/app.css?url'
+import { seo } from '~/utils/seo'
+
+export const Route = createRootRouteWithContext<{
+ queryClient: QueryClient
+}>()({
+ 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 (
+
+
+
+ )
+ },
+ notFoundComponent: () => ,
+ component: RootComponent,
+})
+
+function RootComponent() {
+ return
+}
+
+function RootDocument({ children }: { children: Solid.JSX.Element }) {
+ return (
+ <>
+
+
+
+ Home
+ {' '}
+
+ Posts
+ {' '}
+
+ Users
+ {' '}
+
+ Pathless Layout
+ {' '}
+
+ Deferred
+ {' '}
+
+ This Route Does Not Exist
+
+
+
+ {children}
+
+
+
+ >
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout.tsx b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout.tsx
new file mode 100644
index 0000000000..e525470c60
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout.tsx
@@ -0,0 +1,16 @@
+import { Outlet, createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_pathlessLayout')({
+ component: PathlessLayoutComponent,
+})
+
+function PathlessLayoutComponent() {
+ return (
+
+
I'm a pathless layout
+
+
+
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout.tsx b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout.tsx
new file mode 100644
index 0000000000..8651932b0f
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout.tsx
@@ -0,0 +1,34 @@
+import { Link, Outlet, createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({
+ component: PathlessLayoutComponent,
+})
+
+function PathlessLayoutComponent() {
+ return (
+
+
I'm a nested pathless layout
+
+
+ Go to route A
+
+
+ Go to route B
+
+
+
+
+
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout/route-a.tsx b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout/route-a.tsx
new file mode 100644
index 0000000000..a22902a271
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout/route-a.tsx
@@ -0,0 +1,11 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')(
+ {
+ component: LayoutAComponent,
+ },
+)
+
+function LayoutAComponent() {
+ return I'm A!
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout/route-b.tsx b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout/route-b.tsx
new file mode 100644
index 0000000000..36231d2153
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/_pathlessLayout/_nested-layout/route-b.tsx
@@ -0,0 +1,11 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')(
+ {
+ component: LayoutBComponent,
+ },
+)
+
+function LayoutBComponent() {
+ return I'm B!
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/api.users.ts b/examples/solid/start-basic-solid-query/src/routes/api.users.ts
new file mode 100644
index 0000000000..f37bf0db1b
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/api.users.ts
@@ -0,0 +1,17 @@
+import { json } from '@tanstack/solid-start'
+import { createAPIFileRoute } from '@tanstack/solid-start/api'
+import axios from 'redaxios'
+import type { User } from '../utils/users'
+
+export const APIRoute = createAPIFileRoute('/api/users')({
+ GET: async ({ request }) => {
+ console.info('Fetching users... @', request.url)
+ const res = await axios.get>(
+ 'https://jsonplaceholder.typicode.com/users',
+ )
+
+ const list = res.data.slice(0, 10)
+
+ return json(list.map((u) => ({ id: u.id, name: u.name, email: u.email })))
+ },
+})
diff --git a/examples/solid/start-basic-solid-query/src/routes/api/users.$id.ts b/examples/solid/start-basic-solid-query/src/routes/api/users.$id.ts
new file mode 100644
index 0000000000..b1786f6a30
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/api/users.$id.ts
@@ -0,0 +1,24 @@
+import { json } from '@tanstack/solid-start'
+import { createAPIFileRoute } from '@tanstack/solid-start/api'
+import axios from 'redaxios'
+import type { User } from '../../utils/users'
+
+export const APIRoute = createAPIFileRoute('/api/users/$id')({
+ GET: async ({ request, params }) => {
+ console.info(`Fetching users by id=${params.id}... @`, request.url)
+ try {
+ const res = await axios.get(
+ 'https://jsonplaceholder.typicode.com/users/' + params.id,
+ )
+
+ return json({
+ id: res.data.id,
+ name: res.data.name,
+ email: res.data.email,
+ })
+ } catch (e) {
+ console.error(e)
+ return json({ error: 'User not found' }, { status: 404 })
+ }
+ },
+})
diff --git a/examples/solid/start-basic-solid-query/src/routes/deferred.tsx b/examples/solid/start-basic-solid-query/src/routes/deferred.tsx
new file mode 100644
index 0000000000..7ce060840f
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/deferred.tsx
@@ -0,0 +1,53 @@
+import { queryOptions, createQuery } from '@tanstack/solid-query'
+import { createFileRoute } from '@tanstack/solid-router'
+import { Suspense, createSignal } from 'solid-js'
+
+const deferredQueryOptions = () =>
+ queryOptions({
+ queryKey: ['deferred'],
+ queryFn: async () => {
+ await new Promise((r) => setTimeout(r, 3000))
+ return {
+ message: `Hello deferred from the server!`,
+ status: 'success',
+ time: new Date(),
+ }
+ },
+ })
+
+export const Route = createFileRoute('/deferred')({
+ loader: ({ context }) => {
+ // Kick off loading as early as possible!
+ context.queryClient.prefetchQuery(deferredQueryOptions())
+ },
+ component: Deferred,
+})
+
+function Deferred() {
+ const [count, setCount] = createSignal(0)
+
+ return (
+
+
+
+
+
Count: {count()}
+
+
+
+
+ )
+}
+
+function DeferredQuery() {
+ const deferredQuery = createQuery(() => deferredQueryOptions())
+
+ return (
+
+
Deferred Query
+
Status: {deferredQuery.data?.status}
+
Message: {deferredQuery.data?.message}
+
Time: {deferredQuery.data?.time.toISOString()}
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/index.tsx b/examples/solid/start-basic-solid-query/src/routes/index.tsx
new file mode 100644
index 0000000000..a128aeca0e
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/index.tsx
@@ -0,0 +1,13 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/')({
+ component: Home,
+})
+
+function Home() {
+ return (
+
+
Welcome Home!!!
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/posts.$postId.tsx b/examples/solid/start-basic-solid-query/src/routes/posts.$postId.tsx
new file mode 100644
index 0000000000..a370d08090
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/posts.$postId.tsx
@@ -0,0 +1,51 @@
+import { ErrorComponent, Link, createFileRoute } from '@tanstack/solid-router'
+import { createQuery } from '@tanstack/solid-query'
+import { postQueryOptions } from '../utils/posts'
+import type { ErrorComponentProps } from '@tanstack/solid-router'
+import { NotFound } from '~/components/NotFound'
+
+export const Route = createFileRoute('/posts/$postId')({
+ loader: async ({ params: { postId }, context }) => {
+ const data = await context.queryClient.ensureQueryData(
+ postQueryOptions(postId),
+ )
+
+ return {
+ title: data.title,
+ }
+ },
+ head: ({ loaderData }) => ({
+ meta: loaderData ? [{ title: loaderData.title }] : undefined,
+ }),
+ errorComponent: PostErrorComponent,
+ notFoundComponent: () => {
+ return Post not found
+ },
+ component: PostComponent,
+})
+
+export function PostErrorComponent({ error }: ErrorComponentProps) {
+ return
+}
+
+function PostComponent() {
+ const params = Route.useParams()
+ const postQuery = createQuery(() => postQueryOptions(params().postId))
+
+ return (
+
+
{postQuery.data?.title}
+
{postQuery.data?.body}
+
+ Deep View
+
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/posts.index.tsx b/examples/solid/start-basic-solid-query/src/routes/posts.index.tsx
new file mode 100644
index 0000000000..33d0386c19
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/posts.index.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/posts/')({
+ component: PostsIndexComponent,
+})
+
+function PostsIndexComponent() {
+ return Select a post.
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/posts.route.tsx b/examples/solid/start-basic-solid-query/src/routes/posts.route.tsx
new file mode 100644
index 0000000000..b568cdd3d6
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/posts.route.tsx
@@ -0,0 +1,45 @@
+import { createQuery } from '@tanstack/solid-query'
+import { Link, Outlet, createFileRoute } from '@tanstack/solid-router'
+import { postsQueryOptions } from '../utils/posts'
+
+export const Route = createFileRoute('/posts')({
+ loader: async ({ context }) => {
+ await context.queryClient.ensureQueryData(postsQueryOptions())
+ },
+ head: () => ({
+ meta: [{ title: 'Posts' }],
+ }),
+ component: PostsComponent,
+})
+
+function PostsComponent() {
+ const postsQuery = createQuery(() => postsQueryOptions())
+
+ return (
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/posts_.$postId.deep.tsx b/examples/solid/start-basic-solid-query/src/routes/posts_.$postId.deep.tsx
new file mode 100644
index 0000000000..f5ae7ff840
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/posts_.$postId.deep.tsx
@@ -0,0 +1,36 @@
+import { Link, createFileRoute } from '@tanstack/solid-router'
+import { useSuspenseQuery } from '@tanstack/solid-query'
+import { postQueryOptions } from '../utils/posts'
+import { PostErrorComponent } from './posts.$postId'
+
+export const Route = createFileRoute('/posts_/$postId/deep')({
+ loader: async ({ params: { postId }, context }) => {
+ const data = await context.queryClient.ensureQueryData(
+ postQueryOptions(postId),
+ )
+
+ return {
+ title: data.title,
+ }
+ },
+ head: ({ loaderData }) => ({
+ meta: loaderData ? [{ title: loaderData.title }] : undefined,
+ }),
+ errorComponent: PostErrorComponent,
+ component: PostDeepComponent,
+})
+
+function PostDeepComponent() {
+ const { postId } = Route.useParams()
+ const postQuery = useSuspenseQuery(postQueryOptions(postId))
+
+ return (
+
+
+ ← All Posts
+
+
{postQuery.data.title}
+
{postQuery.data.body}
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/redirect.tsx b/examples/solid/start-basic-solid-query/src/routes/redirect.tsx
new file mode 100644
index 0000000000..ca017f0635
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/redirect.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute, redirect } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/redirect')({
+ beforeLoad: async () => {
+ throw redirect({
+ to: '/posts',
+ })
+ },
+})
diff --git a/examples/solid/start-basic-solid-query/src/routes/users.$userId.tsx b/examples/solid/start-basic-solid-query/src/routes/users.$userId.tsx
new file mode 100644
index 0000000000..ad8a1a1209
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/users.$userId.tsx
@@ -0,0 +1,33 @@
+import { createQuery } 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
+ },
+})
+
+export function UserErrorComponent({ error }: ErrorComponentProps) {
+ return
+}
+
+function UserComponent() {
+ const params = Route.useParams()
+ const userQuery = createQuery(() => userQueryOptions(params().userId))
+ const user = userQuery.data
+
+ return (
+
+
{user?.name}
+
{user?.email}
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/users.index.tsx b/examples/solid/start-basic-solid-query/src/routes/users.index.tsx
new file mode 100644
index 0000000000..bbc96801a9
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/users.index.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/users/')({
+ component: UsersIndexComponent,
+})
+
+function UsersIndexComponent() {
+ return Select a user.
+}
diff --git a/examples/solid/start-basic-solid-query/src/routes/users.route.tsx b/examples/solid/start-basic-solid-query/src/routes/users.route.tsx
new file mode 100644
index 0000000000..fa2edb30a5
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/routes/users.route.tsx
@@ -0,0 +1,42 @@
+import { createQuery } from '@tanstack/solid-query'
+import { Link, Outlet, createFileRoute } from '@tanstack/solid-router'
+import { usersQueryOptions } from '../utils/users'
+
+export const Route = createFileRoute('/users')({
+ loader: async ({ context }) => {
+ await context.queryClient.ensureQueryData(usersQueryOptions())
+ },
+ component: UsersComponent,
+})
+
+function UsersComponent() {
+ const usersQuery = createQuery(() => usersQueryOptions())
+
+ return (
+
+
+ {[
+ ...usersQuery.data!,
+ { id: 'i-do-not-exist', name: 'Non-existent User', email: '' },
+ ].map((user) => {
+ return (
+ -
+
+
{user.name}
+
+
+ )
+ })}
+
+
+
+
+ )
+}
diff --git a/examples/solid/start-basic-solid-query/src/ssr.tsx b/examples/solid/start-basic-solid-query/src/ssr.tsx
new file mode 100644
index 0000000000..ebd14c8120
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/ssr.tsx
@@ -0,0 +1,13 @@
+///
+import {
+ createStartHandler,
+ defaultStreamHandler,
+} from '@tanstack/solid-start/server'
+import { getRouterManifest } from '@tanstack/solid-start/router-manifest'
+
+import { createRouter } from './router'
+
+export default createStartHandler({
+ createRouter,
+ getRouterManifest,
+})(defaultStreamHandler)
diff --git a/examples/solid/start-basic-solid-query/src/styles/app.css b/examples/solid/start-basic-solid-query/src/styles/app.css
new file mode 100644
index 0000000000..c53c870665
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/styles/app.css
@@ -0,0 +1,22 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@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;
+ }
+}
diff --git a/examples/solid/start-basic-solid-query/src/utils/posts.tsx b/examples/solid/start-basic-solid-query/src/utils/posts.tsx
new file mode 100644
index 0000000000..84091f451a
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/utils/posts.tsx
@@ -0,0 +1,49 @@
+import { queryOptions } from '@tanstack/solid-query'
+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
+}
+
+export const fetchPosts = createServerFn({ method: 'GET' }).handler(
+ async () => {
+ console.info('Fetching posts...')
+ return axios
+ .get>('https://jsonplaceholder.typicode.com/posts')
+ .then((r) => r.data.slice(0, 10))
+ },
+)
+
+export const postsQueryOptions = () =>
+ queryOptions({
+ queryKey: ['posts'],
+ queryFn: () => fetchPosts(),
+ })
+
+export const fetchPost = createServerFn({ method: 'GET' })
+ .validator((d: string) => d)
+ .handler(async ({ data }) => {
+ console.info(`Fetching post with id ${data}...`)
+ const post = await axios
+ .get(`https://jsonplaceholder.typicode.com/posts/${data}`)
+ .then((r) => r.data)
+ .catch((err) => {
+ console.error(err)
+ if (err.status === 404) {
+ throw notFound()
+ }
+ throw err
+ })
+
+ return post
+ })
+
+export const postQueryOptions = (postId: string) =>
+ queryOptions({
+ queryKey: ['post', postId],
+ queryFn: () => fetchPost({ data: postId }),
+ })
diff --git a/examples/solid/start-basic-solid-query/src/utils/seo.ts b/examples/solid/start-basic-solid-query/src/utils/seo.ts
new file mode 100644
index 0000000000..d18ad84b74
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/utils/seo.ts
@@ -0,0 +1,33 @@
+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
+}
diff --git a/examples/solid/start-basic-solid-query/src/utils/users.tsx b/examples/solid/start-basic-solid-query/src/utils/users.tsx
new file mode 100644
index 0000000000..a27f1bb142
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/src/utils/users.tsx
@@ -0,0 +1,34 @@
+import { queryOptions } from '@tanstack/solid-query'
+import axios from 'redaxios'
+
+export type User = {
+ id: number
+ name: string
+ email: string
+}
+
+export const DEPLOY_URL = 'http://localhost:3000'
+
+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')
+ }),
+ })
diff --git a/examples/solid/start-basic-solid-query/tailwind.config.mjs b/examples/solid/start-basic-solid-query/tailwind.config.mjs
new file mode 100644
index 0000000000..e49f4eb776
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/tailwind.config.mjs
@@ -0,0 +1,4 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./src/**/*.{js,jsx,ts,tsx}'],
+}
diff --git a/examples/solid/start-basic-solid-query/tsconfig.json b/examples/solid/start-basic-solid-query/tsconfig.json
new file mode 100644
index 0000000000..a40235b863
--- /dev/null
+++ b/examples/solid/start-basic-solid-query/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "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
+ }
+}
diff --git a/package.json b/package.json
index aa7c8639b0..5d1474219a 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,8 @@
"@tanstack/history": "workspace:*",
"@tanstack/router-core": "workspace:*",
"@tanstack/react-router": "workspace:*",
+ "@tanstack/solid-router": "workspace:*",
+ "@tanstack/solid-router-with-query": "workspace:*",
"@tanstack/router-cli": "workspace:*",
"@tanstack/router-devtools": "workspace:*",
"@tanstack/router-devtools-core": "workspace:^",
diff --git a/packages/solid-router-with-query/README.md b/packages/solid-router-with-query/README.md
new file mode 100644
index 0000000000..d83bf5fdf4
--- /dev/null
+++ b/packages/solid-router-with-query/README.md
@@ -0,0 +1,31 @@
+
+
+# TanStack React Router
+
+
+
+🤖 Type-safe router w/ built-in caching & URL state management for React!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Enjoy this library? Try the entire [TanStack](https://tanstack.com)! [React Query](https://github.com/tannerlinsley/react-query), [React Table](https://github.com/tanstack/react-table), [React Charts](https://github.com/tannerlinsley/react-charts), [React Virtual](https://github.com/tannerlinsley/react-virtual)
+
+## Visit [tanstack.com/router](https://tanstack.com/router) for docs, guides, API and more!
diff --git a/packages/solid-router-with-query/eslint.config.js b/packages/solid-router-with-query/eslint.config.js
new file mode 100644
index 0000000000..bd7118fa1a
--- /dev/null
+++ b/packages/solid-router-with-query/eslint.config.js
@@ -0,0 +1,20 @@
+// @ts-check
+
+import rootConfig from '../../eslint.config.js'
+
+export default [
+ ...rootConfig,
+ {
+ files: ['**/*.{ts,tsx}'],
+ },
+ {
+ plugins: {},
+ rules: {},
+ },
+ {
+ files: ['**/__tests__/**'],
+ rules: {
+ '@typescript-eslint/no-unnecessary-condition': 'off',
+ },
+ },
+]
diff --git a/packages/solid-router-with-query/package.json b/packages/solid-router-with-query/package.json
new file mode 100644
index 0000000000..0036794a23
--- /dev/null
+++ b/packages/solid-router-with-query/package.json
@@ -0,0 +1,79 @@
+{
+ "name": "@tanstack/solid-router-with-query",
+ "version": "1.114.29",
+ "description": "Modern and scalable routing for React applications",
+ "author": "Tanner Linsley",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/TanStack/router.git",
+ "directory": "packages/solid-router-with-query"
+ },
+ "homepage": "https://tanstack.com/router",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "keywords": [
+ "solid",
+ "location",
+ "router",
+ "routing",
+ "async",
+ "async router",
+ "typescript"
+ ],
+ "scripts": {
+ "clean": "rimraf ./dist && rimraf ./coverage",
+ "test:eslint": "eslint ./src",
+ "test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
+ "test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js",
+ "test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js",
+ "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
+ "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js",
+ "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js",
+ "test:types:ts58": "tsc",
+ "test:unit": "vitest",
+ "test:unit:dev": "pnpm run test:unit --watch",
+ "test:build": "publint --strict && attw --ignore-rules no-resolution --pack .",
+ "build": "vite build"
+ },
+ "type": "module",
+ "types": "dist/esm/index.d.ts",
+ "main": "dist/cjs/index.cjs",
+ "module": "dist/esm/index.js",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/index.d.cts",
+ "default": "./dist/cjs/index.cjs"
+ }
+ },
+ "./package.json": "./package.json"
+ },
+ "sideEffects": false,
+ "files": [
+ "dist",
+ "src"
+ ],
+ "engines": {
+ "node": ">=12"
+ },
+ "devDependencies": {
+ "vite-plugin-solid": "^2.11.6",
+ "solid-js": ">=1.9.5",
+ "@tanstack/router-core": "workspace:^",
+ "@tanstack/solid-router": "workspace:^",
+ "@tanstack/solid-query": ">=5.66.0"
+ },
+ "peerDependencies": {
+ "solid-js": ">=1.9.5",
+ "@tanstack/router-core": ">=1.114.7",
+ "@tanstack/solid-router": ">=1.43.2",
+ "@tanstack/solid-query": ">=5.49.2"
+ }
+}
diff --git a/packages/solid-router-with-query/src/index.tsx b/packages/solid-router-with-query/src/index.tsx
new file mode 100644
index 0000000000..4601542ea1
--- /dev/null
+++ b/packages/solid-router-with-query/src/index.tsx
@@ -0,0 +1,215 @@
+import {
+ QueryClientProvider,
+ dehydrate,
+ hashKey,
+ hydrate,
+} from '@tanstack/solid-query'
+import { isRedirect } from '@tanstack/router-core'
+import type * as Solid from 'solid-js'
+import type { AnyRouter } from '@tanstack/solid-router'
+import type {
+ CreateQueryOptions,
+ QueryClient,
+ QueryKey,
+ QueryObserverResult,
+} from '@tanstack/solid-query'
+
+// Extended query options to include the properties used in this file
+interface ExtendedQueryOptions extends CreateQueryOptions {
+ queryKey: QueryKey
+ queryKeyHashFn?: (queryKey: QueryKey) => string
+ __skipInjection?: boolean
+}
+
+type AdditionalOptions = {
+ WrapProvider?: (props: { children: any }) => Solid.JSX.Element
+ /**
+ * If `true`, the QueryClient will handle errors thrown by `redirect()` inside of mutations and queries.
+ *
+ * @default true
+ * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction)
+ */
+ handleRedirects?: boolean
+}
+
+export type ValidateRouter =
+ NonNullable extends {
+ queryClient: QueryClient
+ }
+ ? TRouter
+ : never
+
+export function routerWithQueryClient(
+ router: ValidateRouter,
+ queryClient: QueryClient,
+ additionalOpts?: AdditionalOptions,
+): TRouter {
+ const seenQueryKeys = new Set()
+ const streamedQueryKeys = new Set()
+
+ const ogClientOptions = queryClient.getDefaultOptions()
+ queryClient.setDefaultOptions({
+ ...ogClientOptions,
+ queries: {
+ ...ogClientOptions.queries,
+ _experimental_beforeQuery: (options: CreateQueryOptions) => {
+ // Call the original beforeQuery
+ ;(ogClientOptions.queries as any)?._experimental_beforeQuery?.(options)
+
+ const extOptions = options as ExtendedQueryOptions
+ const hash = extOptions.queryKeyHashFn || hashKey
+ // On the server, check if we've already seen the query before
+ if (router.isServer) {
+ if (seenQueryKeys.has(hash(extOptions.queryKey))) {
+ return
+ }
+
+ seenQueryKeys.add(hash(extOptions.queryKey))
+
+ // If we haven't seen the query and we have data for it,
+ // That means it's going to get dehydrated with critical
+ // data, so we can skip the injection
+ if (queryClient.getQueryData(extOptions.queryKey) !== undefined) {
+ extOptions.__skipInjection = true
+ return
+ }
+ } else {
+ // On the client, pick up the deferred data from the stream
+ const dehydratedClient = router.clientSsr!.getStreamedValue(
+ '__QueryClient__' + hash(extOptions.queryKey),
+ )
+
+ // If we have data, hydrate it into the query client
+ if (dehydratedClient && !dehydratedClient.hydrated) {
+ dehydratedClient.hydrated = true
+ hydrate(queryClient, dehydratedClient)
+ }
+ }
+ },
+ _experimental_afterQuery: (
+ options: CreateQueryOptions,
+ _result: QueryObserverResult,
+ ) => {
+ // On the server (if we're not skipping injection)
+ // send down the dehydrated query
+ const extOptions = options as ExtendedQueryOptions
+ const hash = extOptions.queryKeyHashFn || hashKey
+ if (
+ router.isServer &&
+ !extOptions.__skipInjection &&
+ queryClient.getQueryData(extOptions.queryKey) !== undefined &&
+ !streamedQueryKeys.has(hash(extOptions.queryKey))
+ ) {
+ streamedQueryKeys.add(hash(extOptions.queryKey))
+
+ router.serverSsr!.streamValue(
+ '__QueryClient__' + hash(extOptions.queryKey),
+ dehydrate(queryClient, {
+ shouldDehydrateMutation: () => false,
+ shouldDehydrateQuery: (query) =>
+ hash(query.queryKey) === hash(extOptions.queryKey),
+ }),
+ )
+ }
+
+ // Call the original afterQuery
+ ;(ogClientOptions.queries as any)?._experimental_afterQuery?.(
+ options,
+ _result,
+ )
+ },
+ } as any,
+ })
+
+ if (additionalOpts?.handleRedirects ?? true) {
+ const ogMutationCacheConfig = queryClient.getMutationCache().config
+ queryClient.getMutationCache().config = {
+ ...ogMutationCacheConfig,
+ onError: (error, _variables, _context, _mutation) => {
+ if (isRedirect(error)) {
+ return router.navigate(
+ router.resolveRedirect({
+ ...error,
+ _fromLocation: router.state.location,
+ }),
+ )
+ }
+
+ return ogMutationCacheConfig.onError?.(
+ error,
+ _variables,
+ _context,
+ _mutation,
+ )
+ },
+ }
+
+ const ogQueryCacheConfig = queryClient.getQueryCache().config
+ queryClient.getQueryCache().config = {
+ ...ogQueryCacheConfig,
+ onError: (error, _query) => {
+ if (isRedirect(error)) {
+ return router.navigate(
+ router.resolveRedirect({
+ ...error,
+ _fromLocation: router.state.location,
+ }),
+ )
+ }
+
+ return ogQueryCacheConfig.onError?.(error, _query)
+ },
+ }
+ }
+
+ const ogOptions = router.options
+ router.options = {
+ ...router.options,
+ dehydrate: () => {
+ return {
+ ...ogOptions.dehydrate?.(),
+ // When critical data is dehydrated, we also dehydrate the query client
+ dehydratedQueryClient: dehydrate(queryClient),
+ }
+ },
+ hydrate: (dehydrated: any) => {
+ ogOptions.hydrate?.(dehydrated)
+ // On the client, hydrate the query client with the dehydrated data
+ hydrate(queryClient, dehydrated.dehydratedQueryClient)
+ },
+ context: {
+ ...ogOptions.context,
+ // Pass the query client to the context, so we can access it in loaders
+ queryClient,
+ },
+ // Wrap the app in a QueryClientProvider
+ Wrap: ({ children }) => {
+ const OuterWrapper = additionalOpts?.WrapProvider
+ return (
+ <>
+ {OuterWrapper ? (
+
+
+ {ogOptions.Wrap ? (
+ {children}
+ ) : (
+ children
+ )}
+
+
+ ) : (
+
+ {ogOptions.Wrap ? (
+ {children}
+ ) : (
+ children
+ )}
+
+ )}
+ >
+ )
+ },
+ }
+
+ return router
+}
diff --git a/packages/solid-router-with-query/tests/index.test-d.ts b/packages/solid-router-with-query/tests/index.test-d.ts
new file mode 100644
index 0000000000..a6a220526a
--- /dev/null
+++ b/packages/solid-router-with-query/tests/index.test-d.ts
@@ -0,0 +1,55 @@
+import { QueryClient } from '@tanstack/solid-query'
+import {
+ createRootRouteWithContext,
+ createRouter,
+} from '@tanstack/solid-router'
+
+import { expectTypeOf, test } from 'vitest'
+
+import { routerWithQueryClient } from '../src'
+
+test('basic { queryClient } context', () => {
+ const root = createRootRouteWithContext<{
+ queryClient: QueryClient
+ }>()({})
+
+ const queryClient = new QueryClient()
+ const router = createRouter({
+ context: { queryClient },
+ routeTree: root,
+ })
+
+ const routerWithQuery = routerWithQueryClient(router, queryClient)
+ expectTypeOf(routerWithQuery).toEqualTypeOf(router)
+})
+
+test('no context fails', () => {
+ const root = createRootRouteWithContext()({})
+
+ const queryClient = new QueryClient()
+ const router = createRouter({
+ routeTree: root,
+ })
+
+ routerWithQueryClient(
+ // @ts-expect-error - QueryClient must be in context type
+ router,
+ queryClient,
+ )
+})
+
+test('allows additional props on context', () => {
+ const root = createRootRouteWithContext<{
+ queryClient: QueryClient
+ extra: string
+ }>()({})
+
+ const queryClient = new QueryClient()
+ const router = createRouter({
+ context: { queryClient, extra: 'extra' },
+ routeTree: root,
+ })
+
+ const routerWithQuery = routerWithQueryClient(router, queryClient)
+ expectTypeOf(routerWithQuery).toEqualTypeOf(router)
+})
diff --git a/packages/solid-router-with-query/tsconfig.json b/packages/solid-router-with-query/tsconfig.json
new file mode 100644
index 0000000000..e24672b5de
--- /dev/null
+++ b/packages/solid-router-with-query/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "jsx": "preserve",
+ "jsxImportSource": "solid-js"
+ },
+ "include": ["src", "tests", "vite.config.ts"]
+}
diff --git a/packages/solid-router-with-query/vite.config.ts b/packages/solid-router-with-query/vite.config.ts
new file mode 100644
index 0000000000..b78cdf1373
--- /dev/null
+++ b/packages/solid-router-with-query/vite.config.ts
@@ -0,0 +1,24 @@
+import { defineConfig, mergeConfig } from 'vitest/config'
+import { tanstackViteConfig } from '@tanstack/config/vite'
+import solid from 'vite-plugin-solid'
+import packageJson from './package.json'
+import type { UserConfig } from 'vitest/config'
+
+const config = defineConfig({
+ plugins: [solid()] as UserConfig['plugins'],
+ test: {
+ name: packageJson.name,
+ dir: './tests',
+ watch: false,
+ environment: 'jsdom',
+ typecheck: { enabled: true },
+ },
+})
+
+export default mergeConfig(
+ config,
+ tanstackViteConfig({
+ entry: './src/index.tsx',
+ srcDir: './src',
+ }),
+)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2a505dbe86..a7870eec6e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -16,6 +16,8 @@ overrides:
'@tanstack/history': workspace:*
'@tanstack/router-core': workspace:*
'@tanstack/react-router': workspace:*
+ '@tanstack/solid-router': workspace:*
+ '@tanstack/solid-router-with-query': workspace:*
'@tanstack/router-cli': workspace:*
'@tanstack/router-devtools': workspace:*
'@tanstack/router-devtools-core': workspace:^
@@ -1498,7 +1500,7 @@ importers:
e2e/solid-router/basic:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1538,7 +1540,7 @@ importers:
specifier: workspace:*
version: link:../../../packages/router-plugin
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1575,7 +1577,7 @@ importers:
specifier: workspace:*
version: link:../../../packages/router-plugin
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1624,7 +1626,7 @@ importers:
specifier: workspace:*
version: link:../../../packages/router-plugin
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1661,7 +1663,7 @@ importers:
e2e/solid-router/basic-scroll-restoration:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1707,7 +1709,7 @@ importers:
specifier: ^5.66.0
version: 5.66.0(@tanstack/solid-query@5.66.0(solid-js@1.9.5))(solid-js@1.9.5)
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1753,7 +1755,7 @@ importers:
specifier: ^5.66.0
version: 5.66.0(@tanstack/solid-query@5.66.0(solid-js@1.9.5))(solid-js@1.9.5)
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1796,7 +1798,7 @@ importers:
specifier: workspace:*
version: link:../../../packages/router-plugin
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1842,7 +1844,7 @@ importers:
specifier: workspace:*
version: link:../../../packages/router-plugin
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1885,7 +1887,7 @@ importers:
e2e/solid-router/rspack-basic-file-based:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1931,7 +1933,7 @@ importers:
e2e/solid-router/rspack-basic-virtual-named-export-config-file-based:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -1983,7 +1985,7 @@ importers:
specifier: workspace:*
version: link:../../../packages/router-plugin
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -2026,7 +2028,7 @@ importers:
e2e/solid-start/basic:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -2081,10 +2083,71 @@ importers:
specifier: ^5.1.4
version: 5.1.4(typescript@5.8.2)(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
- e2e/solid-start/basic-tsr-config:
+ e2e/solid-start/basic-solid-query:
dependencies:
+ '@tanstack/solid-query':
+ specifier: ^5.66.0
+ version: 5.66.0(solid-js@1.9.5)
+ '@tanstack/solid-query-devtools':
+ specifier: ^5.66.0
+ version: 5.66.0(@tanstack/solid-query@5.66.0(solid-js@1.9.5))(solid-js@1.9.5)
'@tanstack/solid-router':
+ specifier: workspace:*
+ version: link:../../../packages/solid-router
+ '@tanstack/solid-router-devtools':
+ specifier: workspace:^
+ version: link:../../../packages/solid-router-devtools
+ '@tanstack/solid-router-with-query':
+ specifier: workspace:*
+ version: link:../../../packages/solid-router-with-query
+ '@tanstack/solid-start':
+ specifier: workspace:*
+ version: link:../../../packages/solid-start
+ redaxios:
+ specifier: ^0.5.1
+ version: 0.5.1
+ solid-js:
+ specifier: ^1.9.5
+ version: 1.9.5
+ tailwind-merge:
+ specifier: ^2.6.0
+ version: 2.6.0
+ vinxi:
+ specifier: 0.5.3
+ version: 0.5.3(@types/node@22.13.4)(db0@0.2.3)(ioredis@5.4.2)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(typescript@5.8.2)(yaml@2.7.0)
+ devDependencies:
+ '@playwright/test':
+ specifier: ^1.50.1
+ version: 1.50.1
+ '@tanstack/router-e2e-utils':
specifier: workspace:^
+ version: link:../../e2e-utils
+ '@types/node':
+ specifier: ^22.10.2
+ version: 22.13.4
+ '@vitejs/plugin-react':
+ specifier: ^4.3.4
+ version: 4.3.4(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.4.20(postcss@8.5.3)
+ postcss:
+ specifier: ^8.5.1
+ version: 8.5.3
+ tailwindcss:
+ specifier: ^3.4.17
+ version: 3.4.17
+ typescript:
+ specifier: ^5.7.2
+ version: 5.8.2
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.8.2)(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+
+ e2e/solid-start/basic-tsr-config:
+ dependencies:
+ '@tanstack/solid-router':
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -2112,7 +2175,7 @@ importers:
e2e/solid-start/scroll-restoration:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -2173,7 +2236,7 @@ importers:
e2e/solid-start/server-functions:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -2237,7 +2300,7 @@ importers:
e2e/solid-start/website:
dependencies:
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5191,7 +5254,7 @@ importers:
examples/solid/basic:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5231,7 +5294,7 @@ importers:
examples/solid/basic-devtools-panel:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5265,7 +5328,7 @@ importers:
examples/solid/basic-file-based:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5305,7 +5368,7 @@ importers:
examples/solid/basic-non-nested-devtools:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5351,7 +5414,7 @@ importers:
specifier: ^5.66.0
version: 5.66.0(@tanstack/solid-query@5.66.0(solid-js@1.9.5))(solid-js@1.9.5)
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5394,7 +5457,7 @@ importers:
specifier: ^5.66.0
version: 5.66.0(@tanstack/solid-query@5.66.0(solid-js@1.9.5))(solid-js@1.9.5)
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5434,7 +5497,7 @@ importers:
examples/solid/kitchen-sink-file-based:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5477,7 +5540,7 @@ importers:
examples/solid/quickstart-file-based:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5517,7 +5580,7 @@ importers:
examples/solid/start-bare:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5566,7 +5629,7 @@ importers:
examples/solid/start-basic:
dependencies:
'@tanstack/solid-router':
- specifier: ^1.114.29
+ specifier: workspace:*
version: link:../../../packages/solid-router
'@tanstack/solid-router-devtools':
specifier: workspace:^
@@ -5609,6 +5672,58 @@ importers:
specifier: ^5.1.4
version: 5.1.4(typescript@5.8.2)(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+ examples/solid/start-basic-solid-query:
+ dependencies:
+ '@tanstack/solid-query':
+ specifier: ^5.66.0
+ version: 5.66.0(solid-js@1.9.5)
+ '@tanstack/solid-query-devtools':
+ specifier: ^5.66.0
+ version: 5.66.0(@tanstack/solid-query@5.66.0(solid-js@1.9.5))(solid-js@1.9.5)
+ '@tanstack/solid-router':
+ specifier: workspace:*
+ version: link:../../../packages/solid-router
+ '@tanstack/solid-router-devtools':
+ specifier: workspace:^
+ version: link:../../../packages/solid-router-devtools
+ '@tanstack/solid-router-with-query':
+ specifier: workspace:*
+ version: link:../../../packages/solid-router-with-query
+ '@tanstack/solid-start':
+ specifier: workspace:*
+ version: link:../../../packages/solid-start
+ redaxios:
+ specifier: ^0.5.1
+ version: 0.5.1
+ solid-js:
+ specifier: ^1.9.5
+ version: 1.9.5
+ tailwind-merge:
+ specifier: ^2.6.0
+ version: 2.6.0
+ vinxi:
+ specifier: 0.5.3
+ version: 0.5.3(@types/node@22.13.4)(db0@0.2.3)(ioredis@5.4.2)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(typescript@5.8.2)(yaml@2.7.0)
+ devDependencies:
+ '@types/node':
+ specifier: ^22.5.4
+ version: 22.13.4
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.4.20(postcss@8.5.3)
+ postcss:
+ specifier: ^8.5.1
+ version: 8.5.3
+ tailwindcss:
+ specifier: ^3.4.17
+ version: 3.4.17
+ typescript:
+ specifier: ^5.7.2
+ version: 5.8.2
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.8.2)(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+
packages/arktype-adapter:
devDependencies:
'@tanstack/react-router':
@@ -6525,7 +6640,7 @@ importers:
specifier: workspace:^
version: link:../router-devtools-core
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../solid-router
solid-js:
specifier: ^1.9.5
@@ -6535,6 +6650,24 @@ importers:
specifier: ^2.11.6
version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+ packages/solid-router-with-query:
+ devDependencies:
+ '@tanstack/router-core':
+ specifier: workspace:*
+ version: link:../router-core
+ '@tanstack/solid-query':
+ specifier: '>=5.66.0'
+ version: 5.66.0(solid-js@1.9.5)
+ '@tanstack/solid-router':
+ specifier: workspace:*
+ version: link:../solid-router
+ solid-js:
+ specifier: '>=1.9.5'
+ version: 1.9.5
+ vite-plugin-solid:
+ specifier: ^2.11.6
+ version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.1.2(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+
packages/solid-start:
dependencies:
'@tanstack/solid-start-client':
@@ -6581,7 +6714,7 @@ importers:
specifier: workspace:*
version: link:../router-core
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../solid-router
'@tanstack/start-client-core':
specifier: workspace:*
@@ -6740,7 +6873,7 @@ importers:
specifier: workspace:*
version: link:../router-core
'@tanstack/solid-router':
- specifier: workspace:^
+ specifier: workspace:*
version: link:../solid-router
'@tanstack/start-client-core':
specifier: workspace:*
diff --git a/scripts/publish.js b/scripts/publish.js
index 1d55ca6ddc..e1f9c8b3f1 100644
--- a/scripts/publish.js
+++ b/scripts/publish.js
@@ -28,6 +28,10 @@ await publish({
name: '@tanstack/react-router-with-query',
packageDir: 'packages/react-router-with-query',
},
+ {
+ name: '@tanstack/solid-router-with-query',
+ packageDir: 'packages/solid-router-with-query',
+ },
{
name: '@tanstack/zod-adapter',
packageDir: 'packages/zod-adapter',