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