├── .prettierignore ├── postcss.config.mjs ├── public ├── favicon.ico ├── favicon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png └── site.webmanifest ├── tailwind.config.mjs ├── src ├── api.ts ├── global-middleware.ts ├── components │ ├── PostError.tsx │ ├── UserError.tsx │ ├── NotFound.tsx │ └── DefaultCatchBoundary.tsx ├── routes │ ├── redirect.tsx │ ├── posts.index.tsx │ ├── index.tsx │ ├── _pathlessLayout │ │ ├── _nested-layout │ │ │ ├── route-a.tsx │ │ │ └── route-b.tsx │ │ └── _nested-layout.tsx │ ├── _pathlessLayout.tsx │ ├── api │ │ ├── users.ts │ │ └── users.$id.ts │ ├── posts_.$postId.deep.tsx │ ├── posts.$postId.tsx │ ├── posts.route.tsx │ ├── deferred.tsx │ └── __root.tsx ├── client.tsx ├── ssr.tsx ├── styles │ └── app.css ├── utils │ ├── cf-bindings.ts │ ├── seo.ts │ ├── loggingMiddleware.tsx │ └── posts.tsx ├── router.tsx └── routeTree.gen.ts ├── .vscode └── settings.json ├── .gitignore ├── app.config.ts ├── tsconfig.json ├── wrangler.jsonc ├── package.json └── README.md /.prettierignore: -------------------------------------------------------------------------------- 1 | **/build 2 | **/public 3 | pnpm-lock.yaml 4 | routeTree.gen.ts -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./src/**/*.{js,jsx,ts,tsx}'], 4 | } 5 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare-workers-v0/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createStartAPIHandler, 3 | defaultAPIFileRouteHandler, 4 | } from '@tanstack/react-start/api' 5 | 6 | export default createStartAPIHandler(defaultAPIFileRouteHandler) 7 | -------------------------------------------------------------------------------- /src/global-middleware.ts: -------------------------------------------------------------------------------- 1 | import { registerGlobalMiddleware } from '@tanstack/react-start' 2 | import { logMiddleware } from './utils/loggingMiddleware' 3 | 4 | registerGlobalMiddleware({ 5 | middleware: [logMiddleware], 6 | }) 7 | -------------------------------------------------------------------------------- /src/components/PostError.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router' 2 | 3 | export function PostErrorComponent({ error }: ErrorComponentProps) { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /src/components/UserError.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router' 2 | 3 | export function UserErrorComponent({ error }: ErrorComponentProps) { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.watcherExclude": { 3 | "**/routeTree.gen.ts": true 4 | }, 5 | "search.exclude": { 6 | "**/routeTree.gen.ts": true 7 | }, 8 | "files.readonlyInclude": { 9 | "**/routeTree.gen.ts": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/routes/redirect.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute, redirect } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/redirect')({ 4 | beforeLoad: async () => { 5 | throw redirect({ 6 | to: '/posts', 7 | }) 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /src/routes/posts.index.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/posts/')({ 4 | component: PostsIndexComponent, 5 | }) 6 | 7 | function PostsIndexComponent() { 8 | return
Select a post.
9 | } 10 | -------------------------------------------------------------------------------- /src/client.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | import { hydrateRoot } from 'react-dom/client' 3 | import { StartClient } from '@tanstack/react-start' 4 | import { createRouter } from './router' 5 | 6 | const router = createRouter() 7 | 8 | hydrateRoot(document, ) 9 | -------------------------------------------------------------------------------- /src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/')({ 4 | component: Home, 5 | }) 6 | 7 | function Home() { 8 | return ( 9 |
10 |

Welcome Home!!!

11 |
12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/_pathlessLayout/_nested-layout/route-a.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( 4 | { 5 | component: LayoutAComponent, 6 | }, 7 | ) 8 | 9 | function LayoutAComponent() { 10 | return
I'm A!
11 | } 12 | -------------------------------------------------------------------------------- /src/routes/_pathlessLayout/_nested-layout/route-b.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( 4 | { 5 | component: LayoutBComponent, 6 | }, 7 | ) 8 | 9 | function LayoutBComponent() { 10 | return
I'm B!
11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | yarn.lock 4 | 5 | .DS_Store 6 | .cache 7 | .env 8 | .vercel 9 | .output 10 | .vinxi 11 | 12 | /build/ 13 | /api/ 14 | /server/build 15 | /public/build 16 | .vinxi 17 | # Sentry Config File 18 | .env.sentry-build-plugin 19 | /test-results/ 20 | /playwright-report/ 21 | /blob-report/ 22 | /playwright/.cache/ 23 | 24 | .wrangler 25 | .dev.vars 26 | -------------------------------------------------------------------------------- /src/ssr.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | import { 3 | createStartHandler, 4 | defaultStreamHandler, 5 | } from '@tanstack/react-start/server' 6 | import { getRouterManifest } from '@tanstack/react-start/router-manifest' 7 | 8 | import { createRouter } from './router' 9 | 10 | export default createStartHandler({ 11 | createRouter, 12 | getRouterManifest, 13 | })(defaultStreamHandler) 14 | -------------------------------------------------------------------------------- /src/routes/_pathlessLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet, createFileRoute } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/_pathlessLayout')({ 4 | component: LayoutComponent, 5 | }) 6 | 7 | function LayoutComponent() { 8 | return ( 9 |
10 |
I'm a layout
11 |
12 | 13 |
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/styles/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | html { 7 | color-scheme: light dark; 8 | } 9 | 10 | * { 11 | @apply border-gray-200 dark:border-gray-800; 12 | } 13 | 14 | html, 15 | body { 16 | @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; 17 | } 18 | 19 | .using-mouse * { 20 | outline: none !important; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/cf-bindings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Will only work when being accessed on the server. Obviously, CF bindings are not available in the browser. 3 | * @returns 4 | */ 5 | export function getBindings() { 6 | if (import.meta.env.DEV) { 7 | const proxyPromise = import("wrangler").then(({ getPlatformProxy }) => 8 | getPlatformProxy().then((proxy) => proxy.env), 9 | ); 10 | return proxyPromise as unknown as CloudflareBindings; 11 | } 12 | 13 | return process.env as unknown as CloudflareBindings; 14 | } 15 | -------------------------------------------------------------------------------- /app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@tanstack/react-start/config"; 2 | import tsConfigPaths from "vite-tsconfig-paths"; 3 | import { cloudflare } from "unenv"; 4 | import nitroCloudflareBindings from "nitro-cloudflare-dev"; 5 | 6 | export default defineConfig({ 7 | server: { 8 | preset: "cloudflare-module", 9 | unenv: cloudflare, 10 | modules: [nitroCloudflareBindings], 11 | }, 12 | tsr: { 13 | appDirectory: "src", 14 | }, 15 | vite: { 16 | plugins: [ 17 | tsConfigPaths({ 18 | projects: ["./tsconfig.json"], 19 | }), 20 | ], 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["**/*.ts", "**/*.tsx"], 3 | "compilerOptions": { 4 | "strict": true, 5 | "esModuleInterop": true, 6 | "jsx": "react-jsx", 7 | "module": "ESNext", 8 | "moduleResolution": "Bundler", 9 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 10 | "isolatedModules": true, 11 | "resolveJsonModule": true, 12 | "skipLibCheck": true, 13 | "target": "ES2022", 14 | "allowJs": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "~/*": ["./src/*"] 19 | }, 20 | "noEmit": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/routes/api/users.ts: -------------------------------------------------------------------------------- 1 | import { json } from "@tanstack/react-start"; 2 | import { createAPIFileRoute } from "@tanstack/react-start/api"; 3 | import { getBindings } from "~/utils/cf-bindings"; 4 | 5 | export const APIRoute = createAPIFileRoute("/api/users")({ 6 | GET: async ({ request }) => { 7 | const bindings = getBindings(); 8 | const deferredCount = await bindings.CACHE.get("mykey"); 9 | const currentCount = deferredCount ? parseInt(deferredCount) : 0; 10 | const newCount = currentCount + 1; 11 | await bindings.CACHE.put("queryCount2", newCount.toString()); 12 | return json({ 13 | deferredCount, 14 | }); 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /src/router.tsx: -------------------------------------------------------------------------------- 1 | import { createRouter as createTanStackRouter } from '@tanstack/react-router' 2 | import { routeTree } from './routeTree.gen' 3 | import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' 4 | import { NotFound } from './components/NotFound' 5 | 6 | export function createRouter() { 7 | const router = createTanStackRouter({ 8 | routeTree, 9 | defaultPreload: 'intent', 10 | defaultErrorComponent: DefaultCatchBoundary, 11 | defaultNotFoundComponent: () => , 12 | scrollRestoration: true, 13 | }) 14 | 15 | return router 16 | } 17 | 18 | declare module '@tanstack/react-router' { 19 | interface Register { 20 | router: ReturnType 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /wrangler.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/wrangler/config-schema.json", 3 | "name": "tanstack-start-on-workers-v0", 4 | "main": ".output/server/index.mjs", 5 | "compatibility_date": "2025-04-10", 6 | "compatibility_flags": ["nodejs_compat"], 7 | "assets": { 8 | "directory": ".output/public", 9 | }, 10 | "observability": { 11 | "enabled": true, 12 | }, 13 | "kv_namespaces": [ 14 | { 15 | "binding": "CACHE", 16 | "id": "243100874d9447bfa10a820bcbf51ede", 17 | }, 18 | ], 19 | "durable_objects": { 20 | "bindings": [ 21 | { 22 | "class_name": "Counter", 23 | "name": "DO", 24 | "script_name": "durable-object-counter", 25 | }, 26 | ], 27 | }, 28 | "routes": [ 29 | { 30 | "custom_domain": true, 31 | "pattern": "tanstackstart.backpine.com", 32 | }, 33 | ], 34 | } 35 | -------------------------------------------------------------------------------- /src/components/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from '@tanstack/react-router' 2 | 3 | export function NotFound({ children }: { children?: any }) { 4 | return ( 5 |
6 |
7 | {children ||

The page you are looking for does not exist.

} 8 |
9 |

10 | 16 | 20 | Start Over 21 | 22 |

23 |
24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/routes/_pathlessLayout/_nested-layout.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Outlet, createFileRoute } from '@tanstack/react-router' 2 | 3 | export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ 4 | component: LayoutComponent, 5 | }) 6 | 7 | function LayoutComponent() { 8 | return ( 9 |
10 |
I'm a nested layout
11 |
12 | 18 | Go to route A 19 | 20 | 26 | Go to route B 27 | 28 |
29 |
30 | 31 |
32 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/routes/api/users.$id.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@tanstack/react-start' 2 | import { createAPIFileRoute } from '@tanstack/react-start/api' 3 | import type { User } from '../../utils/users' 4 | 5 | export const APIRoute = createAPIFileRoute('/api/users/$id')({ 6 | GET: async ({ request, params }) => { 7 | console.info(`Fetching users by id=${params.id}... @`, request.url) 8 | try { 9 | const res = await fetch( 10 | 'https://jsonplaceholder.typicode.com/users/' + params.id, 11 | ) 12 | if (!res.ok) { 13 | throw new Error('Failed to fetch user') 14 | } 15 | 16 | const user = (await res.json()) as User 17 | 18 | return json({ 19 | id: user.id, 20 | name: user.name, 21 | email: user.email, 22 | }) 23 | } catch (e) { 24 | console.error(e) 25 | return json({ error: 'User not found' }, { status: 404 }) 26 | } 27 | }, 28 | }) 29 | -------------------------------------------------------------------------------- /src/routes/posts_.$postId.deep.tsx: -------------------------------------------------------------------------------- 1 | import { Link, createFileRoute } from '@tanstack/react-router' 2 | import { fetchPost } from '../utils/posts' 3 | import { PostErrorComponent } from '~/components/PostError' 4 | 5 | export const Route = createFileRoute('/posts_/$postId/deep')({ 6 | loader: async ({ params: { postId } }) => 7 | fetchPost({ 8 | data: postId, 9 | }), 10 | errorComponent: PostErrorComponent, 11 | component: PostDeepComponent, 12 | }) 13 | 14 | function PostDeepComponent() { 15 | const post = Route.useLoaderData() 16 | 17 | return ( 18 |
19 | 23 | ← All Posts 24 | 25 |

{post.title}

26 |
{post.body}
27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/seo.ts: -------------------------------------------------------------------------------- 1 | export const seo = ({ 2 | title, 3 | description, 4 | keywords, 5 | image, 6 | }: { 7 | title: string 8 | description?: string 9 | image?: string 10 | keywords?: string 11 | }) => { 12 | const tags = [ 13 | { title }, 14 | { name: 'description', content: description }, 15 | { name: 'keywords', content: keywords }, 16 | { name: 'twitter:title', content: title }, 17 | { name: 'twitter:description', content: description }, 18 | { name: 'twitter:creator', content: '@tannerlinsley' }, 19 | { name: 'twitter:site', content: '@tannerlinsley' }, 20 | { name: 'og:type', content: 'website' }, 21 | { name: 'og:title', content: title }, 22 | { name: 'og:description', content: description }, 23 | ...(image 24 | ? [ 25 | { name: 'twitter:image', content: image }, 26 | { name: 'twitter:card', content: 'summary_large_image' }, 27 | { name: 'og:image', content: image }, 28 | ] 29 | : []), 30 | ] 31 | 32 | return tags 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tanstack-start-example-basic", 3 | "private": true, 4 | "sideEffects": false, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vinxi dev", 8 | "build": "vinxi build", 9 | "start": "vinxi start", 10 | "deploy": "npm run build && wrangler deploy", 11 | "cf-typegen": "wrangler types --env-interface CloudflareBindings" 12 | }, 13 | "dependencies": { 14 | "@tanstack/react-router": "^1.116.0", 15 | "@tanstack/react-router-devtools": "^1.116.0", 16 | "@tanstack/react-start": "^1.116.1", 17 | "react": "^19.0.0", 18 | "react-dom": "^19.0.0", 19 | "tailwind-merge": "^2.6.0", 20 | "vinxi": "0.5.3" 21 | }, 22 | "devDependencies": { 23 | "@types/node": "^22.5.4", 24 | "@types/react": "^19.0.8", 25 | "@types/react-dom": "^19.0.3", 26 | "autoprefixer": "^10.4.20", 27 | "nitro-cloudflare-dev": "^0.2.2", 28 | "postcss": "^8.5.1", 29 | "tailwindcss": "^3.4.17", 30 | "typescript": "^5.7.2", 31 | "unenv": "^1.10.0", 32 | "vite": "^6.3.2", 33 | "vite-tsconfig-paths": "^5.1.4", 34 | "wrangler": "^4.12.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/routes/posts.$postId.tsx: -------------------------------------------------------------------------------- 1 | import { Link, createFileRoute } from '@tanstack/react-router' 2 | import { fetchPost } from '../utils/posts' 3 | import { NotFound } from '~/components/NotFound' 4 | import { PostErrorComponent } from '~/components/PostError' 5 | 6 | export const Route = createFileRoute('/posts/$postId')({ 7 | loader: ({ params: { postId } }) => fetchPost({ data: postId }), 8 | errorComponent: PostErrorComponent, 9 | component: PostComponent, 10 | notFoundComponent: () => { 11 | return Post not found 12 | }, 13 | }) 14 | 15 | function PostComponent() { 16 | const post = Route.useLoaderData() 17 | 18 | return ( 19 |
20 |

{post.title}

21 |
{post.body}
22 | 30 | Deep View 31 | 32 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/loggingMiddleware.tsx: -------------------------------------------------------------------------------- 1 | import { createMiddleware } from '@tanstack/react-start' 2 | 3 | const preLogMiddleware = createMiddleware() 4 | .client(async (ctx) => { 5 | const clientTime = new Date() 6 | 7 | return ctx.next({ 8 | context: { 9 | clientTime, 10 | }, 11 | sendContext: { 12 | clientTime, 13 | }, 14 | }) 15 | }) 16 | .server(async (ctx) => { 17 | const serverTime = new Date() 18 | 19 | return ctx.next({ 20 | sendContext: { 21 | serverTime, 22 | durationToServer: 23 | serverTime.getTime() - ctx.context.clientTime.getTime(), 24 | }, 25 | }) 26 | }) 27 | 28 | export const logMiddleware = createMiddleware() 29 | .middleware([preLogMiddleware]) 30 | .client(async (ctx) => { 31 | const res = await ctx.next() 32 | 33 | const now = new Date() 34 | console.log('Client Req/Res:', { 35 | duration: res.context.clientTime.getTime() - now.getTime(), 36 | durationToServer: res.context.durationToServer, 37 | durationFromServer: now.getTime() - res.context.serverTime.getTime(), 38 | }) 39 | 40 | return res 41 | }) 42 | -------------------------------------------------------------------------------- /src/utils/posts.tsx: -------------------------------------------------------------------------------- 1 | import { notFound } from '@tanstack/react-router' 2 | import { createServerFn } from '@tanstack/react-start' 3 | 4 | export type PostType = { 5 | id: string 6 | title: string 7 | body: string 8 | } 9 | 10 | export const fetchPost = createServerFn({ method: 'GET' }) 11 | .validator((d: string) => d) 12 | .handler(async ({ data }) => { 13 | console.info(`Fetching post with id ${data}...`) 14 | const res = await fetch( 15 | `https://jsonplaceholder.typicode.com/posts/${data}`, 16 | ) 17 | if (!res.ok) { 18 | if (res.status === 404) { 19 | throw notFound() 20 | } 21 | 22 | throw new Error('Failed to fetch post') 23 | } 24 | 25 | const post = (await res.json()) as PostType 26 | 27 | return post 28 | }) 29 | 30 | export const fetchPosts = createServerFn({ method: 'GET' }).handler( 31 | async () => { 32 | console.info('Fetching posts...') 33 | const res = await fetch('https://jsonplaceholder.typicode.com/posts') 34 | if (!res.ok) { 35 | throw new Error('Failed to fetch posts') 36 | } 37 | 38 | const posts = (await res.json()) as Array 39 | 40 | return posts 41 | }, 42 | ) 43 | -------------------------------------------------------------------------------- /src/routes/posts.route.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Outlet, createFileRoute } from '@tanstack/react-router' 2 | import { fetchPosts } from '../utils/posts' 3 | 4 | export const Route = createFileRoute('/posts')({ 5 | loader: async () => fetchPosts(), 6 | component: PostsLayoutComponent, 7 | }) 8 | 9 | function PostsLayoutComponent() { 10 | const posts = Route.useLoaderData() 11 | 12 | return ( 13 |
14 |
    15 | {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( 16 | (post) => { 17 | return ( 18 |
  • 19 | 27 |
    {post.title.substring(0, 20)}
    28 | 29 |
  • 30 | ) 31 | }, 32 | )} 33 |
34 |
35 | 36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/components/DefaultCatchBoundary.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ErrorComponent, 3 | Link, 4 | rootRouteId, 5 | useMatch, 6 | useRouter, 7 | } from '@tanstack/react-router' 8 | import type { ErrorComponentProps } from '@tanstack/react-router' 9 | 10 | export function DefaultCatchBoundary({ error }: ErrorComponentProps) { 11 | const router = useRouter() 12 | const isRoot = useMatch({ 13 | strict: false, 14 | select: (state) => state.id === rootRouteId, 15 | }) 16 | 17 | console.error('DefaultCatchBoundary Error:', error) 18 | 19 | return ( 20 |
21 | 22 |
23 | 31 | {isRoot ? ( 32 | 36 | Home 37 | 38 | ) : ( 39 | { 43 | e.preventDefault() 44 | window.history.back() 45 | }} 46 | > 47 | Go Back 48 | 49 | )} 50 |
51 |
52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/routes/deferred.tsx: -------------------------------------------------------------------------------- 1 | import { Await, createFileRoute } from "@tanstack/react-router"; 2 | import { createServerFn } from "@tanstack/react-start"; 3 | import { Suspense, useState } from "react"; 4 | import { getBindings } from "~/utils/cf-bindings"; 5 | 6 | const personServerFn = createServerFn({ method: "GET" }) 7 | .validator((d: string) => d) 8 | .handler(({ data: name }) => { 9 | return { name, randomNumber: Math.floor(Math.random() * 100) }; 10 | }); 11 | 12 | const slowServerFn = createServerFn({ method: "GET" }) 13 | .validator((d: string) => d) 14 | .handler(async ({ data: name }) => { 15 | const bindings = getBindings(); 16 | const cache = bindings.CACHE; 17 | const queryCount = (await cache.get("queryCount")) || "0"; 18 | await cache.put("queryCount", String(Number(queryCount) + 1)); 19 | await new Promise((r) => setTimeout(r, 1000)); 20 | return { name, randomNumber: Math.floor(Math.random() * 100), queryCount }; 21 | }); 22 | 23 | export const Route = createFileRoute("/deferred")({ 24 | loader: async () => { 25 | return { 26 | deferredStuff: new Promise((r) => 27 | setTimeout(() => r("Hello deferred!"), 2000), 28 | ), 29 | deferredPerson: slowServerFn({ data: "Tanner Linsley" }), 30 | person: await personServerFn({ data: "John Doe" }), 31 | }; 32 | }, 33 | component: Deferred, 34 | }); 35 | 36 | function Deferred() { 37 | const [count, setCount] = useState(0); 38 | const { deferredStuff, deferredPerson, person } = Route.useLoaderData(); 39 | 40 | return ( 41 |
42 |
43 | {person.name} - {person.randomNumber} 44 |
45 | Loading person...
}> 46 | ( 49 |
50 | {data.name} - {data.randomNumber} - Cache hit Count:{" "} 51 | {data.queryCount} 👈 From Cloudflare KV 52 |
53 | )} 54 | /> 55 | 56 | Loading stuff...}> 57 |

{data}

} 60 | /> 61 |
62 |
Count: {count}
63 |
64 | 65 |
66 | 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /src/routes/__root.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | HeadContent, 3 | Link, 4 | Outlet, 5 | Scripts, 6 | createRootRoute, 7 | } from "@tanstack/react-router"; 8 | import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; 9 | import * as React from "react"; 10 | import { DefaultCatchBoundary } from "~/components/DefaultCatchBoundary"; 11 | import { NotFound } from "~/components/NotFound"; 12 | import appCss from "~/styles/app.css?url"; 13 | import { seo } from "~/utils/seo"; 14 | 15 | export const Route = createRootRoute({ 16 | head: () => ({ 17 | meta: [ 18 | { 19 | charSet: "utf-8", 20 | }, 21 | { 22 | name: "viewport", 23 | content: "width=device-width, initial-scale=1", 24 | }, 25 | ...seo({ 26 | title: 27 | "TanStack Start | Type-Safe, Client-First, Full-Stack React Framework", 28 | description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, 29 | }), 30 | ], 31 | links: [ 32 | { rel: "stylesheet", href: appCss }, 33 | { 34 | rel: "apple-touch-icon", 35 | sizes: "180x180", 36 | href: "/apple-touch-icon.png", 37 | }, 38 | { 39 | rel: "icon", 40 | type: "image/png", 41 | sizes: "32x32", 42 | href: "/favicon-32x32.png", 43 | }, 44 | { 45 | rel: "icon", 46 | type: "image/png", 47 | sizes: "16x16", 48 | href: "/favicon-16x16.png", 49 | }, 50 | { rel: "manifest", href: "/site.webmanifest", color: "#fffff" }, 51 | { rel: "icon", href: "/favicon.ico" }, 52 | ], 53 | }), 54 | errorComponent: (props) => { 55 | return ( 56 | 57 | 58 | 59 | ); 60 | }, 61 | notFoundComponent: () => , 62 | component: RootComponent, 63 | }); 64 | 65 | function RootComponent() { 66 | return ( 67 | 68 | 69 | 70 | ); 71 | } 72 | 73 | function RootDocument({ children }: { children: React.ReactNode }) { 74 | return ( 75 | 76 | 77 | 78 | 79 | 80 |
81 | 88 | Home 89 | {" "} 90 | 96 | Posts 97 | {" "} 98 | 104 | Pathless Layout 105 | {" "} 106 | 112 | Deferred 113 | {" "} 114 | 121 | This Route Does Not Exist 122 | 123 |
124 |
125 | {children} 126 | 127 | 128 | 129 | 130 | ); 131 | } 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **⚠️ IMPORTANT NOTICE** 2 | > 3 | > **TanStack Start Beta is now live!** This repository and guide are now **outdated**. 4 | > 5 | > For the latest implementation, please follow the updated guide here: 6 | > **https://github.com/backpine/tanstack-start-beta-on-cloudflare** 7 | ## Tanstack Start on Workers v0 8 | 9 | ### Step 1: Create project from Tanstack Start quickstart 10 | You can start a new project using [Tanstack Start's quickstart](https://tanstack.com/start/latest/docs/framework/react/quick-start). 11 | 12 | ### Step 2: Update the app.config.ts file with Cloudflare preset 13 | 14 | ```ts 15 | import { defineConfig } from "@tanstack/react-start/config"; 16 | import tsConfigPaths from "vite-tsconfig-paths"; 17 | import { cloudflare } from "unenv"; 18 | 19 | export default defineConfig({ 20 | server: { 21 | preset: "cloudflare-module", 22 | unenv: cloudflare, 23 | }, 24 | tsr: { 25 | appDirectory: "src", 26 | }, 27 | vite: { 28 | plugins: [ 29 | tsConfigPaths({ 30 | projects: ["./tsconfig.json"], 31 | }), 32 | ], 33 | }, 34 | }); 35 | ``` 36 | The `cloudflare-module` preset attempts to make the build compatible with Cloudflare's worker runtime. It outputs the build into the `.output` directory. 37 | 38 | The Cloudflare entrypoint is in the `.output/server/index.mjs` file. All assets are in `.output/public`. 39 | 40 | 41 | ### Step 3: Install and configure nitroCloudflareBindings module in the app.config.ts file 42 | 43 | ```ts 44 | import { defineConfig } from "@tanstack/react-start/config"; 45 | import tsConfigPaths from "vite-tsconfig-paths"; 46 | import { cloudflare } from "unenv"; 47 | import nitroCloudflareBindings from "nitro-cloudflare-dev"; 48 | 49 | export default defineConfig({ 50 | server: { 51 | preset: "cloudflare-module", 52 | unenv: cloudflare, 53 | modules: [nitroCloudflareBindings], 54 | }, 55 | tsr: { 56 | appDirectory: "src", 57 | }, 58 | vite: { 59 | plugins: [ 60 | tsConfigPaths({ 61 | projects: ["./tsconfig.json"], 62 | }), 63 | ], 64 | }, 65 | }); 66 | ``` 67 | This plugin provides your app access to Cloudflare's Worker bindings when running locally. It mirrors the production bindings so you can access the Cloudflare Dev resources locally such as KV, D1, and R2. 68 | 69 | If you change your dev script to `wrangler dev`, you can access the bindings in server components from `process.env`. (That said, I found a workaround that allows you to still use vinxi, which will be discussed later.) 70 | 71 | ### Step 4: Configure your wrangler config in wrangler.jsonc 72 | ``` 73 | { 74 | "$schema": "node_modules/wrangler/config-schema.json", 75 | "name": "tanstack-start-on-workers-v0", 76 | "main": ".output/server/index.mjs", 77 | "compatibility_date": "2025-04-10", 78 | "compatibility_flags": ["nodejs_compat"], 79 | "assets": { 80 | "directory": ".output/public", 81 | }, 82 | "observability": { 83 | "enabled": true, 84 | }, 85 | "kv_namespaces": [ 86 | { 87 | "binding": "CACHE", 88 | "id": "5d8a8ea387f348f7a156ffd98384998c", 89 | }, 90 | ], 91 | "routes": [ 92 | { 93 | "custom_domain": true, 94 | "pattern": "tanstackstart.backpine.com", 95 | }, 96 | ], 97 | } 98 | ``` 99 | 100 | ### Step 5: Add wrangler commands to scripts in package.json 101 | ``` 102 | "deploy": "npm run build && wrangler deploy", 103 | "cf-typegen": "wrangler types --env-interface CloudflareBindings" 104 | ``` 105 | Run `npm run cf-typegen` to generate the types for the Cloudflare bindings. 106 | 107 | ### Step 6: Deploy your app to ensure all is working 108 | Run `npm run deploy` to deploy your app to Cloudflare Workers. 109 | 110 | 111 | ### Step 7: Define a helper method to get access to the Cloudflare bindings 112 | ```ts 113 | /** 114 | * Will only work when being accessed on the server. Obviously, CF bindings are not available in the browser. 115 | * @returns 116 | */ 117 | export function getBindings() { 118 | if (import.meta.env.DEV) { 119 | const proxyPromise = import("wrangler").then(({ getPlatformProxy }) => 120 | getPlatformProxy().then((proxy) => proxy.env), 121 | ); 122 | return proxyPromise as unknown as CloudflareBindings; 123 | } 124 | 125 | return process.env as unknown as CloudflareBindings; 126 | } 127 | ``` 128 | 129 | For CF apps built with Nitro, the CloudflareBindings can be accessed from process.env. There are a few other ways of accessing the bindings, but I ran across some issues with SSR when using getEvent().context.cloudflare.env. 130 | 131 | I'm aware that this is not the most elegant solution, but it works for now. 132 | 133 | To get your bindings to work locally with vinxi, you can access the bindings using the getPlatformProxy method from wrangler. This logic is placed under a check if import.meta.env.DEV is true. 134 | 135 | To set the DEV environment variable in your local environment, use the `.dev.vars` file. 136 | 137 | ``` 138 | # .dev.vars 139 | DEV=true 140 | ``` 141 | 142 | ### Step 8: Use the getBindings method to access the Cloudflare bindings in server side logic 143 | ```ts 144 | const bindings = getBindings(); 145 | const cache = bindings.CACHE; 146 | const queryCount = (await cache.get("queryCount")) || "0"; 147 | await cache.put("queryCount", String(Number(queryCount) + 1)); 148 | ``` 149 | 150 | This should work both locally and on Cloudflare Workers. 151 | -------------------------------------------------------------------------------- /src/routeTree.gen.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // @ts-nocheck 4 | 5 | // noinspection JSUnusedGlobalSymbols 6 | 7 | // This file was automatically generated by TanStack Router. 8 | // You should NOT make any changes in this file as it will be overwritten. 9 | // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. 10 | 11 | // Import Routes 12 | 13 | import { Route as rootRoute } from './routes/__root' 14 | import { Route as RedirectImport } from './routes/redirect' 15 | import { Route as DeferredImport } from './routes/deferred' 16 | import { Route as PathlessLayoutImport } from './routes/_pathlessLayout' 17 | import { Route as PostsRouteImport } from './routes/posts.route' 18 | import { Route as IndexImport } from './routes/index' 19 | import { Route as PostsIndexImport } from './routes/posts.index' 20 | import { Route as PostsPostIdImport } from './routes/posts.$postId' 21 | import { Route as PathlessLayoutNestedLayoutImport } from './routes/_pathlessLayout/_nested-layout' 22 | import { Route as PostsPostIdDeepImport } from './routes/posts_.$postId.deep' 23 | import { Route as PathlessLayoutNestedLayoutRouteBImport } from './routes/_pathlessLayout/_nested-layout/route-b' 24 | import { Route as PathlessLayoutNestedLayoutRouteAImport } from './routes/_pathlessLayout/_nested-layout/route-a' 25 | 26 | // Create/Update Routes 27 | 28 | const RedirectRoute = RedirectImport.update({ 29 | id: '/redirect', 30 | path: '/redirect', 31 | getParentRoute: () => rootRoute, 32 | } as any) 33 | 34 | const DeferredRoute = DeferredImport.update({ 35 | id: '/deferred', 36 | path: '/deferred', 37 | getParentRoute: () => rootRoute, 38 | } as any) 39 | 40 | const PathlessLayoutRoute = PathlessLayoutImport.update({ 41 | id: '/_pathlessLayout', 42 | getParentRoute: () => rootRoute, 43 | } as any) 44 | 45 | const PostsRouteRoute = PostsRouteImport.update({ 46 | id: '/posts', 47 | path: '/posts', 48 | getParentRoute: () => rootRoute, 49 | } as any) 50 | 51 | const IndexRoute = IndexImport.update({ 52 | id: '/', 53 | path: '/', 54 | getParentRoute: () => rootRoute, 55 | } as any) 56 | 57 | const PostsIndexRoute = PostsIndexImport.update({ 58 | id: '/', 59 | path: '/', 60 | getParentRoute: () => PostsRouteRoute, 61 | } as any) 62 | 63 | const PostsPostIdRoute = PostsPostIdImport.update({ 64 | id: '/$postId', 65 | path: '/$postId', 66 | getParentRoute: () => PostsRouteRoute, 67 | } as any) 68 | 69 | const PathlessLayoutNestedLayoutRoute = PathlessLayoutNestedLayoutImport.update( 70 | { 71 | id: '/_nested-layout', 72 | getParentRoute: () => PathlessLayoutRoute, 73 | } as any, 74 | ) 75 | 76 | const PostsPostIdDeepRoute = PostsPostIdDeepImport.update({ 77 | id: '/posts_/$postId/deep', 78 | path: '/posts/$postId/deep', 79 | getParentRoute: () => rootRoute, 80 | } as any) 81 | 82 | const PathlessLayoutNestedLayoutRouteBRoute = 83 | PathlessLayoutNestedLayoutRouteBImport.update({ 84 | id: '/route-b', 85 | path: '/route-b', 86 | getParentRoute: () => PathlessLayoutNestedLayoutRoute, 87 | } as any) 88 | 89 | const PathlessLayoutNestedLayoutRouteARoute = 90 | PathlessLayoutNestedLayoutRouteAImport.update({ 91 | id: '/route-a', 92 | path: '/route-a', 93 | getParentRoute: () => PathlessLayoutNestedLayoutRoute, 94 | } as any) 95 | 96 | // Populate the FileRoutesByPath interface 97 | 98 | declare module '@tanstack/react-router' { 99 | interface FileRoutesByPath { 100 | '/': { 101 | id: '/' 102 | path: '/' 103 | fullPath: '/' 104 | preLoaderRoute: typeof IndexImport 105 | parentRoute: typeof rootRoute 106 | } 107 | '/posts': { 108 | id: '/posts' 109 | path: '/posts' 110 | fullPath: '/posts' 111 | preLoaderRoute: typeof PostsRouteImport 112 | parentRoute: typeof rootRoute 113 | } 114 | '/_pathlessLayout': { 115 | id: '/_pathlessLayout' 116 | path: '' 117 | fullPath: '' 118 | preLoaderRoute: typeof PathlessLayoutImport 119 | parentRoute: typeof rootRoute 120 | } 121 | '/deferred': { 122 | id: '/deferred' 123 | path: '/deferred' 124 | fullPath: '/deferred' 125 | preLoaderRoute: typeof DeferredImport 126 | parentRoute: typeof rootRoute 127 | } 128 | '/redirect': { 129 | id: '/redirect' 130 | path: '/redirect' 131 | fullPath: '/redirect' 132 | preLoaderRoute: typeof RedirectImport 133 | parentRoute: typeof rootRoute 134 | } 135 | '/_pathlessLayout/_nested-layout': { 136 | id: '/_pathlessLayout/_nested-layout' 137 | path: '' 138 | fullPath: '' 139 | preLoaderRoute: typeof PathlessLayoutNestedLayoutImport 140 | parentRoute: typeof PathlessLayoutImport 141 | } 142 | '/posts/$postId': { 143 | id: '/posts/$postId' 144 | path: '/$postId' 145 | fullPath: '/posts/$postId' 146 | preLoaderRoute: typeof PostsPostIdImport 147 | parentRoute: typeof PostsRouteImport 148 | } 149 | '/posts/': { 150 | id: '/posts/' 151 | path: '/' 152 | fullPath: '/posts/' 153 | preLoaderRoute: typeof PostsIndexImport 154 | parentRoute: typeof PostsRouteImport 155 | } 156 | '/_pathlessLayout/_nested-layout/route-a': { 157 | id: '/_pathlessLayout/_nested-layout/route-a' 158 | path: '/route-a' 159 | fullPath: '/route-a' 160 | preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteAImport 161 | parentRoute: typeof PathlessLayoutNestedLayoutImport 162 | } 163 | '/_pathlessLayout/_nested-layout/route-b': { 164 | id: '/_pathlessLayout/_nested-layout/route-b' 165 | path: '/route-b' 166 | fullPath: '/route-b' 167 | preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBImport 168 | parentRoute: typeof PathlessLayoutNestedLayoutImport 169 | } 170 | '/posts_/$postId/deep': { 171 | id: '/posts_/$postId/deep' 172 | path: '/posts/$postId/deep' 173 | fullPath: '/posts/$postId/deep' 174 | preLoaderRoute: typeof PostsPostIdDeepImport 175 | parentRoute: typeof rootRoute 176 | } 177 | } 178 | } 179 | 180 | // Create and export the route tree 181 | 182 | interface PostsRouteRouteChildren { 183 | PostsPostIdRoute: typeof PostsPostIdRoute 184 | PostsIndexRoute: typeof PostsIndexRoute 185 | } 186 | 187 | const PostsRouteRouteChildren: PostsRouteRouteChildren = { 188 | PostsPostIdRoute: PostsPostIdRoute, 189 | PostsIndexRoute: PostsIndexRoute, 190 | } 191 | 192 | const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( 193 | PostsRouteRouteChildren, 194 | ) 195 | 196 | interface PathlessLayoutNestedLayoutRouteChildren { 197 | PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute 198 | PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute 199 | } 200 | 201 | const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = 202 | { 203 | PathlessLayoutNestedLayoutRouteARoute: 204 | PathlessLayoutNestedLayoutRouteARoute, 205 | PathlessLayoutNestedLayoutRouteBRoute: 206 | PathlessLayoutNestedLayoutRouteBRoute, 207 | } 208 | 209 | const PathlessLayoutNestedLayoutRouteWithChildren = 210 | PathlessLayoutNestedLayoutRoute._addFileChildren( 211 | PathlessLayoutNestedLayoutRouteChildren, 212 | ) 213 | 214 | interface PathlessLayoutRouteChildren { 215 | PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren 216 | } 217 | 218 | const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { 219 | PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, 220 | } 221 | 222 | const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( 223 | PathlessLayoutRouteChildren, 224 | ) 225 | 226 | export interface FileRoutesByFullPath { 227 | '/': typeof IndexRoute 228 | '/posts': typeof PostsRouteRouteWithChildren 229 | '': typeof PathlessLayoutNestedLayoutRouteWithChildren 230 | '/deferred': typeof DeferredRoute 231 | '/redirect': typeof RedirectRoute 232 | '/posts/$postId': typeof PostsPostIdRoute 233 | '/posts/': typeof PostsIndexRoute 234 | '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute 235 | '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute 236 | '/posts/$postId/deep': typeof PostsPostIdDeepRoute 237 | } 238 | 239 | export interface FileRoutesByTo { 240 | '/': typeof IndexRoute 241 | '': typeof PathlessLayoutNestedLayoutRouteWithChildren 242 | '/deferred': typeof DeferredRoute 243 | '/redirect': typeof RedirectRoute 244 | '/posts/$postId': typeof PostsPostIdRoute 245 | '/posts': typeof PostsIndexRoute 246 | '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute 247 | '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute 248 | '/posts/$postId/deep': typeof PostsPostIdDeepRoute 249 | } 250 | 251 | export interface FileRoutesById { 252 | __root__: typeof rootRoute 253 | '/': typeof IndexRoute 254 | '/posts': typeof PostsRouteRouteWithChildren 255 | '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren 256 | '/deferred': typeof DeferredRoute 257 | '/redirect': typeof RedirectRoute 258 | '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren 259 | '/posts/$postId': typeof PostsPostIdRoute 260 | '/posts/': typeof PostsIndexRoute 261 | '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute 262 | '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute 263 | '/posts_/$postId/deep': typeof PostsPostIdDeepRoute 264 | } 265 | 266 | export interface FileRouteTypes { 267 | fileRoutesByFullPath: FileRoutesByFullPath 268 | fullPaths: 269 | | '/' 270 | | '/posts' 271 | | '' 272 | | '/deferred' 273 | | '/redirect' 274 | | '/posts/$postId' 275 | | '/posts/' 276 | | '/route-a' 277 | | '/route-b' 278 | | '/posts/$postId/deep' 279 | fileRoutesByTo: FileRoutesByTo 280 | to: 281 | | '/' 282 | | '' 283 | | '/deferred' 284 | | '/redirect' 285 | | '/posts/$postId' 286 | | '/posts' 287 | | '/route-a' 288 | | '/route-b' 289 | | '/posts/$postId/deep' 290 | id: 291 | | '__root__' 292 | | '/' 293 | | '/posts' 294 | | '/_pathlessLayout' 295 | | '/deferred' 296 | | '/redirect' 297 | | '/_pathlessLayout/_nested-layout' 298 | | '/posts/$postId' 299 | | '/posts/' 300 | | '/_pathlessLayout/_nested-layout/route-a' 301 | | '/_pathlessLayout/_nested-layout/route-b' 302 | | '/posts_/$postId/deep' 303 | fileRoutesById: FileRoutesById 304 | } 305 | 306 | export interface RootRouteChildren { 307 | IndexRoute: typeof IndexRoute 308 | PostsRouteRoute: typeof PostsRouteRouteWithChildren 309 | PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren 310 | DeferredRoute: typeof DeferredRoute 311 | RedirectRoute: typeof RedirectRoute 312 | PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute 313 | } 314 | 315 | const rootRouteChildren: RootRouteChildren = { 316 | IndexRoute: IndexRoute, 317 | PostsRouteRoute: PostsRouteRouteWithChildren, 318 | PathlessLayoutRoute: PathlessLayoutRouteWithChildren, 319 | DeferredRoute: DeferredRoute, 320 | RedirectRoute: RedirectRoute, 321 | PostsPostIdDeepRoute: PostsPostIdDeepRoute, 322 | } 323 | 324 | export const routeTree = rootRoute 325 | ._addFileChildren(rootRouteChildren) 326 | ._addFileTypes() 327 | 328 | /* ROUTE_MANIFEST_START 329 | { 330 | "routes": { 331 | "__root__": { 332 | "filePath": "__root.tsx", 333 | "children": [ 334 | "/", 335 | "/posts", 336 | "/_pathlessLayout", 337 | "/deferred", 338 | "/redirect", 339 | "/posts_/$postId/deep" 340 | ] 341 | }, 342 | "/": { 343 | "filePath": "index.tsx" 344 | }, 345 | "/posts": { 346 | "filePath": "posts.route.tsx", 347 | "children": [ 348 | "/posts/$postId", 349 | "/posts/" 350 | ] 351 | }, 352 | "/_pathlessLayout": { 353 | "filePath": "_pathlessLayout.tsx", 354 | "children": [ 355 | "/_pathlessLayout/_nested-layout" 356 | ] 357 | }, 358 | "/deferred": { 359 | "filePath": "deferred.tsx" 360 | }, 361 | "/redirect": { 362 | "filePath": "redirect.tsx" 363 | }, 364 | "/_pathlessLayout/_nested-layout": { 365 | "filePath": "_pathlessLayout/_nested-layout.tsx", 366 | "parent": "/_pathlessLayout", 367 | "children": [ 368 | "/_pathlessLayout/_nested-layout/route-a", 369 | "/_pathlessLayout/_nested-layout/route-b" 370 | ] 371 | }, 372 | "/posts/$postId": { 373 | "filePath": "posts.$postId.tsx", 374 | "parent": "/posts" 375 | }, 376 | "/posts/": { 377 | "filePath": "posts.index.tsx", 378 | "parent": "/posts" 379 | }, 380 | "/_pathlessLayout/_nested-layout/route-a": { 381 | "filePath": "_pathlessLayout/_nested-layout/route-a.tsx", 382 | "parent": "/_pathlessLayout/_nested-layout" 383 | }, 384 | "/_pathlessLayout/_nested-layout/route-b": { 385 | "filePath": "_pathlessLayout/_nested-layout/route-b.tsx", 386 | "parent": "/_pathlessLayout/_nested-layout" 387 | }, 388 | "/posts_/$postId/deep": { 389 | "filePath": "posts_.$postId.deep.tsx" 390 | } 391 | } 392 | } 393 | ROUTE_MANIFEST_END */ 394 | --------------------------------------------------------------------------------