├── db.sqlite ├── .prettierignore ├── .vscode └── settings.json ├── sqlite.db ├── 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.cjs ├── app ├── api.ts ├── routes │ ├── redirect.tsx │ ├── users.index.tsx │ ├── projects.index.tsx │ ├── index.tsx │ ├── _layout.tsx │ ├── api │ │ └── users.$id.ts │ ├── projects.$projectId.tsx │ ├── users.$userId.tsx │ ├── projects.tsx │ ├── users.tsx │ ├── __root.tsx │ ├── users.new.tsx │ └── projects.new.tsx ├── client.tsx ├── styles │ └── app.css ├── ssr.tsx ├── router.tsx ├── components │ ├── NotFound.tsx │ └── DefaultCatchBoundary.tsx ├── utils │ ├── seo.ts │ ├── projects-service.tsx │ └── users-service.ts └── routeTree.gen.ts ├── postcss.config.cjs ├── drizzle.config.ts ├── drizzle ├── meta │ ├── _journal.json │ └── 0000_snapshot.json ├── 0000_icy_carnage.sql ├── db.ts └── schema.ts ├── app.config.ts ├── .gitignore ├── prettier.config.cjs ├── tsconfig.json ├── package.json └── README.md /db.sqlite: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/build 2 | **/public 3 | pnpm-lock.yaml 4 | routeTree.gen.ts -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.configPath": "./prettier.config.cjs" 3 | } -------------------------------------------------------------------------------- /sqlite.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/sqlite.db -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronksaunders/tanstack-start-drizzle-app/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./app/**/*.{js,ts,jsx,tsx}'], 4 | } 5 | -------------------------------------------------------------------------------- /app/api.ts: -------------------------------------------------------------------------------- 1 | import { createStartAPIHandler, defaultAPIFileRouteHandler } from '@tanstack/start/api'; 2 | 3 | export default createStartAPIHandler(defaultAPIFileRouteHandler); 4 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss/nesting'), 4 | require('tailwindcss'), 5 | require('autoprefixer'), 6 | ], 7 | } 8 | -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'drizzle-kit'; 2 | 3 | export default { 4 | schema: './drizzle/schema.ts', 5 | out: './drizzle', 6 | dialect: 'sqlite', 7 | dbCredentials: { 8 | url: './sqlite.db', 9 | }, 10 | } satisfies Config; 11 | -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "sqlite", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "6", 8 | "when": 1729266802662, 9 | "tag": "0000_icy_carnage", 10 | "breakpoints": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /app/routes/users.index.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router'; 2 | 3 | export const Route = createFileRoute('/users/')({ 4 | component: UsersIndexComponent, 5 | }); 6 | 7 | function UsersIndexComponent() { 8 | return
Select a user.
; 9 | } 10 | -------------------------------------------------------------------------------- /app/routes/projects.index.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router'; 2 | 3 | export const Route = createFileRoute('/projects/')({ 4 | component: ProjectsIndexComponent, 5 | }); 6 | 7 | function ProjectsIndexComponent() { 8 | return
Select a project.
; 9 | } 10 | -------------------------------------------------------------------------------- /app/client.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | import { hydrateRoot } from 'react-dom/client'; 3 | import { StartClient } from '@tanstack/start'; 4 | import { createRouter } from './router'; 5 | 6 | const router = createRouter(); 7 | 8 | hydrateRoot(document, ); 9 | -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /app/styles/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | html, 7 | body { 8 | /* @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; */ 9 | } 10 | 11 | .using-mouse * { 12 | outline: none !important; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/ssr.tsx: -------------------------------------------------------------------------------- 1 | import { createStartHandler, defaultStreamHandler } from '@tanstack/start/server'; 2 | import { getRouterManifest } from '@tanstack/start/router-manifest'; 3 | 4 | import { createRouter } from './router'; 5 | 6 | export default createStartHandler({ 7 | createRouter, 8 | getRouterManifest, 9 | })(defaultStreamHandler); 10 | -------------------------------------------------------------------------------- /app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@tanstack/start/config'; 2 | import tsConfigPaths from 'vite-tsconfig-paths'; 3 | 4 | export default defineConfig({ 5 | vite: { 6 | ssr: { external: ['drizzle-orm'] }, 7 | plugins: [ 8 | tsConfigPaths({ 9 | projects: ['./tsconfig.json'], 10 | }), 11 | ], 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /app/routes/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet, createFileRoute } from '@tanstack/react-router'; 2 | 3 | export const Route = createFileRoute('/_layout')({ 4 | component: LayoutComponent, 5 | }); 6 | 7 | function LayoutComponent() { 8 | return ( 9 |
10 |
I'm a layout
11 |
12 | 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /drizzle/0000_icy_carnage.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `projects` ( 2 | `id` integer PRIMARY KEY NOT NULL, 3 | `name` text, 4 | `description` text, 5 | `status` text DEFAULT 'not_started' NOT NULL, 6 | `owner_id` integer NOT NULL, 7 | FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action 8 | ); 9 | --> statement-breakpoint 10 | CREATE TABLE `users` ( 11 | `id` integer PRIMARY KEY NOT NULL, 12 | `full_name` text, 13 | `email` text 14 | ); 15 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSameLine: true, 3 | bracketSpacing: true, 4 | endOfLine: 'auto', 5 | embeddedLanguageFormatting: 'auto', 6 | htmlWhitespaceSensitivity: 'strict', 7 | jsxSingleQuote: true, 8 | printWidth: 100, 9 | proseWrap: 'always', 10 | quoteProps: 'consistent', 11 | semi: true, 12 | singleQuote: true, 13 | tabWidth: 2, 14 | trailingComma: 'es5', 15 | useTabs: false, 16 | // The parser option is not needed here as Prettier will automatically detect the correct parser 17 | }; 18 | -------------------------------------------------------------------------------- /drizzle/db.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Initializes a SQLite database connection using the `drizzle-orm` library. 3 | * The database file is named `sqlite.db` and is located in the `app/db` directory. 4 | * The `db` object can be used to interact with the SQLite database throughout the application. 5 | */ 6 | import { drizzle } from 'drizzle-orm/better-sqlite3'; 7 | import Database from 'better-sqlite3'; 8 | import { projects } from './schema'; 9 | 10 | const sqlite = new Database('sqlite.db'); 11 | export const db = drizzle(sqlite); 12 | 13 | export default db; 14 | -------------------------------------------------------------------------------- /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 | "~/*": ["./app/*"] 19 | }, 20 | "noEmit": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/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 | }); 13 | 14 | return router; 15 | } 16 | 17 | declare module '@tanstack/react-router' { 18 | interface Register { 19 | router: ReturnType; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/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 | 15 | 18 | Start Over 19 | 20 |

21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /app/routes/api/users.$id.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@tanstack/start'; 2 | import { createAPIFileRoute } from '@tanstack/start/api'; 3 | import { eq } from 'drizzle-orm'; 4 | import db from 'drizzle/db'; 5 | import { users } from 'drizzle/schema'; 6 | 7 | export const Route = createAPIFileRoute('/api/users/$id')({ 8 | GET: async ({ request, params }) => { 9 | console.info(`Fetching users by id=${params.id}... @`, request.url); 10 | try { 11 | // use drizzle-orm 12 | const user = db 13 | .select() 14 | .from(users) 15 | .where(eq(users.id, parseInt(params.id, 10))) 16 | .get(); 17 | console.log('[user] ==>', user); 18 | if (!user) { 19 | throw new Error('User not found'); 20 | } 21 | return json(user); 22 | } catch (e) { 23 | console.error(e); 24 | return json({ error: 'User not found' }, { status: 404 }); 25 | } 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /drizzle/schema.ts: -------------------------------------------------------------------------------- 1 | import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; 2 | 3 | // Define the allowed values for the enum column 4 | const PROJECT_STATUS = ['not_started', 'completed', 'archived', 'in_progress'] as const; 5 | 6 | export const users = sqliteTable('users', { 7 | id: integer('id').primaryKey(), 8 | fullName: text('full_name'), 9 | email: text('email'), 10 | }); 11 | 12 | export const projects = sqliteTable('projects', { 13 | id: integer('id').primaryKey(), 14 | name: text('name'), 15 | description: text('description'), 16 | status: text('status', { enum: PROJECT_STATUS }).default('not_started').notNull(), 17 | ownerId: integer('owner_id') 18 | .notNull() 19 | .references(() => users.id), 20 | }); 21 | 22 | // Add type definitions 23 | export type User = typeof users.$inferSelect; 24 | export type NewUser = typeof users.$inferInsert; 25 | export type Project = typeof projects.$inferSelect; 26 | export type NewProject = typeof projects.$inferInsert; 27 | export type ProjectStatus = (typeof PROJECT_STATUS)[number]; 28 | -------------------------------------------------------------------------------- /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 --preset node-server", 9 | "start": "vinxi start --preset node-server", 10 | "generate": "drizzle-kit generate", 11 | "push": "drizzle-kit push", 12 | "studio": "drizzle-kit studio", 13 | "format": "prettier --write --ignore-path .prettierignore \"**/*.{js,jsx,ts,tsx,md}\"" 14 | }, 15 | "dependencies": { 16 | "@tanstack/react-router": "1.95.1", 17 | "@tanstack/router-devtools": "1.95.1", 18 | "@tanstack/start": "1.95.1", 19 | "better-sqlite3": "^9.4.3", 20 | "drizzle-orm": "0.28.5", 21 | "react": "18.3.1", 22 | "react-dom": "18.3.1", 23 | "redaxios": "^0.5.1", 24 | "tailwind-merge": "^2.5.5", 25 | "vinxi": "0.5.1" 26 | }, 27 | "devDependencies": { 28 | "@types/better-sqlite3": "^7.6.9", 29 | "@types/node": "^22.5.4", 30 | "@types/react": "^18.2.65", 31 | "@types/react-dom": "^18.2.21", 32 | "@vitejs/plugin-react": "^4.2.1", 33 | "autoprefixer": "^10.4.17", 34 | "drizzle-kit": "0.19.13", 35 | "postcss": "^8.4.35", 36 | "tailwindcss": "^3.4.1", 37 | "typescript": "^5.3.3", 38 | "vite-tsconfig-paths": "^4.3.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/routes/projects.$projectId.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorComponent, Link, createFileRoute } from '@tanstack/react-router'; 2 | import type { ErrorComponentProps } from '@tanstack/react-router'; 3 | import { NotFound } from '~/components/NotFound'; 4 | import { fetchProject } from '~/utils/projects-service'; 5 | 6 | /** 7 | * Dynamic project route component. 8 | * This component handles individual project pages with dynamic IDs. 9 | * 10 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/route-trees#dynamic-route-segments TanStack Router - Dynamic Route Segments} 11 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/path-params TanStack Router - Path Params} 12 | */ 13 | export const Route = createFileRoute('/projects/$projectId')({ 14 | loader: async ({ params: { projectId } }) => fetchProject({ data: Number(projectId) }), 15 | errorComponent: ProjectErrorComponent as any, 16 | component: ProjectComponent, 17 | notFoundComponent: () => { 18 | return Project not found; 19 | }, 20 | }); 21 | 22 | export function ProjectErrorComponent({ error }: ErrorComponentProps) { 23 | return ; 24 | } 25 | 26 | function ProjectComponent() { 27 | const project = Route.useLoaderData(); 28 | 29 | return ( 30 |
31 |

{project?.name}

32 |
{project?.ownerId}
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /app/utils/seo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates SEO-related meta tags for a page. 3 | * @param options - An object containing SEO-related information. 4 | * @param options.title - The title of the page. 5 | * @param options.description - A brief description of the page content. 6 | * @param options.keywords - Comma-separated keywords relevant to the page. 7 | * @param options.image - URL of an image to be used in social media shares. 8 | * @returns An array of meta tag objects to be used in the page head. 9 | */ 10 | export const seo = ({ 11 | title, 12 | description, 13 | keywords, 14 | image, 15 | }: { 16 | title: string; 17 | description?: string; 18 | image?: string; 19 | keywords?: string; 20 | }) => { 21 | const tags = [ 22 | { title }, 23 | { name: 'description', content: description }, 24 | { name: 'keywords', content: keywords }, 25 | { name: 'twitter:title', content: title }, 26 | { name: 'twitter:description', content: description }, 27 | { name: 'twitter:creator', content: '@tannerlinsley' }, 28 | { name: 'twitter:site', content: '@tannerlinsley' }, 29 | { name: 'og:type', content: 'website' }, 30 | { name: 'og:title', content: title }, 31 | { name: 'og:description', content: description }, 32 | ...(image 33 | ? [ 34 | { name: 'twitter:image', content: image }, 35 | { name: 'twitter:card', content: 'summary_large_image' }, 36 | { name: 'og:image', content: image }, 37 | ] 38 | : []), 39 | ]; 40 | 41 | return tags; 42 | }; 43 | -------------------------------------------------------------------------------- /app/routes/users.$userId.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorComponent, createFileRoute } from '@tanstack/react-router'; 2 | import type { ErrorComponentProps } from '@tanstack/react-router'; 3 | import { NotFound } from '~/components/NotFound'; 4 | import { fetchUser } from '~/utils/users-service'; 5 | 6 | /** 7 | * Route configuration for individual user pages. 8 | * This route handles the '/users/:userId' path. 9 | * 10 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/route-trees#dynamic-route-segments TanStack Router - Dynamic Route Segments} 11 | */ 12 | export const Route = createFileRoute('/users/$userId')({ 13 | loader: async ({ params: { userId } }) => { 14 | return await fetchUser({ data: Number(userId) }); 15 | }, 16 | errorComponent: UserErrorComponent, 17 | component: UserComponent, 18 | notFoundComponent: () => User not found, 19 | }); 20 | 21 | /** 22 | * Error component for the user route. 23 | * Renders when an error occurs during route loading or rendering. 24 | */ 25 | export function UserErrorComponent({ error }: ErrorComponentProps) { 26 | return ; 27 | } 28 | 29 | /** 30 | * Main component for rendering individual user details. 31 | * Uses the data fetched by the loader. 32 | */ 33 | function UserComponent() { 34 | const user = Route.useLoaderData(); 35 | 36 | return ( 37 |
38 |

{user?.fullName}

39 | {/* Add more user details here */} 40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /app/components/DefaultCatchBoundary.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorComponent, Link, rootRouteId, useMatch, useRouter } from '@tanstack/react-router'; 2 | import type { ErrorComponentProps } from '@tanstack/react-router'; 3 | 4 | export function DefaultCatchBoundary({ error }: ErrorComponentProps) { 5 | const router = useRouter(); 6 | const isRoot = useMatch({ 7 | strict: false, 8 | select: (state) => state.id === rootRouteId, 9 | }); 10 | 11 | console.error(error); 12 | 13 | return ( 14 |
15 | 16 |
17 | 24 | {isRoot ? ( 25 | 28 | Home 29 | 30 | ) : ( 31 | { 35 | e.preventDefault(); 36 | window.history.back(); 37 | }}> 38 | Go Back 39 | 40 | )} 41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /app/routes/projects.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Outlet, createFileRoute } from '@tanstack/react-router'; 2 | import { fetchProjects } from '~/utils/projects-service'; 3 | 4 | /** 5 | * Projects route component. 6 | * This component handles the /projects route and its children. 7 | * It fetches and displays a list of projects and renders child routes. 8 | * 9 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/route-trees#static-routes TanStack Router - Static Routes} 10 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/data-loading TanStack Router - Data Loading} 11 | */ 12 | export const Route = createFileRoute('/projects')({ 13 | loader: async () => fetchProjects(), 14 | component: ProjectsComponent, 15 | }); 16 | 17 | function ProjectsComponent() { 18 | const projects = Route.useLoaderData(); 19 | 20 | return ( 21 |
22 |
23 |
24 | 27 | New Project 28 | 29 |
30 |
31 |
32 |
    33 | {projects?.map((project) => { 34 | return ( 35 |
  • 36 | 41 |
    {project.name}
    42 | 43 |
  • 44 | ); 45 | })} 46 |
47 |
48 | 49 |
50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /app/routes/users.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Outlet, createFileRoute } from '@tanstack/react-router'; 2 | import { fetchUsers } from '~/utils/users-service'; 3 | 4 | /** 5 | * Route configuration for the users list page. 6 | * This route handles the '/users' path and acts as a parent for individual user routes. 7 | * 8 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/route-trees#static-routes TanStack Router - Static Routes} 9 | * @see {@link https://tanstack.com/router/latest/docs/framework/react/guide/data-loading TanStack Router - Data Loading} 10 | */ 11 | export const Route = createFileRoute('/users')({ 12 | loader: async () => { 13 | return await fetchUsers(); 14 | }, 15 | component: UsersComponent, 16 | }); 17 | 18 | /** 19 | * Main component for rendering the list of users and handling nested routes. 20 | * Uses the data fetched by the loader to display a list of users. 21 | */ 22 | function UsersComponent() { 23 | const users = Route.useLoaderData(); 24 | 25 | return ( 26 |
27 |
28 |
29 | 32 | New User 33 | 34 |
35 |
36 |
37 |
    38 | {users?.map((user) => ( 39 |
  • 40 | 47 |
    {user.fullName}
    48 | 49 |
  • 50 | ))} 51 |
52 |
53 | 54 |
55 |
56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /app/utils/projects-service.tsx: -------------------------------------------------------------------------------- 1 | import { createServerFn } from '@tanstack/start'; 2 | import db from 'drizzle/db'; 3 | import { NewProject, projects } from 'drizzle/schema'; 4 | import { eq } from 'drizzle-orm'; 5 | 6 | /** 7 | * Fetches a single project by its ID. 8 | * @param projectId - The ID of the project to fetch. 9 | * @returns A Promise that resolves to the project data. 10 | */ 11 | export const fetchProject = createServerFn({ method: 'GET' }) 12 | .validator((projectId: number) => { 13 | if (typeof projectId !== 'number') { 14 | throw new Error('Invalid project ID'); 15 | } 16 | return projectId; 17 | }) 18 | .handler(async ({ data }: { data: number }) => { 19 | console.info(`Fetching project with id ${data}...`); 20 | 21 | try { 22 | const project = db.select().from(projects).where(eq(projects.id, data)).get(); 23 | 24 | if (!project) { 25 | throw new Error('Project not found'); 26 | } 27 | 28 | return project; 29 | } catch (error) { 30 | console.error('Error fetching project:', error); 31 | throw new Error('Failed to fetch project'); 32 | } 33 | }); 34 | 35 | /** 36 | * Fetches all projects. 37 | * @returns A Promise that resolves to an array of project data. 38 | */ 39 | export const fetchProjects = createServerFn({ method: 'GET' }).handler(async () => { 40 | console.info('Fetching projects...'); 41 | 42 | try { 43 | const allProjects = db.select().from(projects).all(); 44 | console.log('[projects] ==>', allProjects); 45 | return allProjects; 46 | } catch (error) { 47 | console.error('Error fetching projects:', error); 48 | throw new Error('Failed to fetch projects'); 49 | } 50 | }); 51 | 52 | /** 53 | * Creates a new project. 54 | * @param projectData - The data for the new project. 55 | * @returns A Promise that resolves to the created project data. 56 | */ 57 | export const createProject = createServerFn({ method: 'POST' }) 58 | .validator((input: NewProject) => { 59 | if (!input) { 60 | throw new Error('Project data is required'); 61 | } 62 | return input; 63 | }) 64 | .handler(async ({ data }: { data: NewProject }) => { 65 | console.info('Creating project...', data); 66 | try { 67 | const result = db.insert(projects).values(data).run(); 68 | return result; 69 | } catch (error) { 70 | console.error('Error creating project:', error); 71 | throw new Error('Failed to create project'); 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /app/routes/__root.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Outlet, ScrollRestoration, createRootRoute } from '@tanstack/react-router'; 2 | import { TanStackRouterDevtools } from '@tanstack/router-devtools'; 3 | import { Meta, Scripts } from '@tanstack/start'; 4 | import * as React from 'react'; 5 | import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'; 6 | import { NotFound } from '~/components/NotFound'; 7 | import appCss from '~/styles/app.css?url'; 8 | import { seo } from '~/utils/seo'; 9 | 10 | export const Route = createRootRoute({ 11 | head: () => ({ 12 | meta: [ 13 | { charSet: 'utf-8' }, 14 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 15 | ...seo({ 16 | title: 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', 17 | description: `TanStack Start is a type-safe, client-first, full-stack React framework.`, 18 | }), 19 | ], 20 | links: [{ rel: 'stylesheet', href: appCss }], 21 | }), 22 | component: RootComponent, 23 | }); 24 | 25 | function RootComponent() { 26 | return ( 27 | 28 | 29 | 30 | ); 31 | } 32 | 33 | function RootDocument({ children }: { children: React.ReactNode }) { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 |
41 | 47 | Home 48 | {' '} 49 | 54 | Projects 55 | {' '} 56 | 61 | Users 62 | {' '} 63 | 69 | This Route Does Not Exist 70 | 71 |
72 |
73 | {children} 74 | 75 | 76 | 77 | 78 | 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /app/routes/users.new.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute, useNavigate } from '@tanstack/react-router'; 2 | import { useState } from 'react'; 3 | import { createUser } from '~/utils/users-service'; 4 | 5 | export const Route = createFileRoute('/users/new')({ 6 | component: NewUserComponent, 7 | }); 8 | 9 | function NewUserComponent() { 10 | const navigate = useNavigate(); 11 | const [fullName, setFullName] = useState(''); 12 | const [email, setEmail] = useState(''); 13 | 14 | const handleSubmit = async (e: React.FormEvent) => { 15 | e.preventDefault(); 16 | try { 17 | await createUser({ data: { fullName, email } }); 18 | navigate({ to: '/users' }); 19 | } catch (error) { 20 | console.error('Error creating user:', error); 21 | } 22 | }; 23 | 24 | return ( 25 |
26 |
27 |

Create New User

28 |
29 |
30 | 33 | setFullName(e.target.value)} 38 | className='mt-1 p-1 block w-full rounded-md border border-gray-300 shadow-sm focus:ring-transparent focus:outline-none' 39 | required 40 | /> 41 |
42 |
43 | 46 | setEmail(e.target.value)} 51 | className='mt-1 p-1 block w-full rounded-md border border-gray-300 shadow-sm focus:ring-transparent focus:outline-none' 52 | required 53 | /> 54 |
55 | 60 |
61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /app/utils/users-service.ts: -------------------------------------------------------------------------------- 1 | import { createServerFn } from '@tanstack/start'; 2 | import db from 'drizzle/db'; 3 | import { users, NewUser } from 'drizzle/schema'; 4 | import { eq } from 'drizzle-orm'; // Add this import 5 | 6 | /** 7 | * Fetches all users from the database. 8 | * @returns A Promise that resolves to an array of user data. 9 | * @throws {Error} If there's a database error. 10 | */ 11 | export const fetchUsers = createServerFn({ method: 'GET' }).handler(async () => { 12 | console.info('Fetching users...'); 13 | 14 | try { 15 | const allUsers = db.select().from(users).all(); 16 | console.log('[users] ==>', allUsers); 17 | return allUsers; 18 | } catch (error) { 19 | console.error('Error fetching users:', error); 20 | throw new Error('Failed to fetch users'); 21 | } 22 | }); 23 | 24 | /** 25 | * Fetches a single user by their ID. 26 | * @param userId - The ID of the user to fetch. 27 | * @returns A Promise that resolves to the user data. 28 | * @throws {Error} If the user is not found or there's a database error. 29 | */ 30 | export const fetchUser = createServerFn({ method: 'GET' }) 31 | .validator((userId: number) => { 32 | if (!userId) { 33 | throw new Error('Invalid user ID'); 34 | } 35 | return userId; 36 | }) 37 | .handler(async ({ data }: { data: number }) => { 38 | console.info(`Fetching user with id ${data}...`); 39 | 40 | try { 41 | const user = db.select().from(users).where(eq(users.id, data)).get(); 42 | 43 | if (!user) { 44 | throw new Error('User not found'); 45 | } 46 | 47 | return user; 48 | } catch (error) { 49 | console.error('Error fetching user:', error); 50 | throw new Error('Failed to fetch user'); 51 | } 52 | }); 53 | 54 | /** 55 | * Creates a new user in the database. 56 | * @param userData - The data for the new user. 57 | * @returns A Promise that resolves to the created user data. 58 | * @throws {Error} If there's a database error. 59 | */ 60 | export const createUser = createServerFn({ method: 'POST' }) 61 | .validator((userData: NewUser) => { 62 | if (!userData) { 63 | throw new Error('Invalid user data'); 64 | } 65 | return userData; 66 | }) 67 | .handler(async ({ data }: { data: NewUser }) => { 68 | console.info('Creating new user...'); 69 | 70 | try { 71 | const [newUser] = await db.insert(users).values(data).returning(); 72 | console.log('[new user] ==>', newUser); 73 | return newUser; 74 | } catch (error) { 75 | console.error('Error creating user:', error); 76 | throw new Error('Failed to create user'); 77 | } 78 | }); 79 | -------------------------------------------------------------------------------- /drizzle/meta/0000_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "6", 3 | "dialect": "sqlite", 4 | "id": "4df149bf-27ce-4a11-813c-1ac66dc44a70", 5 | "prevId": "00000000-0000-0000-0000-000000000000", 6 | "tables": { 7 | "projects": { 8 | "name": "projects", 9 | "columns": { 10 | "id": { 11 | "name": "id", 12 | "type": "integer", 13 | "primaryKey": true, 14 | "notNull": true, 15 | "autoincrement": false 16 | }, 17 | "name": { 18 | "name": "name", 19 | "type": "text", 20 | "primaryKey": false, 21 | "notNull": false, 22 | "autoincrement": false 23 | }, 24 | "description": { 25 | "name": "description", 26 | "type": "text", 27 | "primaryKey": false, 28 | "notNull": false, 29 | "autoincrement": false 30 | }, 31 | "status": { 32 | "name": "status", 33 | "type": "text", 34 | "primaryKey": false, 35 | "notNull": true, 36 | "autoincrement": false, 37 | "default": "'not_started'" 38 | }, 39 | "owner_id": { 40 | "name": "owner_id", 41 | "type": "integer", 42 | "primaryKey": false, 43 | "notNull": true, 44 | "autoincrement": false 45 | } 46 | }, 47 | "indexes": {}, 48 | "foreignKeys": { 49 | "projects_owner_id_users_id_fk": { 50 | "name": "projects_owner_id_users_id_fk", 51 | "tableFrom": "projects", 52 | "tableTo": "users", 53 | "columnsFrom": [ 54 | "owner_id" 55 | ], 56 | "columnsTo": [ 57 | "id" 58 | ], 59 | "onDelete": "no action", 60 | "onUpdate": "no action" 61 | } 62 | }, 63 | "compositePrimaryKeys": {}, 64 | "uniqueConstraints": {} 65 | }, 66 | "users": { 67 | "name": "users", 68 | "columns": { 69 | "id": { 70 | "name": "id", 71 | "type": "integer", 72 | "primaryKey": true, 73 | "notNull": true, 74 | "autoincrement": false 75 | }, 76 | "full_name": { 77 | "name": "full_name", 78 | "type": "text", 79 | "primaryKey": false, 80 | "notNull": false, 81 | "autoincrement": false 82 | }, 83 | "email": { 84 | "name": "email", 85 | "type": "text", 86 | "primaryKey": false, 87 | "notNull": false, 88 | "autoincrement": false 89 | } 90 | }, 91 | "indexes": {}, 92 | "foreignKeys": {}, 93 | "compositePrimaryKeys": {}, 94 | "uniqueConstraints": {} 95 | } 96 | }, 97 | "enums": {}, 98 | "_meta": { 99 | "schemas": {}, 100 | "tables": {}, 101 | "columns": {} 102 | }, 103 | "internal": { 104 | "indexes": {} 105 | } 106 | } -------------------------------------------------------------------------------- /app/routes/projects.new.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute, useNavigate } from '@tanstack/react-router'; 2 | import { useState, useEffect } from 'react'; 3 | import { createProject } from '~/utils/projects-service'; 4 | import { fetchUsers } from '~/utils/users-service'; 5 | import { ProjectStatus, User } from 'drizzle/schema'; 6 | 7 | export const Route = createFileRoute('/projects/new')({ 8 | component: NewProjectComponent, 9 | loader: async () => { 10 | return await fetchUsers(); 11 | }, 12 | }); 13 | 14 | function NewProjectComponent() { 15 | const navigate = useNavigate(); 16 | const [name, setName] = useState(''); 17 | const [description, setDescription] = useState(''); 18 | const [status, setStatus] = useState('not_started'); 19 | const [ownerId, setOwnerId] = useState(''); 20 | const users = Route.useLoaderData(); 21 | 22 | const handleSubmit = async (e: React.FormEvent) => { 23 | e.preventDefault(); 24 | try { 25 | const response = await createProject({ 26 | data: { 27 | name, 28 | description, 29 | status, 30 | ownerId: Number(ownerId), 31 | }, 32 | }); 33 | console.log('response', response); 34 | navigate({ to: '/projects' }); 35 | } catch (error) { 36 | console.error('Error creating project:', error); 37 | } 38 | }; 39 | 40 | return ( 41 |
42 |
43 |

Create New Project

44 |
45 |
46 | 49 | setName(e.target.value)} 54 | className='mt-1 p-1 block w-full rounded-md border border-gray-300 shadow-sm focus:ring-transparent focus:outline-none' 55 | required 56 | /> 57 |
58 |
59 | 62 | 68 |
69 |
70 | 73 | 83 |
84 |
85 | 88 | 101 |
102 | 107 |
108 |
109 |
110 | ); 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TanStack Start Example - update 1/5/2025 2 | 3 | This project is a web application built with TanStack Start and Drizzle ORM. It demonstrates a 4 | full-stack React application with routing, server-side rendering, and database integration. 5 | 6 | VIDEO - https://youtu.be/oOqjZWpb-EI?feature=shared 7 | 8 | ## Features 9 | 10 | - React-based frontend with TanStack Router for routing 11 | - Server-side rendering (SSR) support 12 | - SQLite database integration using Drizzle ORM 13 | - API routes for backend functionality 14 | - Tailwind CSS for styling 15 | 16 | ## Technologies Used 17 | 18 | - [TanStack Start](https://tanstack.com/start) 19 | - [TanStack Router](https://tanstack.com/router) 20 | - [Drizzle ORM](https://orm.drizzle.team/) 21 | - [React](https://reactjs.org/) 22 | - [TypeScript](https://www.typescriptlang.org/) 23 | - [Tailwind CSS](https://tailwindcss.com/) 24 | - [Vite](https://vitejs.dev/) 25 | - [SQLite](https://www.sqlite.org/) 26 | 27 | ## Project Structure 28 | 29 | - `/app`: Contains the main application code 30 | - `/components`: Reusable React components 31 | - `/routes`: Route components and API handlers 32 | - `/styles`: CSS styles, including Tailwind configuration 33 | - `/utils`: Utility functions and services 34 | - `/drizzle`: Database schema and migrations 35 | - `/public`: Static assets 36 | 37 | ## Setup and Installation 38 | 39 | 1. Clone the repository 40 | 2. Install dependencies: 41 | ``` 42 | npm install 43 | ``` 44 | 3. Set up the database: 45 | ``` 46 | npm run generate 47 | npm run push 48 | ``` 49 | 4. Start the development server: 50 | ``` 51 | npm run dev 52 | ``` 53 | 54 | ## Available Scripts 55 | 56 | - `npm run dev`: Start the development server 57 | - `npm run build`: Build the production-ready application 58 | - `npm run start`: Start the production server 59 | - `npm run generate`: Generate Drizzle ORM schema 60 | - `npm run push`: Push schema changes to the database 61 | - `npm run studio`: Open Drizzle Studio for database management 62 | - `npm run format`: Format code using Prettier 63 | 64 | ## Development 65 | 66 | This project uses Vite for fast development and building. The development server will rebuild assets 67 | on file changes. 68 | 69 | ## Database 70 | 71 | The project uses SQLite with Drizzle ORM. The database schema is defined in `drizzle/schema.ts`. You 72 | can use Drizzle Studio to manage your database by running `npm run studio`. 73 | 74 | ## Routing 75 | 76 | Routing is handled by TanStack Router. Route components are located in the `/app/routes` directory. 77 | 78 | ## Styling 79 | 80 | Tailwind CSS is used for styling. The main CSS file is located at `/app/styles/app.css`. 81 | 82 | ## API Routes 83 | 84 | API routes are defined in the `/app/routes/api` directory. These routes handle server-side logic and 85 | database operations. 86 | 87 | ## Deployment and Production 88 | 89 | This project uses Vinxi, a powerful meta-framework for building full-stack JavaScript applications. 90 | To deploy the application: 91 | 92 | 1. Build the project: 93 | 94 | ``` 95 | npm run build 96 | ``` 97 | 98 | This command uses Vinxi to build the application with the `node-server` preset, optimizing it for 99 | server-side rendering with a Node.js backend. 100 | 101 | 2. Start the production server: 102 | ``` 103 | npm run start 104 | ``` 105 | This command starts the Vinxi server in production mode, serving your built application. 106 | 107 | ### Node.js Server 108 | 109 | The built project runs on a Node.js server, which handles both serving the static assets and 110 | server-side rendering (SSR) of your React application. This setup provides several benefits: 111 | 112 | - Improved initial page load times due to server-side rendering 113 | - Better SEO as search engines can crawl the fully rendered content 114 | - Seamless handling of both client-side and server-side routing 115 | 116 | ### Environment Variables 117 | 118 | When running the production server, make sure to set any necessary environment variables. You can do 119 | this by creating a `.env` file in the root of your project or by setting them directly in your 120 | deployment environment. 121 | 122 | ### Hosting Recommendations 123 | 124 | This Vinxi-powered application can be deployed to various Node.js-compatible hosting platforms, such 125 | as: 126 | 127 | - Vercel 128 | - Netlify 129 | - DigitalOcean App Platform 130 | - Heroku 131 | - AWS Elastic Beanstalk 132 | 133 | Ensure that your chosen hosting platform supports Node.js and can run the `npm run start` command to 134 | start the server. 135 | 136 | ### Performance Considerations 137 | 138 | - The production build is optimized for performance, but you may want to implement additional 139 | caching strategies or a CDN for static assets in a high-traffic production environment. 140 | - Monitor your application's performance and resource usage in production, and scale your server 141 | resources as needed. 142 | 143 | For more detailed information on deploying Vinxi applications, refer to the 144 | [Vinxi documentation](https://vinxi.vercel.app/guide/deployment). 145 | -------------------------------------------------------------------------------- /app/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 UsersImport } from './routes/users' 15 | import { Route as RedirectImport } from './routes/redirect' 16 | import { Route as ProjectsImport } from './routes/projects' 17 | import { Route as LayoutImport } from './routes/_layout' 18 | import { Route as IndexImport } from './routes/index' 19 | import { Route as UsersIndexImport } from './routes/users.index' 20 | import { Route as ProjectsIndexImport } from './routes/projects.index' 21 | import { Route as UsersNewImport } from './routes/users.new' 22 | import { Route as UsersUserIdImport } from './routes/users.$userId' 23 | import { Route as ProjectsNewImport } from './routes/projects.new' 24 | import { Route as ProjectsProjectIdImport } from './routes/projects.$projectId' 25 | 26 | // Create/Update Routes 27 | 28 | const UsersRoute = UsersImport.update({ 29 | id: '/users', 30 | path: '/users', 31 | getParentRoute: () => rootRoute, 32 | } as any) 33 | 34 | const RedirectRoute = RedirectImport.update({ 35 | id: '/redirect', 36 | path: '/redirect', 37 | getParentRoute: () => rootRoute, 38 | } as any) 39 | 40 | const ProjectsRoute = ProjectsImport.update({ 41 | id: '/projects', 42 | path: '/projects', 43 | getParentRoute: () => rootRoute, 44 | } as any) 45 | 46 | const LayoutRoute = LayoutImport.update({ 47 | id: '/_layout', 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 UsersIndexRoute = UsersIndexImport.update({ 58 | id: '/', 59 | path: '/', 60 | getParentRoute: () => UsersRoute, 61 | } as any) 62 | 63 | const ProjectsIndexRoute = ProjectsIndexImport.update({ 64 | id: '/', 65 | path: '/', 66 | getParentRoute: () => ProjectsRoute, 67 | } as any) 68 | 69 | const UsersNewRoute = UsersNewImport.update({ 70 | id: '/new', 71 | path: '/new', 72 | getParentRoute: () => UsersRoute, 73 | } as any) 74 | 75 | const UsersUserIdRoute = UsersUserIdImport.update({ 76 | id: '/$userId', 77 | path: '/$userId', 78 | getParentRoute: () => UsersRoute, 79 | } as any) 80 | 81 | const ProjectsNewRoute = ProjectsNewImport.update({ 82 | id: '/new', 83 | path: '/new', 84 | getParentRoute: () => ProjectsRoute, 85 | } as any) 86 | 87 | const ProjectsProjectIdRoute = ProjectsProjectIdImport.update({ 88 | id: '/$projectId', 89 | path: '/$projectId', 90 | getParentRoute: () => ProjectsRoute, 91 | } as any) 92 | 93 | // Populate the FileRoutesByPath interface 94 | 95 | declare module '@tanstack/react-router' { 96 | interface FileRoutesByPath { 97 | '/': { 98 | id: '/' 99 | path: '/' 100 | fullPath: '/' 101 | preLoaderRoute: typeof IndexImport 102 | parentRoute: typeof rootRoute 103 | } 104 | '/_layout': { 105 | id: '/_layout' 106 | path: '' 107 | fullPath: '' 108 | preLoaderRoute: typeof LayoutImport 109 | parentRoute: typeof rootRoute 110 | } 111 | '/projects': { 112 | id: '/projects' 113 | path: '/projects' 114 | fullPath: '/projects' 115 | preLoaderRoute: typeof ProjectsImport 116 | parentRoute: typeof rootRoute 117 | } 118 | '/redirect': { 119 | id: '/redirect' 120 | path: '/redirect' 121 | fullPath: '/redirect' 122 | preLoaderRoute: typeof RedirectImport 123 | parentRoute: typeof rootRoute 124 | } 125 | '/users': { 126 | id: '/users' 127 | path: '/users' 128 | fullPath: '/users' 129 | preLoaderRoute: typeof UsersImport 130 | parentRoute: typeof rootRoute 131 | } 132 | '/projects/$projectId': { 133 | id: '/projects/$projectId' 134 | path: '/$projectId' 135 | fullPath: '/projects/$projectId' 136 | preLoaderRoute: typeof ProjectsProjectIdImport 137 | parentRoute: typeof ProjectsImport 138 | } 139 | '/projects/new': { 140 | id: '/projects/new' 141 | path: '/new' 142 | fullPath: '/projects/new' 143 | preLoaderRoute: typeof ProjectsNewImport 144 | parentRoute: typeof ProjectsImport 145 | } 146 | '/users/$userId': { 147 | id: '/users/$userId' 148 | path: '/$userId' 149 | fullPath: '/users/$userId' 150 | preLoaderRoute: typeof UsersUserIdImport 151 | parentRoute: typeof UsersImport 152 | } 153 | '/users/new': { 154 | id: '/users/new' 155 | path: '/new' 156 | fullPath: '/users/new' 157 | preLoaderRoute: typeof UsersNewImport 158 | parentRoute: typeof UsersImport 159 | } 160 | '/projects/': { 161 | id: '/projects/' 162 | path: '/' 163 | fullPath: '/projects/' 164 | preLoaderRoute: typeof ProjectsIndexImport 165 | parentRoute: typeof ProjectsImport 166 | } 167 | '/users/': { 168 | id: '/users/' 169 | path: '/' 170 | fullPath: '/users/' 171 | preLoaderRoute: typeof UsersIndexImport 172 | parentRoute: typeof UsersImport 173 | } 174 | } 175 | } 176 | 177 | // Create and export the route tree 178 | 179 | interface ProjectsRouteChildren { 180 | ProjectsProjectIdRoute: typeof ProjectsProjectIdRoute 181 | ProjectsNewRoute: typeof ProjectsNewRoute 182 | ProjectsIndexRoute: typeof ProjectsIndexRoute 183 | } 184 | 185 | const ProjectsRouteChildren: ProjectsRouteChildren = { 186 | ProjectsProjectIdRoute: ProjectsProjectIdRoute, 187 | ProjectsNewRoute: ProjectsNewRoute, 188 | ProjectsIndexRoute: ProjectsIndexRoute, 189 | } 190 | 191 | const ProjectsRouteWithChildren = ProjectsRoute._addFileChildren( 192 | ProjectsRouteChildren, 193 | ) 194 | 195 | interface UsersRouteChildren { 196 | UsersUserIdRoute: typeof UsersUserIdRoute 197 | UsersNewRoute: typeof UsersNewRoute 198 | UsersIndexRoute: typeof UsersIndexRoute 199 | } 200 | 201 | const UsersRouteChildren: UsersRouteChildren = { 202 | UsersUserIdRoute: UsersUserIdRoute, 203 | UsersNewRoute: UsersNewRoute, 204 | UsersIndexRoute: UsersIndexRoute, 205 | } 206 | 207 | const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren) 208 | 209 | export interface FileRoutesByFullPath { 210 | '/': typeof IndexRoute 211 | '': typeof LayoutRoute 212 | '/projects': typeof ProjectsRouteWithChildren 213 | '/redirect': typeof RedirectRoute 214 | '/users': typeof UsersRouteWithChildren 215 | '/projects/$projectId': typeof ProjectsProjectIdRoute 216 | '/projects/new': typeof ProjectsNewRoute 217 | '/users/$userId': typeof UsersUserIdRoute 218 | '/users/new': typeof UsersNewRoute 219 | '/projects/': typeof ProjectsIndexRoute 220 | '/users/': typeof UsersIndexRoute 221 | } 222 | 223 | export interface FileRoutesByTo { 224 | '/': typeof IndexRoute 225 | '': typeof LayoutRoute 226 | '/redirect': typeof RedirectRoute 227 | '/projects/$projectId': typeof ProjectsProjectIdRoute 228 | '/projects/new': typeof ProjectsNewRoute 229 | '/users/$userId': typeof UsersUserIdRoute 230 | '/users/new': typeof UsersNewRoute 231 | '/projects': typeof ProjectsIndexRoute 232 | '/users': typeof UsersIndexRoute 233 | } 234 | 235 | export interface FileRoutesById { 236 | __root__: typeof rootRoute 237 | '/': typeof IndexRoute 238 | '/_layout': typeof LayoutRoute 239 | '/projects': typeof ProjectsRouteWithChildren 240 | '/redirect': typeof RedirectRoute 241 | '/users': typeof UsersRouteWithChildren 242 | '/projects/$projectId': typeof ProjectsProjectIdRoute 243 | '/projects/new': typeof ProjectsNewRoute 244 | '/users/$userId': typeof UsersUserIdRoute 245 | '/users/new': typeof UsersNewRoute 246 | '/projects/': typeof ProjectsIndexRoute 247 | '/users/': typeof UsersIndexRoute 248 | } 249 | 250 | export interface FileRouteTypes { 251 | fileRoutesByFullPath: FileRoutesByFullPath 252 | fullPaths: 253 | | '/' 254 | | '' 255 | | '/projects' 256 | | '/redirect' 257 | | '/users' 258 | | '/projects/$projectId' 259 | | '/projects/new' 260 | | '/users/$userId' 261 | | '/users/new' 262 | | '/projects/' 263 | | '/users/' 264 | fileRoutesByTo: FileRoutesByTo 265 | to: 266 | | '/' 267 | | '' 268 | | '/redirect' 269 | | '/projects/$projectId' 270 | | '/projects/new' 271 | | '/users/$userId' 272 | | '/users/new' 273 | | '/projects' 274 | | '/users' 275 | id: 276 | | '__root__' 277 | | '/' 278 | | '/_layout' 279 | | '/projects' 280 | | '/redirect' 281 | | '/users' 282 | | '/projects/$projectId' 283 | | '/projects/new' 284 | | '/users/$userId' 285 | | '/users/new' 286 | | '/projects/' 287 | | '/users/' 288 | fileRoutesById: FileRoutesById 289 | } 290 | 291 | export interface RootRouteChildren { 292 | IndexRoute: typeof IndexRoute 293 | LayoutRoute: typeof LayoutRoute 294 | ProjectsRoute: typeof ProjectsRouteWithChildren 295 | RedirectRoute: typeof RedirectRoute 296 | UsersRoute: typeof UsersRouteWithChildren 297 | } 298 | 299 | const rootRouteChildren: RootRouteChildren = { 300 | IndexRoute: IndexRoute, 301 | LayoutRoute: LayoutRoute, 302 | ProjectsRoute: ProjectsRouteWithChildren, 303 | RedirectRoute: RedirectRoute, 304 | UsersRoute: UsersRouteWithChildren, 305 | } 306 | 307 | export const routeTree = rootRoute 308 | ._addFileChildren(rootRouteChildren) 309 | ._addFileTypes() 310 | 311 | /* ROUTE_MANIFEST_START 312 | { 313 | "routes": { 314 | "__root__": { 315 | "filePath": "__root.tsx", 316 | "children": [ 317 | "/", 318 | "/_layout", 319 | "/projects", 320 | "/redirect", 321 | "/users" 322 | ] 323 | }, 324 | "/": { 325 | "filePath": "index.tsx" 326 | }, 327 | "/_layout": { 328 | "filePath": "_layout.tsx" 329 | }, 330 | "/projects": { 331 | "filePath": "projects.tsx", 332 | "children": [ 333 | "/projects/$projectId", 334 | "/projects/new", 335 | "/projects/" 336 | ] 337 | }, 338 | "/redirect": { 339 | "filePath": "redirect.tsx" 340 | }, 341 | "/users": { 342 | "filePath": "users.tsx", 343 | "children": [ 344 | "/users/$userId", 345 | "/users/new", 346 | "/users/" 347 | ] 348 | }, 349 | "/projects/$projectId": { 350 | "filePath": "projects.$projectId.tsx", 351 | "parent": "/projects" 352 | }, 353 | "/projects/new": { 354 | "filePath": "projects.new.tsx", 355 | "parent": "/projects" 356 | }, 357 | "/users/$userId": { 358 | "filePath": "users.$userId.tsx", 359 | "parent": "/users" 360 | }, 361 | "/users/new": { 362 | "filePath": "users.new.tsx", 363 | "parent": "/users" 364 | }, 365 | "/projects/": { 366 | "filePath": "projects.index.tsx", 367 | "parent": "/projects" 368 | }, 369 | "/users/": { 370 | "filePath": "users.index.tsx", 371 | "parent": "/users" 372 | } 373 | } 374 | } 375 | ROUTE_MANIFEST_END */ 376 | --------------------------------------------------------------------------------