├── .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 |
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 |
--------------------------------------------------------------------------------