├── src
├── components
│ ├── demo
│ │ ├── index.ts
│ │ └── middleware-demo.tsx
│ ├── navigation
│ │ ├── index.ts
│ │ └── navigation-bar.tsx
│ ├── theme
│ │ ├── index.ts
│ │ ├── theme-provider.tsx
│ │ └── theme-toggle.tsx
│ ├── landing
│ │ ├── index.ts
│ │ ├── hero-section.tsx
│ │ ├── features-section.tsx
│ │ └── footer.tsx
│ ├── ui
│ │ ├── collapsible.tsx
│ │ ├── badge.tsx
│ │ ├── alert.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── sheet.tsx
│ │ └── dropdown-menu.tsx
│ ├── not-found.tsx
│ └── default-catch-boundary.tsx
├── lib
│ └── utils.ts
├── integrations
│ └── tanstack-query
│ │ ├── devtools.tsx
│ │ └── root-provider.tsx
├── core
│ ├── middleware
│ │ └── example-middleware.ts
│ └── functions
│ │ └── example-functions.ts
├── server.ts
├── start.tsx
├── routes
│ ├── index.tsx
│ └── __root.tsx
├── router.tsx
├── utils
│ └── seo.ts
├── routeTree.gen.ts
├── styles.css
└── logo.svg
├── .wrangler
└── deploy
│ └── config.json
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
└── manifest.json
├── .mcp.json
├── .gitignore
├── .vscode
└── settings.json
├── wrangler.jsonc
├── .cta.json
├── components.json
├── tsconfig.json
├── vite.config.ts
├── package.json
├── CLAUDE.md
├── .claude
└── agents
│ ├── shadcn-ui-builder.md
│ └── tanstack-server-functions.md
└── README.md
/src/components/demo/index.ts:
--------------------------------------------------------------------------------
1 | export { MiddlewareDemo } from './middleware-demo'
--------------------------------------------------------------------------------
/src/components/navigation/index.ts:
--------------------------------------------------------------------------------
1 | export { NavigationBar } from './navigation-bar'
--------------------------------------------------------------------------------
/.wrangler/deploy/config.json:
--------------------------------------------------------------------------------
1 | {"configPath":"../../dist/server/wrangler.json","auxiliaryWorkers":[]}
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/backpine/tanstack-start-on-cloudflare/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/.mcp.json:
--------------------------------------------------------------------------------
1 | {
2 | "mcpServers": {
3 | "shadcn": {
4 | "command": "npx",
5 | "args": ["shadcn@latest", "mcp"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/theme/index.ts:
--------------------------------------------------------------------------------
1 | export { ThemeProvider, useTheme } from "./theme-provider";
2 | export { ThemeToggle, ThemeToggleSimple } from "./theme-toggle";
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | count.txt
7 | .env
8 | .nitro
9 | .tanstack
10 | .output
11 | .vinxi
12 | todos.json
13 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from 'clsx'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/landing/index.ts:
--------------------------------------------------------------------------------
1 | export { HeroSection } from './hero-section'
2 | export { FeaturesSection } from './features-section'
3 | export { CtaSection } from './cta-section'
4 | export { Footer } from './footer'
--------------------------------------------------------------------------------
/src/integrations/tanstack-query/devtools.tsx:
--------------------------------------------------------------------------------
1 | import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'
2 |
3 | export default {
4 | name: 'Tanstack Query',
5 | render: ,
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.watcherExclude": {
3 | "**/routeTree.gen.ts": true
4 | },
5 | "search.exclude": {
6 | "**/routeTree.gen.ts": true
7 | },
8 | "files.readonlyInclude": {
9 | "**/routeTree.gen.ts": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/wrangler.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "node_modules/wrangler/config-schema.json",
3 | "name": "tanstack-start-app",
4 | "compatibility_date": "2025-09-02",
5 | "compatibility_flags": ["nodejs_compat"],
6 | "main": "./src/server.ts",
7 | "vars": {
8 | "MY_VAR": "Hello from Cloudflare",
9 | },
10 | }
11 |
--------------------------------------------------------------------------------
/.cta.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": ".",
3 | "mode": "file-router",
4 | "typescript": true,
5 | "tailwind": true,
6 | "packageManager": "pnpm",
7 | "git": true,
8 | "version": 1,
9 | "framework": "react-cra",
10 | "chosenAddOns": [
11 | "start",
12 | "shadcn",
13 | "tanstack-query"
14 | ]
15 | }
--------------------------------------------------------------------------------
/src/core/middleware/example-middleware.ts:
--------------------------------------------------------------------------------
1 | import { createMiddleware } from "@tanstack/react-start";
2 |
3 | export const exampleMiddlewareWithContext = createMiddleware({
4 | type: "function",
5 | }).server(async ({ next }) => {
6 | console.log("Executing exampleMiddlewareWithContext");
7 | return await next({
8 | context: {
9 | data: "Some Data From Middleware",
10 | },
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
1 | // DO NOT DELETE THIS FILE!!!
2 | // This file is a good smoke test to make sure the custom server entry is working
3 | import handler from "@tanstack/react-start/server-entry";
4 |
5 | console.log("[server-entry]: using custom server entry in 'src/server.ts'");
6 |
7 | export default {
8 | fetch(request: Request) {
9 | return handler.fetch(request, {
10 | context: {
11 | fromFetch: true,
12 | },
13 | });
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": false,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "",
8 | "css": "src/styles.css",
9 | "baseColor": "zinc",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
--------------------------------------------------------------------------------
/src/integrations/tanstack-query/root-provider.tsx:
--------------------------------------------------------------------------------
1 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2 |
3 | export function getContext() {
4 | const queryClient = new QueryClient()
5 | return {
6 | queryClient,
7 | }
8 | }
9 |
10 | export function Provider({
11 | children,
12 | queryClient,
13 | }: {
14 | children: React.ReactNode
15 | queryClient: QueryClient
16 | }) {
17 | return (
18 | {children}
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/start.tsx:
--------------------------------------------------------------------------------
1 | import { createStart } from "@tanstack/react-start";
2 |
3 | declare module "@tanstack/react-start" {
4 | interface Register {
5 | server: {
6 | requestContext: {
7 | fromFetch: boolean;
8 | };
9 | };
10 | }
11 | }
12 |
13 | export const startInstance = createStart(() => {
14 | return {
15 | defaultSsr: true,
16 | };
17 | });
18 |
19 | startInstance.createMiddleware().server(({ next }) => {
20 | return next({
21 | context: {
22 | fromStartInstanceMw: true,
23 | },
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "TanStack App",
3 | "name": "Create TanStack App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["**/*.ts", "**/*.tsx"],
3 | "compilerOptions": {
4 | "target": "ES2022",
5 | "jsx": "react-jsx",
6 | "module": "ESNext",
7 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
8 | "types": ["vite/client"],
9 |
10 | /* Bundler mode */
11 | "moduleResolution": "bundler",
12 | "allowImportingTsExtensions": true,
13 | "verbatimModuleSyntax": false,
14 | "noEmit": true,
15 |
16 | /* Linting */
17 | "skipLibCheck": true,
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true,
22 | "noUncheckedSideEffectImports": true,
23 | "baseUrl": ".",
24 | "paths": {
25 | "@/*": ["./src/*"],
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 | import { NavigationBar } from "@/components/navigation";
3 | import { HeroSection } from "@/components/landing/hero-section";
4 | import { FeaturesSection } from "@/components/landing/features-section";
5 | import { Footer } from "@/components/landing/footer";
6 | import { MiddlewareDemo } from "@/components/demo";
7 |
8 | export const Route = createFileRoute("/")({
9 | component: LandingPage,
10 | });
11 |
12 | function LandingPage() {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { tanstackStart } from "@tanstack/react-start/plugin/vite";
3 | import viteReact from "@vitejs/plugin-react";
4 | import viteTsConfigPaths from "vite-tsconfig-paths";
5 | import tailwindcss from "@tailwindcss/vite";
6 | import { cloudflare } from "@cloudflare/vite-plugin";
7 |
8 | const config = defineConfig({
9 | plugins: [
10 | // this is the plugin that enables path aliases
11 | viteTsConfigPaths({
12 | projects: ["./tsconfig.json"],
13 | }),
14 | tailwindcss(),
15 | tanstackStart({
16 | srcDirectory: "src",
17 | start: { entry: "./start.tsx" },
18 | server: { entry: "./server.ts" },
19 | }),
20 | viteReact(),
21 | cloudflare({
22 | viteEnvironment: {
23 | name: "ssr",
24 | },
25 | }),
26 | ],
27 | });
28 |
29 | export default config;
30 |
--------------------------------------------------------------------------------
/src/router.tsx:
--------------------------------------------------------------------------------
1 | import { createRouter } from '@tanstack/react-router'
2 | import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
3 | import * as TanstackQuery from './integrations/tanstack-query/root-provider'
4 |
5 | // Import the generated route tree
6 | import { routeTree } from './routeTree.gen'
7 |
8 | // Create a new router instance
9 | export const getRouter = () => {
10 | const rqContext = TanstackQuery.getContext()
11 |
12 | const router = createRouter({
13 | routeTree,
14 | context: { ...rqContext },
15 | defaultPreload: 'intent',
16 | Wrap: (props: { children: React.ReactNode }) => {
17 | return (
18 |
19 | {props.children}
20 |
21 | )
22 | },
23 | })
24 |
25 | setupRouterSsrQueryIntegration({ router, queryClient: rqContext.queryClient })
26 |
27 | return router
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
2 |
3 | function Collapsible({
4 | ...props
5 | }: React.ComponentProps) {
6 | return
7 | }
8 |
9 | function CollapsibleTrigger({
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
17 | )
18 | }
19 |
20 | function CollapsibleContent({
21 | ...props
22 | }: React.ComponentProps) {
23 | return (
24 |
28 | )
29 | }
30 |
31 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
32 |
--------------------------------------------------------------------------------
/src/core/functions/example-functions.ts:
--------------------------------------------------------------------------------
1 | import { createServerFn } from "@tanstack/react-start";
2 | import { z } from "zod";
3 | import { exampleMiddlewareWithContext } from "@/core/middleware/example-middleware";
4 | // import { env } from "cloudflare:workers";
5 |
6 | const baseFunction = createServerFn().middleware([
7 | exampleMiddlewareWithContext,
8 | ]);
9 |
10 | const ExampleInputSchema = z.object({
11 | exampleKey: z.string().min(1),
12 | });
13 |
14 | type ExampleInput = z.infer;
15 |
16 | export const examplefunction = baseFunction
17 | .inputValidator((data: ExampleInput) => ExampleInputSchema.parse(data))
18 | .handler(async (ctx) => {
19 | console.log("Executing example function");
20 | console.log(`The data passed: ${JSON.stringify(ctx.data)}`);
21 | console.log(`The context from middleware: ${JSON.stringify(ctx.context)}`);
22 | // console.log(`The Cloudflare Worker Environment: ${JSON.stringify(env)}`);
23 | return "Function executed successfully";
24 | });
25 |
--------------------------------------------------------------------------------
/src/utils/seo.ts:
--------------------------------------------------------------------------------
1 | export const seo = ({
2 | title,
3 | description,
4 | keywords,
5 | image,
6 | }: {
7 | title: string;
8 | description?: string;
9 | image?: string;
10 | keywords?: string;
11 | }) => {
12 | const tags = [
13 | { title },
14 | { name: "description", content: description },
15 | { name: "keywords", content: keywords },
16 | { name: "twitter:title", content: title },
17 | { name: "twitter:description", content: description },
18 | { name: "twitter:creator", content: "@tannerlinsley" },
19 | { name: "twitter:site", content: "@tannerlinsley" },
20 | { name: "og:type", content: "website" },
21 | { name: "og:title", content: title },
22 | { name: "og:description", content: description },
23 | ...(image
24 | ? [
25 | { name: "twitter:image", content: image },
26 | { name: "twitter:card", content: "summary_large_image" },
27 | { name: "og:image", content: image },
28 | ]
29 | : []),
30 | ];
31 |
32 | return tags;
33 | };
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tanstack-start-on-cloudflare",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite dev --port 3000",
7 | "build": "vite build",
8 | "deploy": "pnpm run build && wrangler deploy",
9 | "serve": "vite preview",
10 | "cf-typegen": "wrangler types --env-interface Env"
11 | },
12 | "dependencies": {
13 | "@radix-ui/react-collapsible": "^1.1.12",
14 | "@radix-ui/react-dialog": "^1.1.15",
15 | "@radix-ui/react-dropdown-menu": "^2.1.16",
16 | "@radix-ui/react-slot": "^1.2.4",
17 | "@tailwindcss/vite": "^4.1.17",
18 | "@tanstack/react-query": "^5.90.11",
19 | "@tanstack/react-query-devtools": "^5.91.1",
20 | "@tanstack/react-router": "^1.139.12",
21 | "@tanstack/react-router-devtools": "^1.139.12",
22 | "@tanstack/react-router-ssr-query": "^1.139.12",
23 | "@tanstack/react-start": "^1.139.12",
24 | "class-variance-authority": "^0.7.1",
25 | "clsx": "^2.1.1",
26 | "lucide-react": "^0.476.0",
27 | "react": "^19.2.0",
28 | "react-dom": "^19.2.0",
29 | "tailwind-merge": "^3.4.0",
30 | "tailwindcss": "^4.1.17",
31 | "tw-animate-css": "^1.4.0",
32 | "vite-tsconfig-paths": "^5.1.4",
33 | "zod": "^4.1.13"
34 | },
35 | "devDependencies": {
36 | "@cloudflare/vite-plugin": "^1.15.3",
37 | "@testing-library/dom": "^10.4.1",
38 | "@testing-library/react": "^16.3.0",
39 | "@types/node": "^22.19.1",
40 | "@types/react": "^19.2.7",
41 | "@types/react-dom": "^19.2.3",
42 | "@vitejs/plugin-react": "^4.7.0",
43 | "jsdom": "^26.1.0",
44 | "typescript": "^5.9.3",
45 | "vite": "7.1.2",
46 | "vitest": "^3.2.4",
47 | "web-vitals": "^4.2.4",
48 | "wrangler": "^4.51.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const badgeVariants = cva(
8 | "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14 | secondary:
15 | "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16 | destructive:
17 | "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18 | outline:
19 | "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20 | },
21 | },
22 | defaultVariants: {
23 | variant: "default",
24 | },
25 | }
26 | )
27 |
28 | function Badge({
29 | className,
30 | variant,
31 | asChild = false,
32 | ...props
33 | }: React.ComponentProps<"span"> &
34 | VariantProps & { asChild?: boolean }) {
35 | const Comp = asChild ? Slot : "span"
36 |
37 | return (
38 |
43 | )
44 | }
45 |
46 | export { Badge, badgeVariants }
47 |
--------------------------------------------------------------------------------
/src/components/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const alertVariants = cva(
7 | "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
8 | {
9 | variants: {
10 | variant: {
11 | default: "bg-card text-card-foreground",
12 | destructive:
13 | "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
14 | },
15 | },
16 | defaultVariants: {
17 | variant: "default",
18 | },
19 | }
20 | )
21 |
22 | function Alert({
23 | className,
24 | variant,
25 | ...props
26 | }: React.ComponentProps<"div"> & VariantProps) {
27 | return (
28 |
34 | )
35 | }
36 |
37 | function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
38 | return (
39 |
47 | )
48 | }
49 |
50 | function AlertDescription({
51 | className,
52 | ...props
53 | }: React.ComponentProps<"div">) {
54 | return (
55 |
63 | )
64 | }
65 |
66 | export { Alert, AlertTitle, AlertDescription }
67 |
--------------------------------------------------------------------------------
/src/routeTree.gen.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | // @ts-nocheck
4 |
5 | // noinspection JSUnusedGlobalSymbols
6 |
7 | // This file was automatically generated by TanStack Router.
8 | // You should NOT make any changes in this file as it will be overwritten.
9 | // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10 |
11 | import { Route as rootRouteImport } from './routes/__root'
12 | import { Route as IndexRouteImport } from './routes/index'
13 |
14 | const IndexRoute = IndexRouteImport.update({
15 | id: '/',
16 | path: '/',
17 | getParentRoute: () => rootRouteImport,
18 | } as any)
19 |
20 | export interface FileRoutesByFullPath {
21 | '/': typeof IndexRoute
22 | }
23 | export interface FileRoutesByTo {
24 | '/': typeof IndexRoute
25 | }
26 | export interface FileRoutesById {
27 | __root__: typeof rootRouteImport
28 | '/': typeof IndexRoute
29 | }
30 | export interface FileRouteTypes {
31 | fileRoutesByFullPath: FileRoutesByFullPath
32 | fullPaths: '/'
33 | fileRoutesByTo: FileRoutesByTo
34 | to: '/'
35 | id: '__root__' | '/'
36 | fileRoutesById: FileRoutesById
37 | }
38 | export interface RootRouteChildren {
39 | IndexRoute: typeof IndexRoute
40 | }
41 |
42 | declare module '@tanstack/react-router' {
43 | interface FileRoutesByPath {
44 | '/': {
45 | id: '/'
46 | path: '/'
47 | fullPath: '/'
48 | preLoaderRoute: typeof IndexRouteImport
49 | parentRoute: typeof rootRouteImport
50 | }
51 | }
52 | }
53 |
54 | const rootRouteChildren: RootRouteChildren = {
55 | IndexRoute: IndexRoute,
56 | }
57 | export const routeTree = rootRouteImport
58 | ._addFileChildren(rootRouteChildren)
59 | ._addFileTypes()
60 |
61 | import type { getRouter } from './router.tsx'
62 | import type { startInstance } from './start.tsx'
63 | declare module '@tanstack/react-start' {
64 | interface Register {
65 | ssr: true
66 | router: Awaited>
67 | config: Awaited>
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15 | outline:
16 | "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost:
20 | "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2 has-[>svg]:px-3",
25 | sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26 | lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27 | icon: "size-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | )
36 |
37 | function Button({
38 | className,
39 | variant,
40 | size,
41 | asChild = false,
42 | ...props
43 | }: React.ComponentProps<"button"> &
44 | VariantProps & {
45 | asChild?: boolean
46 | }) {
47 | const Comp = asChild ? Slot : "button"
48 |
49 | return (
50 |
55 | )
56 | }
57 |
58 | export { Button, buttonVariants }
59 |
--------------------------------------------------------------------------------
/src/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | function Card({ className, ...props }: React.ComponentProps<"div">) {
6 | return (
7 |
15 | )
16 | }
17 |
18 | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19 | return (
20 |
28 | )
29 | }
30 |
31 | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32 | return (
33 |
38 | )
39 | }
40 |
41 | function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42 | return (
43 |
48 | )
49 | }
50 |
51 | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52 | return (
53 |
61 | )
62 | }
63 |
64 | function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65 | return (
66 |
71 | )
72 | }
73 |
74 | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75 | return (
76 |
81 | )
82 | }
83 |
84 | export {
85 | Card,
86 | CardHeader,
87 | CardFooter,
88 | CardTitle,
89 | CardAction,
90 | CardDescription,
91 | CardContent,
92 | }
93 |
--------------------------------------------------------------------------------
/src/components/not-found.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "@tanstack/react-router";
2 | import { ArrowLeft, Home, Search, FileQuestion } from "lucide-react";
3 | import { Button } from "@/components/ui/button";
4 | import { Card, CardContent } from "@/components/ui/card";
5 |
6 | export function NotFound({ children }: { children?: any }) {
7 | return (
8 |
9 |
10 |
11 |
12 | {/* Icon */}
13 |
14 |
15 |
16 |
17 | {/* Heading */}
18 |
19 |
20 | Page Not Found
21 |
22 |
23 | {children || (
24 |
25 | The page you're looking for doesn't exist or has been moved.
26 |
27 | )}
28 |
29 |
30 |
31 | {/* Actions */}
32 |
33 |
window.history.back()}
36 | className="flex items-center gap-2"
37 | >
38 |
39 | Go Back
40 |
41 |
42 |
43 |
44 | Home
45 |
46 |
47 |
48 |
49 | {/* Help text */}
50 |
51 |
52 |
53 |
54 | Try checking the URL or use the search functionality
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## Commands
6 |
7 | ### Development
8 | - `pnpm dev` - Start development server on port 3000
9 | - `pnpm build` - Build for production
10 | - `pnpm serve` - Preview production build
11 | - `pnpm test` - Run tests with Vitest
12 |
13 | ### Shadcn Components
14 | - `pnpx shadcn@latest add ` - Add new Shadcn components (use latest version)
15 |
16 | ## Architecture
17 |
18 | This is a TanStack Start application - a type-safe, client-first, full-stack React framework built on top of:
19 |
20 | ### Core Stack
21 | - **TanStack Router**: File-based routing with type-safe navigation
22 | - **TanStack Query**: Server state management with SSR integration
23 | - **React 19**: Latest React with concurrent features
24 | - **Vite**: Build tool and dev server
25 | - **TypeScript**: Strict type checking enabled
26 | - **Tailwind CSS v4**: Utility-first styling with CSS variables
27 |
28 | ### Project Structure
29 | - `src/routes/` - File-based routes (auto-generates `routeTree.gen.ts`)
30 | - `src/components/` - Reusable React components
31 | - `src/integrations/tanstack-query/` - Query client setup and providers
32 | - `src/lib/utils.ts` - Utility functions (includes clsx/tailwind-merge)
33 | - `src/utils/seo.ts` - SEO helper functions
34 | - Path aliases: `@/*` maps to `src/*`
35 |
36 | ### Key Architecture Patterns
37 |
38 | **Router Setup**: The router is created via `getRouter()` in `src/router.tsx` which integrates TanStack Query context and SSR. Routes are auto-generated from the file system.
39 |
40 | **Query Integration**: TanStack Query is pre-configured with SSR support through `setupRouterSsrQueryIntegration`. The query client is accessible in route contexts.
41 |
42 | **Root Layout**: `src/routes/__root.tsx` defines the HTML document structure, includes devtools, and provides navigation links. It uses `createRootRouteWithContext` for type-safe context passing.
43 |
44 | **Styling**: Uses Tailwind CSS v4 with the Vite plugin. Shadcn components are configured with "new-york" style, Zinc base color, and CSS variables enabled.
45 |
46 | **TypeScript**: Strict mode with additional linting rules (`noUnusedLocals`, `noUnusedParameters`, etc.). Uses modern ESNext module resolution.
47 |
48 | ### Development Notes
49 | - Demo files (prefixed with `demo`) can be safely deleted
50 | - The project uses pnpm as the package manager
51 | - Devtools are included for both Router and Query in development
52 | - Routes support loaders, error boundaries, and not-found components
53 | - File-based routing automatically generates type-safe route definitions
--------------------------------------------------------------------------------
/src/components/landing/hero-section.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button"
2 | import { Badge } from "@/components/ui/badge"
3 | import { ArrowRight, Github } from "lucide-react"
4 |
5 | export function HeroSection() {
6 | return (
7 |
8 |
9 |
10 |
11 | Built with React 19, TypeScript & Vite
12 |
13 |
14 |
15 |
16 | TanStack Start
17 | Template
18 |
19 |
20 |
21 | A modern, type-safe, full-stack React framework combining the power of
22 | TanStack Router and Query with the latest web technologies. Start building
23 | production-ready applications today.
24 |
25 |
26 |
44 |
45 |
46 |
Trusted by developers building modern web applications
47 |
48 |
49 |
50 | {/* Background gradient */}
51 |
60 |
61 | )
62 | }
--------------------------------------------------------------------------------
/src/routes/__root.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | import {
3 | HeadContent,
4 | Outlet,
5 | Scripts,
6 | createRootRouteWithContext,
7 | } from "@tanstack/react-router";
8 | import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
9 | import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
10 | import * as React from "react";
11 | import type { QueryClient } from "@tanstack/react-query";
12 | import { DefaultCatchBoundary } from "@/components/default-catch-boundary";
13 | import { NotFound } from "@/components/not-found";
14 | import { ThemeProvider } from "@/components/theme";
15 | import appCss from "@/styles.css?url";
16 | import { seo } from "@/utils/seo";
17 |
18 | export const Route = createRootRouteWithContext<{
19 | queryClient: QueryClient;
20 | }>()({
21 | head: () => ({
22 | meta: [
23 | {
24 | charSet: "utf-8",
25 | },
26 | {
27 | name: "viewport",
28 | content: "width=device-width, initial-scale=1",
29 | },
30 | ...seo({
31 | title:
32 | "TanStack Start | Type-Safe, Client-First, Full-Stack React Framework",
33 | description: `TanStack Start is a type-safe, client-first, full-stack React framework. `,
34 | }),
35 | ],
36 | links: [
37 | { rel: "stylesheet", href: appCss },
38 | {
39 | rel: "apple-touch-icon",
40 | sizes: "180x180",
41 | href: "/apple-touch-icon.png",
42 | },
43 | {
44 | rel: "icon",
45 | type: "image/png",
46 | sizes: "32x32",
47 | href: "/favicon-32x32.png",
48 | },
49 | {
50 | rel: "icon",
51 | type: "image/png",
52 | sizes: "16x16",
53 | href: "/favicon-16x16.png",
54 | },
55 | { rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
56 | { rel: "icon", href: "/favicon.ico" },
57 | ],
58 | }),
59 | errorComponent: (props) => {
60 | return (
61 |
62 |
63 |
64 | );
65 | },
66 | notFoundComponent: () => ,
67 | component: RootComponent,
68 | });
69 |
70 | function RootComponent() {
71 | return (
72 |
73 |
79 |
80 |
81 |
82 | );
83 | }
84 |
85 | function RootDocument({ children }: { children: React.ReactNode }) {
86 | return (
87 |
88 |
89 |
90 |
91 |
92 | {children}
93 |
94 |
95 |
96 |
97 |
98 | );
99 | }
100 |
--------------------------------------------------------------------------------
/src/components/landing/features-section.tsx:
--------------------------------------------------------------------------------
1 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
2 | import { Badge } from "@/components/ui/badge"
3 | import {
4 | Route,
5 | Database,
6 | Zap,
7 | Shield,
8 | Palette,
9 | Code,
10 | Server,
11 | Layers
12 | } from "lucide-react"
13 |
14 | const features = [
15 | {
16 | icon: Route,
17 | title: "TanStack Router",
18 | description: "Type-safe, file-based routing with powerful features like nested layouts, loaders, and search params validation.",
19 | badge: "Type-Safe"
20 | },
21 | {
22 | icon: Database,
23 | title: "TanStack Query",
24 | description: "Powerful data synchronization with server state management, caching, and background updates built-in.",
25 | badge: "Server State"
26 | },
27 | {
28 | icon: Code,
29 | title: "React 19",
30 | description: "Latest React with concurrent features, improved performance, and modern development patterns.",
31 | badge: "Latest"
32 | },
33 | {
34 | icon: Zap,
35 | title: "Vite",
36 | description: "Lightning-fast build tool with hot module replacement and optimized production builds.",
37 | badge: "Fast"
38 | },
39 | {
40 | icon: Shield,
41 | title: "TypeScript",
42 | description: "Full TypeScript support with strict typing, IntelliSense, and compile-time error checking.",
43 | badge: "Type-Safe"
44 | },
45 | {
46 | icon: Palette,
47 | title: "Tailwind CSS v4",
48 | description: "Modern utility-first CSS framework with CSS variables and a comprehensive design system.",
49 | badge: "Styling"
50 | },
51 | {
52 | icon: Server,
53 | title: "SSR Ready",
54 | description: "Server-side rendering support with seamless hydration and SEO optimization out of the box.",
55 | badge: "Performance"
56 | },
57 | {
58 | icon: Layers,
59 | title: "Shadcn/UI",
60 | description: "Beautiful, accessible component library with customizable themes and modern design patterns.",
61 | badge: "Components"
62 | }
63 | ]
64 |
65 | export function FeaturesSection() {
66 | return (
67 |
68 |
69 |
70 |
71 | Everything you need to build modern web apps
72 |
73 |
74 | A carefully curated stack of the best tools and libraries for React development
75 |
76 |
77 |
78 |
79 | {features.map((feature) => {
80 | const IconComponent = feature.icon
81 | return (
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | {feature.badge}
90 |
91 |
92 | {feature.title}
93 |
94 |
95 |
96 | {feature.description}
97 |
98 |
99 |
100 | )
101 | })}
102 |
103 |
104 |
105 | )
106 | }
--------------------------------------------------------------------------------
/src/components/landing/footer.tsx:
--------------------------------------------------------------------------------
1 | import { Github, ExternalLink } from "lucide-react"
2 |
3 | const navigation = {
4 | main: [
5 | { name: 'TanStack Start', href: 'https://tanstack.com/start' },
6 | { name: 'TanStack Router', href: 'https://tanstack.com/router' },
7 | { name: 'TanStack Query', href: 'https://tanstack.com/query' },
8 | { name: 'React', href: 'https://react.dev' },
9 | ],
10 | tools: [
11 | { name: 'Vite', href: 'https://vitejs.dev' },
12 | { name: 'TypeScript', href: 'https://typescriptlang.org' },
13 | { name: 'Tailwind CSS', href: 'https://tailwindcss.com' },
14 | { name: 'Shadcn/UI', href: 'https://ui.shadcn.com' },
15 | ],
16 | social: [
17 | {
18 | name: 'GitHub',
19 | href: 'https://github.com/tanstack',
20 | icon: Github,
21 | },
22 | ],
23 | }
24 |
25 | export function Footer() {
26 | return (
27 |
28 |
29 |
30 |
31 |
TanStack Ecosystem
32 |
47 |
48 |
49 |
50 |
Development Tools
51 |
66 |
67 |
68 |
69 |
70 |
71 | {navigation.social.map((item) => {
72 | const IconComponent = item.icon
73 | return (
74 |
81 | {item.name}
82 |
83 |
84 | )
85 | })}
86 |
87 |
88 |
89 |
90 | Built with TanStack Start
91 |
92 |
93 | © {new Date().getFullYear()} TanStack. MIT Licensed.
94 |
95 |
96 |
97 |
98 |
99 | )
100 | }
--------------------------------------------------------------------------------
/src/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as SheetPrimitive from "@radix-ui/react-dialog"
3 | import { XIcon } from "lucide-react"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | function Sheet({ ...props }: React.ComponentProps) {
8 | return
9 | }
10 |
11 | function SheetTrigger({
12 | ...props
13 | }: React.ComponentProps) {
14 | return
15 | }
16 |
17 | function SheetClose({
18 | ...props
19 | }: React.ComponentProps) {
20 | return
21 | }
22 |
23 | function SheetPortal({
24 | ...props
25 | }: React.ComponentProps) {
26 | return
27 | }
28 |
29 | function SheetOverlay({
30 | className,
31 | ...props
32 | }: React.ComponentProps) {
33 | return (
34 |
42 | )
43 | }
44 |
45 | function SheetContent({
46 | className,
47 | children,
48 | side = "right",
49 | ...props
50 | }: React.ComponentProps & {
51 | side?: "top" | "right" | "bottom" | "left"
52 | }) {
53 | return (
54 |
55 |
56 |
72 | {children}
73 |
74 |
75 | Close
76 |
77 |
78 |
79 | )
80 | }
81 |
82 | function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
83 | return (
84 |
89 | )
90 | }
91 |
92 | function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
93 | return (
94 |
99 | )
100 | }
101 |
102 | function SheetTitle({
103 | className,
104 | ...props
105 | }: React.ComponentProps) {
106 | return (
107 |
112 | )
113 | }
114 |
115 | function SheetDescription({
116 | className,
117 | ...props
118 | }: React.ComponentProps) {
119 | return (
120 |
125 | )
126 | }
127 |
128 | export {
129 | Sheet,
130 | SheetTrigger,
131 | SheetClose,
132 | SheetContent,
133 | SheetHeader,
134 | SheetFooter,
135 | SheetTitle,
136 | SheetDescription,
137 | }
138 |
--------------------------------------------------------------------------------
/.claude/agents/shadcn-ui-builder.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: shadcn-ui-builder
3 | description: Use this agent when you need to create, modify, or enhance UI components using Shadcn/UI with proper design patterns and file organization. Examples: Context: User wants to create a new dashboard component with cards and charts. user: 'Create a dashboard component with metric cards and a chart section' assistant: 'I'll use the shadcn-ui-builder agent to create a well-structured dashboard component following our design system.' The user needs UI components built, so use the shadcn-ui-builder agent to create components with proper Shadcn patterns, theme colors, and file organization. Context: User needs to improve the UX of an existing form component. user: 'This login form needs better validation feedback and loading states' assistant: 'Let me use the shadcn-ui-builder agent to enhance the form with better UX patterns.' Since this involves UI/UX improvements using Shadcn components, use the shadcn-ui-builder agent.
4 | model: sonnet
5 | color: green
6 | ---
7 |
8 | You are a Senior UI/UX Engineer specializing in Shadcn/UI component development with deep expertise in modern React patterns, accessibility, and design systems. You excel at creating beautiful, functional, and accessible user interfaces that follow best practices.
9 |
10 | Your core responsibilities:
11 | 1. **Component Architecture**: Design and build React components using Shadcn/UI primitives with proper composition patterns
12 | 2. **Design System Adherence**: Ensure all components follow consistent design patterns and use theme-based styling
13 | 3. **File Organization**: Structure components logically within the src/components folder with appropriate subfolders
14 | 4. **Accessibility First**: Build components that are accessible by default with proper ARIA attributes and keyboard navigation
15 | 5. **Performance Optimization**: Create efficient components with proper memoization and lazy loading where appropriate
16 |
17 | **Critical Rules You Must Follow:**
18 | - NEVER hardcode Tailwind colors - always use CSS variables and theme tokens (e.g., `bg-background`, `text-foreground`, `border-border`)
19 | - Use lowercase kebab-case for all file names (e.g., `user-profile.tsx`, not `UserProfile.tsx`)
20 | - Organize components in logical subfolders within `src/components/` (e.g., `src/components/forms/`, `src/components/layout/`)
21 | - Use the Shadcn MCP server to add new components when needed with `pnpx shadcn@latest add `
22 | - Follow the project's Tailwind CSS v4 setup with CSS variables enabled
23 |
24 | **Component Development Process:**
25 | 1. **Analyze Requirements**: Understand the component's purpose, user interactions, and data flow
26 | 2. **Plan Structure**: Determine component hierarchy, props interface, and folder organization
27 | 3. **Design Patterns**: Choose appropriate Shadcn primitives and composition patterns
28 | 4. **Theme Integration**: Use semantic color tokens and spacing from the design system
29 | 5. **Accessibility**: Implement proper ARIA labels, keyboard navigation, and screen reader support
30 | 6. **Responsive Design**: Ensure components work across all device sizes
31 | 7. **Error Handling**: Include proper loading states, error boundaries, and fallbacks
32 |
33 | **Code Quality Standards:**
34 | - Use TypeScript with strict typing for all props and state
35 | - Implement proper error boundaries and loading states
36 | - Follow React 19 patterns including concurrent features when appropriate
37 | - Use semantic HTML elements and proper heading hierarchy
38 | - Implement proper focus management and keyboard navigation
39 | - Include hover, focus, and active states for interactive elements
40 |
41 | **UX Principles:**
42 | - Prioritize user feedback with clear loading, success, and error states
43 | - Implement progressive disclosure for complex interfaces
44 | - Use consistent spacing, typography, and interaction patterns
45 | - Provide clear visual hierarchy and scannable layouts
46 | - Ensure fast perceived performance with skeleton loaders and optimistic updates
47 |
48 | **File Organization Examples:**
49 | - Forms: `src/components/forms/login-form.tsx`, `src/components/forms/contact-form.tsx`
50 | - Layout: `src/components/layout/header.tsx`, `src/components/layout/sidebar.tsx`
51 | - UI Elements: `src/components/ui/custom-button.tsx`, `src/components/ui/data-table.tsx`
52 |
53 | When creating components, always consider the complete user journey, provide clear feedback for all interactions, and ensure the component integrates seamlessly with the existing TanStack Start application architecture. Ask for clarification if requirements are ambiguous, and suggest UX improvements when you identify opportunities to enhance the user experience.
54 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 |
3 | @import 'tw-animate-css';
4 |
5 | @custom-variant dark (&:is(.dark *));
6 |
7 | body {
8 | @apply m-0;
9 | font-family:
10 | -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
11 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | }
15 |
16 | code {
17 | font-family:
18 | source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
19 | }
20 |
21 | :root {
22 | --background: oklch(1 0 0);
23 | --foreground: oklch(0.141 0.005 285.823);
24 | --card: oklch(1 0 0);
25 | --card-foreground: oklch(0.141 0.005 285.823);
26 | --popover: oklch(1 0 0);
27 | --popover-foreground: oklch(0.141 0.005 285.823);
28 | --primary: oklch(0.21 0.006 285.885);
29 | --primary-foreground: oklch(0.985 0 0);
30 | --secondary: oklch(0.967 0.001 286.375);
31 | --secondary-foreground: oklch(0.21 0.006 285.885);
32 | --muted: oklch(0.967 0.001 286.375);
33 | --muted-foreground: oklch(0.552 0.016 285.938);
34 | --accent: oklch(0.967 0.001 286.375);
35 | --accent-foreground: oklch(0.21 0.006 285.885);
36 | --destructive: oklch(0.577 0.245 27.325);
37 | --destructive-foreground: oklch(0.577 0.245 27.325);
38 | --border: oklch(0.92 0.004 286.32);
39 | --input: oklch(0.92 0.004 286.32);
40 | --ring: oklch(0.871 0.006 286.286);
41 | --chart-1: oklch(0.646 0.222 41.116);
42 | --chart-2: oklch(0.6 0.118 184.704);
43 | --chart-3: oklch(0.398 0.07 227.392);
44 | --chart-4: oklch(0.828 0.189 84.429);
45 | --chart-5: oklch(0.769 0.188 70.08);
46 | --radius: 0.625rem;
47 | --sidebar: oklch(0.985 0 0);
48 | --sidebar-foreground: oklch(0.141 0.005 285.823);
49 | --sidebar-primary: oklch(0.21 0.006 285.885);
50 | --sidebar-primary-foreground: oklch(0.985 0 0);
51 | --sidebar-accent: oklch(0.967 0.001 286.375);
52 | --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
53 | --sidebar-border: oklch(0.92 0.004 286.32);
54 | --sidebar-ring: oklch(0.871 0.006 286.286);
55 | }
56 |
57 | .dark {
58 | --background: oklch(0.141 0.005 285.823);
59 | --foreground: oklch(0.985 0 0);
60 | --card: oklch(0.141 0.005 285.823);
61 | --card-foreground: oklch(0.985 0 0);
62 | --popover: oklch(0.141 0.005 285.823);
63 | --popover-foreground: oklch(0.985 0 0);
64 | --primary: oklch(0.985 0 0);
65 | --primary-foreground: oklch(0.21 0.006 285.885);
66 | --secondary: oklch(0.274 0.006 286.033);
67 | --secondary-foreground: oklch(0.985 0 0);
68 | --muted: oklch(0.274 0.006 286.033);
69 | --muted-foreground: oklch(0.705 0.015 286.067);
70 | --accent: oklch(0.274 0.006 286.033);
71 | --accent-foreground: oklch(0.985 0 0);
72 | --destructive: oklch(0.396 0.141 25.723);
73 | --destructive-foreground: oklch(0.637 0.237 25.331);
74 | --border: oklch(0.274 0.006 286.033);
75 | --input: oklch(0.274 0.006 286.033);
76 | --ring: oklch(0.442 0.017 285.786);
77 | --chart-1: oklch(0.488 0.243 264.376);
78 | --chart-2: oklch(0.696 0.17 162.48);
79 | --chart-3: oklch(0.769 0.188 70.08);
80 | --chart-4: oklch(0.627 0.265 303.9);
81 | --chart-5: oklch(0.645 0.246 16.439);
82 | --sidebar: oklch(0.21 0.006 285.885);
83 | --sidebar-foreground: oklch(0.985 0 0);
84 | --sidebar-primary: oklch(0.488 0.243 264.376);
85 | --sidebar-primary-foreground: oklch(0.985 0 0);
86 | --sidebar-accent: oklch(0.274 0.006 286.033);
87 | --sidebar-accent-foreground: oklch(0.985 0 0);
88 | --sidebar-border: oklch(0.274 0.006 286.033);
89 | --sidebar-ring: oklch(0.442 0.017 285.786);
90 | }
91 |
92 | @theme inline {
93 | --color-background: var(--background);
94 | --color-foreground: var(--foreground);
95 | --color-card: var(--card);
96 | --color-card-foreground: var(--card-foreground);
97 | --color-popover: var(--popover);
98 | --color-popover-foreground: var(--popover-foreground);
99 | --color-primary: var(--primary);
100 | --color-primary-foreground: var(--primary-foreground);
101 | --color-secondary: var(--secondary);
102 | --color-secondary-foreground: var(--secondary-foreground);
103 | --color-muted: var(--muted);
104 | --color-muted-foreground: var(--muted-foreground);
105 | --color-accent: var(--accent);
106 | --color-accent-foreground: var(--accent-foreground);
107 | --color-destructive: var(--destructive);
108 | --color-destructive-foreground: var(--destructive-foreground);
109 | --color-border: var(--border);
110 | --color-input: var(--input);
111 | --color-ring: var(--ring);
112 | --color-chart-1: var(--chart-1);
113 | --color-chart-2: var(--chart-2);
114 | --color-chart-3: var(--chart-3);
115 | --color-chart-4: var(--chart-4);
116 | --color-chart-5: var(--chart-5);
117 | --radius-sm: calc(var(--radius) - 4px);
118 | --radius-md: calc(var(--radius) - 2px);
119 | --radius-lg: var(--radius);
120 | --radius-xl: calc(var(--radius) + 4px);
121 | --color-sidebar: var(--sidebar);
122 | --color-sidebar-foreground: var(--sidebar-foreground);
123 | --color-sidebar-primary: var(--sidebar-primary);
124 | --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
125 | --color-sidebar-accent: var(--sidebar-accent);
126 | --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
127 | --color-sidebar-border: var(--sidebar-border);
128 | --color-sidebar-ring: var(--sidebar-ring);
129 | }
130 |
131 | @layer base {
132 | * {
133 | @apply border-border outline-ring/50;
134 | }
135 | body {
136 | @apply bg-background text-foreground;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/components/default-catch-boundary.tsx:
--------------------------------------------------------------------------------
1 | import { Link, rootRouteId, useMatch, useRouter } from "@tanstack/react-router";
2 | import type { ErrorComponentProps } from "@tanstack/react-router";
3 | import {
4 | AlertTriangle,
5 | RefreshCw,
6 | ArrowLeft,
7 | Home,
8 | ChevronDown,
9 | Bug,
10 | Mail,
11 | } from "lucide-react";
12 | import { Button } from "@/components/ui/button";
13 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
14 | import { Alert, AlertDescription } from "@/components/ui/alert";
15 | import {
16 | Collapsible,
17 | CollapsibleContent,
18 | CollapsibleTrigger,
19 | } from "@/components/ui/collapsible";
20 | import { useState } from "react";
21 |
22 | export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
23 | const router = useRouter();
24 | const isRoot = useMatch({
25 | strict: false,
26 | select: (state) => state.id === rootRouteId,
27 | });
28 | const [showDetails, setShowDetails] = useState(false);
29 |
30 | console.error(error);
31 |
32 | // Format error details for display
33 | const errorMessage = error?.message || "An unexpected error occurred";
34 | const errorStack = error?.stack || "";
35 | const hasStack = errorStack.length > 0;
36 |
37 | const handleReportError = () => {
38 | const subject = encodeURIComponent("Error Report");
39 | const body = encodeURIComponent(
40 | `An error occurred in the application:\n\nError: ${errorMessage}\n\nStack Trace:\n${errorStack}\n\nPlease describe what you were doing when this error occurred:`,
41 | );
42 | window.location.href = `mailto:support@example.com?subject=${subject}&body=${body}`;
43 | };
44 |
45 | return (
46 |
47 |
48 |
49 |
50 |
53 |
54 |
Something went wrong
55 |
56 | We encountered an unexpected error. Please try again.
57 |
58 |
59 |
60 |
61 |
62 |
63 | {/* Error Alert */}
64 |
65 |
66 |
67 | {errorMessage}
68 |
69 |
70 |
71 | {/* Action Buttons */}
72 |
73 |
router.invalidate()}
75 | className="flex items-center gap-2"
76 | >
77 |
78 | Try Again
79 |
80 |
81 | {isRoot ? (
82 |
83 |
84 |
85 | Go to Home
86 |
87 |
88 | ) : (
89 |
window.history.back()}
92 | className="flex items-center gap-2"
93 | >
94 |
95 | Go Back
96 |
97 | )}
98 |
99 |
100 | {/* Error Details (Collapsible) */}
101 | {hasStack && (
102 |
103 |
104 |
109 |
110 | Technical Details
111 |
114 |
115 |
116 |
117 |
118 |
119 | Error Stack Trace:
120 |
121 |
122 | {errorStack}
123 |
124 |
125 |
126 |
127 | )}
128 |
129 | {/* Help Section */}
130 |
131 |
132 |
133 | If this error persists, please report it to our support team.
134 |
135 |
141 |
142 | Report Error
143 |
144 |
145 |
146 |
147 |
148 |
149 | );
150 | }
151 |
--------------------------------------------------------------------------------
/src/components/theme/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | type Theme = "dark" | "light" | "system";
4 |
5 | type ThemeProviderProps = {
6 | children: React.ReactNode;
7 | defaultTheme?: Theme;
8 | storageKey?: string;
9 | attribute?: string;
10 | enableSystem?: boolean;
11 | disableTransitionOnChange?: boolean;
12 | };
13 |
14 | type ThemeProviderState = {
15 | theme: Theme;
16 | setTheme: (theme: Theme) => void;
17 | resolvedTheme?: "light" | "dark";
18 | systemTheme?: "light" | "dark";
19 | };
20 |
21 | const initialState: ThemeProviderState = {
22 | theme: "system",
23 | setTheme: () => null,
24 | resolvedTheme: undefined,
25 | systemTheme: undefined,
26 | };
27 |
28 | const ThemeProviderContext = React.createContext(initialState);
29 |
30 | export function ThemeProvider({
31 | children,
32 | defaultTheme = "system",
33 | storageKey = "ui-theme",
34 | attribute = "class",
35 | enableSystem = true,
36 | disableTransitionOnChange = false,
37 | ...props
38 | }: ThemeProviderProps) {
39 | const [theme, setThemeState] = React.useState(() => {
40 | // During SSR, always return the default theme to avoid hydration mismatch
41 | if (typeof window === "undefined") {
42 | return defaultTheme;
43 | }
44 |
45 | // Client-side: try to get theme from localStorage
46 | try {
47 | const stored = localStorage.getItem(storageKey) as Theme;
48 | return stored || defaultTheme;
49 | } catch {
50 | return defaultTheme;
51 | }
52 | });
53 |
54 | const [systemTheme, setSystemTheme] = React.useState<"light" | "dark" | undefined>(() => {
55 | // During SSR, return undefined
56 | if (typeof window === "undefined") {
57 | return undefined;
58 | }
59 |
60 | // Client-side: detect system theme
61 | return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
62 | });
63 |
64 | const [isMounted, setIsMounted] = React.useState(false);
65 |
66 | const resolvedTheme = theme === "system" ? systemTheme : theme;
67 |
68 | const setTheme = React.useCallback(
69 | (newTheme: Theme) => {
70 | try {
71 | localStorage.setItem(storageKey, newTheme);
72 | } catch {
73 | // Ignore localStorage errors
74 | }
75 | setThemeState(newTheme);
76 | },
77 | [storageKey]
78 | );
79 |
80 | const applyTheme = React.useCallback(
81 | (targetTheme: "light" | "dark" | undefined) => {
82 | if (!targetTheme || typeof document === "undefined") return;
83 |
84 | const root = document.documentElement;
85 |
86 | if (disableTransitionOnChange) {
87 | const css = document.createElement("style");
88 | css.appendChild(
89 | document.createTextNode(
90 | `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`
91 | )
92 | );
93 | document.head.appendChild(css);
94 |
95 | // Force reflow
96 | (() => window.getComputedStyle(document.body))();
97 |
98 | setTimeout(() => {
99 | document.head.removeChild(css);
100 | }, 1);
101 | }
102 |
103 | if (attribute === "class") {
104 | root.classList.remove("light", "dark");
105 | root.classList.add(targetTheme);
106 | } else {
107 | root.setAttribute(attribute, targetTheme);
108 | }
109 | },
110 | [attribute, disableTransitionOnChange]
111 | );
112 |
113 | // Apply theme on mount and when resolvedTheme changes
114 | React.useEffect(() => {
115 | if (isMounted) {
116 | applyTheme(resolvedTheme);
117 | }
118 | }, [resolvedTheme, applyTheme, isMounted]);
119 |
120 | // Handle system theme changes
121 | React.useEffect(() => {
122 | if (!enableSystem || typeof window === "undefined") return;
123 |
124 | const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
125 |
126 | const handleSystemThemeChange = (e: MediaQueryListEvent) => {
127 | setSystemTheme(e.matches ? "dark" : "light");
128 | };
129 |
130 | mediaQuery.addEventListener("change", handleSystemThemeChange);
131 |
132 | return () => {
133 | mediaQuery.removeEventListener("change", handleSystemThemeChange);
134 | };
135 | }, [enableSystem]);
136 |
137 | // Hydration effect - apply theme immediately on client
138 | React.useEffect(() => {
139 | setIsMounted(true);
140 |
141 | // Immediately apply the correct theme on hydration
142 | const currentTheme = theme === "system" ? systemTheme : theme;
143 | applyTheme(currentTheme);
144 | }, [theme, systemTheme, applyTheme]);
145 |
146 | // Prevent flash during SSR by applying theme via script
147 | React.useEffect(() => {
148 | if (typeof document === "undefined") return;
149 |
150 | // Create a script that runs before React hydration to prevent FOIT
151 | const script = document.createElement("script");
152 | script.innerHTML = `
153 | try {
154 | var theme = localStorage.getItem('${storageKey}') || '${defaultTheme}';
155 | var systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
156 | var resolvedTheme = theme === 'system' ? systemTheme : theme;
157 |
158 | if (resolvedTheme === 'dark') {
159 | document.documentElement.classList.add('dark');
160 | document.documentElement.classList.remove('light');
161 | } else {
162 | document.documentElement.classList.add('light');
163 | document.documentElement.classList.remove('dark');
164 | }
165 | } catch (e) {}
166 | `;
167 |
168 | // Only add if not already present
169 | if (!document.querySelector(`script[data-theme-script]`)) {
170 | script.setAttribute('data-theme-script', 'true');
171 | document.head.appendChild(script);
172 | }
173 | }, [storageKey, defaultTheme]);
174 |
175 | const value = React.useMemo(
176 | () => ({
177 | theme,
178 | setTheme,
179 | resolvedTheme: isMounted ? resolvedTheme : undefined,
180 | systemTheme: isMounted ? systemTheme : undefined,
181 | }),
182 | [theme, setTheme, resolvedTheme, systemTheme, isMounted]
183 | );
184 |
185 | return (
186 |
187 | {children}
188 |
189 | );
190 | }
191 |
192 | export const useTheme = () => {
193 | const context = React.useContext(ThemeProviderContext);
194 |
195 | if (context === undefined) {
196 | throw new Error("useTheme must be used within a ThemeProvider");
197 | }
198 |
199 | return context;
200 | };
--------------------------------------------------------------------------------
/src/components/theme/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Monitor, Moon, Sun, Check } from "lucide-react";
3 |
4 | import { Button } from "@/components/ui/button";
5 | import {
6 | DropdownMenu,
7 | DropdownMenuContent,
8 | DropdownMenuItem,
9 | DropdownMenuTrigger,
10 | } from "@/components/ui/dropdown-menu";
11 | import { useTheme } from "./theme-provider";
12 |
13 | interface ThemeToggleProps {
14 | variant?: "default" | "outline" | "ghost";
15 | size?: "sm" | "default" | "lg";
16 | showLabel?: boolean;
17 | align?: "start" | "center" | "end";
18 | }
19 |
20 | export function ThemeToggle({
21 | variant = "ghost",
22 | size = "default",
23 | showLabel = false,
24 | align = "end",
25 | }: ThemeToggleProps) {
26 | const { theme, setTheme, resolvedTheme } = useTheme();
27 |
28 | // Animation variants for icons
29 | const iconVariants = {
30 | sun: "transition-all duration-500 ease-in-out",
31 | moon: "transition-all duration-500 ease-in-out",
32 | system: "transition-all duration-300 ease-in-out",
33 | };
34 |
35 | const getCurrentIcon = () => {
36 | if (theme === "system") {
37 | return (
38 |
42 | );
43 | }
44 |
45 | if (resolvedTheme === "dark") {
46 | return (
47 |
51 | );
52 | }
53 |
54 | return (
55 |
59 | );
60 | };
61 |
62 | const themeOptions = [
63 | {
64 | value: "light",
65 | label: "Light",
66 | icon: Sun,
67 | description: "Use light theme",
68 | },
69 | {
70 | value: "dark",
71 | label: "Dark",
72 | icon: Moon,
73 | description: "Use dark theme",
74 | },
75 | {
76 | value: "system",
77 | label: "System",
78 | icon: Monitor,
79 | description: "Use system theme",
80 | },
81 | ] as const;
82 |
83 | const handleThemeSelect = (newTheme: typeof theme) => {
84 | setTheme(newTheme);
85 | };
86 |
87 | return (
88 |
89 |
90 |
101 |
102 | {getCurrentIcon()}
103 |
104 | {showLabel && (
105 |
106 | {themeOptions.find(option => option.value === theme)?.label}
107 |
108 | )}
109 |
110 | Current theme: {theme === "system" ? `System (${resolvedTheme})` : theme}
111 |
112 |
113 |
114 |
115 |
119 |
120 | {themeOptions.map((option) => {
121 | const Icon = option.icon;
122 | const isSelected = theme === option.value;
123 |
124 | return (
125 |
handleThemeSelect(option.value)}
128 | className={`
129 | flex items-center gap-3 px-3 py-2.5 cursor-pointer
130 | transition-all duration-200 ease-in-out
131 | hover:bg-accent/80 focus:bg-accent/80
132 | rounded-md group
133 | ${isSelected ? 'bg-accent/60 text-accent-foreground' : ''}
134 | `}
135 | >
136 |
137 |
144 |
145 |
146 |
147 |
151 | {option.label}
152 |
153 |
154 | {option.description}
155 |
156 |
157 |
158 | {isSelected && (
159 |
160 | )}
161 |
162 | );
163 | })}
164 |
165 |
166 | {resolvedTheme && (
167 |
168 |
169 |
173 | Currently using {resolvedTheme} theme
174 |
175 |
176 | )}
177 |
178 |
179 | );
180 | }
181 |
182 | // Simplified version for minimal use cases
183 | export function ThemeToggleSimple() {
184 | const { theme, setTheme, resolvedTheme } = useTheme();
185 |
186 | const handleToggle = () => {
187 | if (theme === "light") {
188 | setTheme("dark");
189 | } else if (theme === "dark") {
190 | setTheme("system");
191 | } else {
192 | setTheme("light");
193 | }
194 | };
195 |
196 | return (
197 |
209 |
210 | {theme === "system" && (
211 |
212 | )}
213 | {resolvedTheme === "dark" && theme !== "system" && (
214 |
215 | )}
216 | {resolvedTheme === "light" && theme !== "system" && (
217 |
218 | )}
219 |
220 |
221 | Current theme: {theme === "system" ? `System (${resolvedTheme})` : theme}
222 |
223 |
224 | );
225 | }
--------------------------------------------------------------------------------
/src/components/navigation/navigation-bar.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Link } from "@tanstack/react-router";
3 | import { Menu, Github, ExternalLink } from "lucide-react";
4 | import { Button } from "@/components/ui/button";
5 | import {
6 | Sheet,
7 | SheetContent,
8 | SheetDescription,
9 | SheetHeader,
10 | SheetTitle,
11 | SheetTrigger,
12 | } from "@/components/ui/sheet";
13 | import { cn } from "@/lib/utils";
14 | import { ThemeToggle } from "@/components/theme";
15 |
16 | interface NavigationItem {
17 | label: string;
18 | href: string;
19 | isExternal?: boolean;
20 | scrollTo?: string;
21 | }
22 |
23 | const navigationItems: NavigationItem[] = [
24 | { label: "Features", href: "#features", scrollTo: "features" },
25 | {
26 | label: "Documentation",
27 | href: "https://tanstack.com/start/latest/docs/framework/react/overview",
28 | isExternal: true,
29 | },
30 | {
31 | label: "GitHub",
32 | href: "https://github.com/backpine/tanstack-start-on-cloudflare",
33 | isExternal: true,
34 | },
35 | ];
36 |
37 | export function NavigationBar() {
38 | const [isOpen, setIsOpen] = React.useState(false);
39 | const [isScrolled, setIsScrolled] = React.useState(false);
40 |
41 | React.useEffect(() => {
42 | const handleScroll = () => {
43 | setIsScrolled(window.scrollY > 20);
44 | };
45 |
46 | window.addEventListener("scroll", handleScroll);
47 | return () => window.removeEventListener("scroll", handleScroll);
48 | }, []);
49 |
50 | const handleSmoothScroll = (elementId: string) => {
51 | const element = document.getElementById(elementId);
52 | if (element) {
53 | element.scrollIntoView({
54 | behavior: "smooth",
55 | block: "start",
56 | });
57 | }
58 | };
59 |
60 | const handleNavClick = (item: NavigationItem) => {
61 | if (item.scrollTo) {
62 | handleSmoothScroll(item.scrollTo);
63 | }
64 | setIsOpen(false);
65 | };
66 |
67 | return (
68 |
76 |
77 |
78 | {/* Logo and Brand */}
79 |
83 |
84 |
85 | TanStack Start
86 |
87 |
88 | on CLOUDFLARE
89 |
90 |
91 |
92 |
93 | {/* Desktop Navigation */}
94 |
95 | {navigationItems.map((item) => (
96 |
121 | ))}
122 |
123 | {/* Theme Toggle */}
124 |
125 |
126 |
127 |
128 |
129 | {/* CTA Button - Desktop */}
130 |
131 |
132 | {/* Mobile Menu Button + Theme Toggle */}
133 |
134 |
135 |
136 |
137 |
142 |
143 | Open navigation menu
144 |
145 |
146 |
150 |
151 |
152 | Navigation
153 |
154 |
155 | Explore TanStack Start
156 |
157 |
158 |
159 |
160 | {navigationItems.map((item) => (
161 |
186 | ))}
187 |
188 |
189 | {/* Mobile CTA */}
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | );
198 | }
199 |
--------------------------------------------------------------------------------
/.claude/agents/tanstack-server-functions.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: tanstack-server-functions
3 | description: Expert agent for TanStack Start server functions and middleware patterns. Use this agent when working with server-side logic, middleware composition, data fetching, validation, authentication, and the full-stack TanStack Start architecture. Examples: Context: User wants to create authenticated API endpoints. user: 'Create server functions with authentication middleware' assistant: 'I'll use the tanstack-server-functions agent to create secure server endpoints with proper middleware composition.' This involves server functions and middleware patterns, so use the tanstack-server-functions agent. Context: User needs data validation and error handling. user: 'Set up form submission with server validation using Zod' assistant: 'Let me use the tanstack-server-functions agent to create a robust form handler with validation.' Server-side validation and form handling requires server functions expertise.
4 | model: sonnet
5 | color: blue
6 | ---
7 |
8 | You are a Senior Full-Stack Engineer specializing in TanStack Start server functions and middleware architecture. You have deep expertise in server-side patterns, middleware composition, data validation, authentication, and full-stack type safety.
9 |
10 | Your core responsibilities:
11 | 1. **Server Function Architecture**: Design and implement server functions with proper input validation, error handling, and type safety
12 | 2. **Middleware Composition**: Create composable middleware chains for authentication, logging, validation, and context management
13 | 3. **Full-Stack Integration**: Seamlessly integrate server functions with client-side TanStack Query mutations and queries
14 | 4. **Security Patterns**: Implement secure server-side logic with proper validation and sanitization
15 | 5. **Performance Optimization**: Create efficient server functions with proper caching and optimization strategies
16 |
17 | **Critical Rules You Must Follow:**
18 | - ALWAYS use TypeScript with strict typing for server functions and middleware
19 | - Use Zod or similar schema validation for all server function inputs
20 | - ALWAYS define explicit types using `z.infer` and pass to inputValidator for proper TypeScript inference
21 | - Follow the established pattern: `src/core/middleware/` for middleware, `src/core/functions/` for server functions
22 | - Create composable middleware chains using the base function pattern
23 | - Implement proper error handling and logging for debugging
24 | - Use kebab-case for file names (e.g., `auth-middleware.ts`, `user-functions.ts`)
25 |
26 | **TanStack Start Server Functions Fundamentals:**
27 |
28 | Server functions in TanStack Start are server-only logic that can be called from anywhere in your application while maintaining full type safety across network boundaries.
29 |
30 | **Basic Pattern:**
31 | ```typescript
32 | import { createServerFn } from '@tanstack/react-start'
33 | import { z } from 'zod'
34 |
35 | const InputSchema = z.object({
36 | data: z.string().min(1),
37 | })
38 |
39 | type InputType = z.infer
40 |
41 | export const myServerFunction = createServerFn()
42 | .inputValidator((data: InputType) => InputSchema.parse(data))
43 | .handler(async (ctx) => {
44 | // Server-only logic here
45 | return 'Response data'
46 | })
47 | ```
48 |
49 | **Middleware Composition Pattern:**
50 | ```typescript
51 | // Middleware
52 | import { createMiddleware } from '@tanstack/react-start'
53 |
54 | export const authMiddleware = createMiddleware({
55 | type: 'function'
56 | }).server(async ({ next }) => {
57 | // Authentication logic
58 | return next({
59 | context: {
60 | user: authenticatedUser
61 | }
62 | })
63 | })
64 |
65 | // Base function with middleware
66 | const baseFunction = createServerFn().middleware([
67 | authMiddleware,
68 | ])
69 |
70 | // Server function using base
71 | type InputType = z.infer
72 |
73 | export const protectedFunction = baseFunction
74 | .inputValidator((data: InputType) => Schema.parse(data))
75 | .handler(async (ctx) => {
76 | // Access ctx.context.user from middleware
77 | return result
78 | })
79 | ```
80 |
81 | **Key Capabilities:**
82 |
83 | 1. **Input Validation**
84 | - Always validate server function inputs with schemas
85 | - Use Zod for runtime type checking
86 | - Provide clear error messages for invalid inputs
87 |
88 | 2. **Middleware Types**
89 | - **Request Middleware**: Applies to all server routes and functions
90 | - **Function Middleware**: More granular control for specific server functions
91 | - **Composable Chains**: Middleware can depend on other middleware
92 |
93 | 3. **Error Handling**
94 | - Throw errors that serialize properly to client
95 | - Support for redirects and "not found" responses
96 | - Automatic error handling in route lifecycles
97 |
98 | 4. **Advanced Features**
99 | - Access request headers and environment variables
100 | - Handle form submissions and file uploads
101 | - Support for streaming responses
102 | - Request cancellation support
103 |
104 | **Established Patterns in This Project:**
105 |
106 | 1. **File Organization:**
107 | ```
108 | src/core/
109 | ├── middleware/
110 | │ ├── auth-middleware.ts
111 | │ ├── logging-middleware.ts
112 | │ └── example-middleware.ts
113 | └── functions/
114 | ├── user-functions.ts
115 | ├── auth-functions.ts
116 | └── example-functions.ts
117 | ```
118 |
119 | 2. **Middleware Pattern:**
120 | ```typescript
121 | export const exampleMiddleware = createMiddleware({
122 | type: 'function'
123 | }).server(async ({ next }) => {
124 | console.log('Middleware executing')
125 | return next({
126 | context: {
127 | data: 'Middleware context'
128 | }
129 | })
130 | })
131 | ```
132 |
133 | 3. **Base Function Pattern:**
134 | ```typescript
135 | const baseFunction = createServerFn().middleware([
136 | exampleMiddleware,
137 | ])
138 |
139 | type InputType = z.infer
140 |
141 | export const myFunction = baseFunction
142 | .inputValidator((data: InputType) => Schema.parse(data))
143 | .handler(async (ctx) => {
144 | // Access ctx.data (validated input)
145 | // Access ctx.context (from middleware)
146 | return response
147 | })
148 | ```
149 |
150 | 4. **Client Integration with TanStack Query:**
151 | ```typescript
152 | const mutation = useMutation({
153 | mutationFn: myServerFunction,
154 | onSuccess: (data) => {
155 | console.log('Server function executed:', data)
156 | }
157 | })
158 | ```
159 |
160 | **Best Practices:**
161 |
162 | 1. **Security First**
163 | - Always validate inputs on the server
164 | - Never trust client-side data
165 | - Implement proper authentication and authorization
166 | - Log server-side execution for debugging
167 |
168 | 2. **Error Handling**
169 | - Use try-catch blocks for external API calls
170 | - Return meaningful error messages
171 | - Log errors for debugging
172 | - Handle edge cases gracefully
173 |
174 | 3. **Performance**
175 | - Use efficient database queries
176 | - Implement proper caching strategies
177 | - Consider rate limiting for public endpoints
178 | - Optimize for Cloudflare Workers environment
179 |
180 | 4. **Type Safety**
181 | - Use strict TypeScript throughout
182 | - Define clear input/output types
183 | - Leverage Zod for runtime validation
184 | - Maintain type safety across client-server boundary
185 |
186 | **Common Use Cases:**
187 |
188 | 1. **Authentication & Authorization**
189 | - User login/logout functions
190 | - JWT token validation
191 | - Role-based access control
192 | - Session management
193 |
194 | 2. **Data Operations**
195 | - CRUD operations with validation
196 | - Database queries with proper error handling
197 | - File upload and processing
198 | - External API integrations
199 |
200 | 3. **Form Processing**
201 | - Contact form submissions
202 | - User registration/profile updates
203 | - File uploads with validation
204 | - Multi-step form handling
205 |
206 | 4. **Background Tasks**
207 | - Email sending
208 | - Data processing
209 | - Scheduled tasks
210 | - Webhook handling
211 |
212 | **Environment-Specific Considerations:**
213 |
214 | This project runs on Cloudflare Workers, which provides:
215 | - Edge computing capabilities
216 | - KV storage for caching
217 | - Durable Objects for stateful logic
218 | - R2 for object storage
219 | - Access to `env` variables via `cloudflare:workers`
220 |
221 | **Debugging Tips:**
222 |
223 | 1. **Server Logs**: Always log middleware and function execution
224 | 2. **Client Errors**: Use proper error handling in TanStack Query
225 | 3. **Network Inspector**: Monitor requests in browser dev tools
226 | 4. **Type Checking**: Leverage TypeScript for compile-time validation
227 | 5. **Environment Variables**: Use `console.log(env)` to debug Cloudflare environment
228 |
229 | When implementing server functions and middleware, always consider security, performance, and developer experience. Create composable, reusable patterns that can be easily extended and maintained. Focus on type safety and proper error handling throughout the full-stack data flow.
--------------------------------------------------------------------------------
/src/components/ui/dropdown-menu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
3 | import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | function DropdownMenu({
8 | ...props
9 | }: React.ComponentProps) {
10 | return
11 | }
12 |
13 | function DropdownMenuPortal({
14 | ...props
15 | }: React.ComponentProps) {
16 | return (
17 |
18 | )
19 | }
20 |
21 | function DropdownMenuTrigger({
22 | ...props
23 | }: React.ComponentProps) {
24 | return (
25 |
29 | )
30 | }
31 |
32 | function DropdownMenuContent({
33 | className,
34 | sideOffset = 4,
35 | ...props
36 | }: React.ComponentProps) {
37 | return (
38 |
39 |
48 |
49 | )
50 | }
51 |
52 | function DropdownMenuGroup({
53 | ...props
54 | }: React.ComponentProps) {
55 | return (
56 |
57 | )
58 | }
59 |
60 | function DropdownMenuItem({
61 | className,
62 | inset,
63 | variant = "default",
64 | ...props
65 | }: React.ComponentProps & {
66 | inset?: boolean
67 | variant?: "default" | "destructive"
68 | }) {
69 | return (
70 |
80 | )
81 | }
82 |
83 | function DropdownMenuCheckboxItem({
84 | className,
85 | children,
86 | checked,
87 | ...props
88 | }: React.ComponentProps) {
89 | return (
90 |
99 |
100 |
101 |
102 |
103 |
104 | {children}
105 |
106 | )
107 | }
108 |
109 | function DropdownMenuRadioGroup({
110 | ...props
111 | }: React.ComponentProps) {
112 | return (
113 |
117 | )
118 | }
119 |
120 | function DropdownMenuRadioItem({
121 | className,
122 | children,
123 | ...props
124 | }: React.ComponentProps) {
125 | return (
126 |
134 |
135 |
136 |
137 |
138 |
139 | {children}
140 |
141 | )
142 | }
143 |
144 | function DropdownMenuLabel({
145 | className,
146 | inset,
147 | ...props
148 | }: React.ComponentProps & {
149 | inset?: boolean
150 | }) {
151 | return (
152 |
161 | )
162 | }
163 |
164 | function DropdownMenuSeparator({
165 | className,
166 | ...props
167 | }: React.ComponentProps) {
168 | return (
169 |
174 | )
175 | }
176 |
177 | function DropdownMenuShortcut({
178 | className,
179 | ...props
180 | }: React.ComponentProps<"span">) {
181 | return (
182 |
190 | )
191 | }
192 |
193 | function DropdownMenuSub({
194 | ...props
195 | }: React.ComponentProps) {
196 | return
197 | }
198 |
199 | function DropdownMenuSubTrigger({
200 | className,
201 | inset,
202 | children,
203 | ...props
204 | }: React.ComponentProps & {
205 | inset?: boolean
206 | }) {
207 | return (
208 |
217 | {children}
218 |
219 |
220 | )
221 | }
222 |
223 | function DropdownMenuSubContent({
224 | className,
225 | ...props
226 | }: React.ComponentProps) {
227 | return (
228 |
236 | )
237 | }
238 |
239 | export {
240 | DropdownMenu,
241 | DropdownMenuPortal,
242 | DropdownMenuTrigger,
243 | DropdownMenuContent,
244 | DropdownMenuGroup,
245 | DropdownMenuLabel,
246 | DropdownMenuItem,
247 | DropdownMenuCheckboxItem,
248 | DropdownMenuRadioGroup,
249 | DropdownMenuRadioItem,
250 | DropdownMenuSeparator,
251 | DropdownMenuShortcut,
252 | DropdownMenuSub,
253 | DropdownMenuSubTrigger,
254 | DropdownMenuSubContent,
255 | }
256 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TanStack Start on Cloudflare
2 |
3 | A modern, full-stack React application built with TanStack Start and deployed on Cloudflare Workers. This template showcases server functions, middleware, type-safe data fetching, and seamless integration with Cloudflare's edge computing platform.
4 |
5 | [](https://www.youtube.com/watch?v=TWWS_lo4kOA)
6 |
7 | ## 🚀 Quick Start
8 |
9 | ```bash
10 | # Install dependencies
11 | pnpm install
12 |
13 | # Start development server
14 | pnpm dev
15 |
16 | # Build for production
17 | pnpm build
18 |
19 | # Deploy to Cloudflare
20 | pnpm deploy
21 | ```
22 |
23 | ## 📦 Development Workflow
24 |
25 | This project provides a comprehensive development workflow with the following scripts:
26 |
27 | - **`pnpm dev`** - Start development server on port 3000
28 | - **`pnpm build`** - Build the application for production
29 | - **`pnpm deploy`** - Build and deploy to Cloudflare Workers
30 | - **`pnpm serve`** - Preview production build locally
31 | - **`pnpm cf-typegen`** - Generate TypeScript types for Cloudflare environment
32 |
33 | ## 🌩️ Cloudflare Integration
34 |
35 | ### Environment Variables & Type Generation
36 |
37 | This project includes full TypeScript support for Cloudflare Workers environment variables:
38 |
39 | ```bash
40 | # Generate types for Cloudflare environment
41 | pnpm cf-typegen
42 | ```
43 |
44 | This creates type definitions allowing you to safely import and use Cloudflare environment variables:
45 |
46 | ```typescript
47 | import { env } from "cloudflare:workers";
48 |
49 | // Now env is fully typed with your Wrangler configuration
50 | console.log(env.MY_VAR); // TypeScript knows this exists
51 | ```
52 |
53 | ### Wrangler Configuration
54 |
55 | The `wrangler.jsonc` file configures your Cloudflare deployment:
56 |
57 | ```jsonc
58 | {
59 | "$schema": "node_modules/wrangler/config-schema.json",
60 | "name": "tanstack-start-app",
61 | "compatibility_date": "2025-09-02",
62 | "compatibility_flags": ["nodejs_compat"],
63 | "main": "./src/server.ts", // Custom server entry point
64 | "vars": {
65 | "MY_VAR": "Hello from Cloudflare"
66 | }
67 | }
68 | ```
69 |
70 | ### Custom Server Entry (`src/server.ts`)
71 |
72 | The `src/server.ts` file is your custom Cloudflare Workers entry point where you can add additional Cloudflare features:
73 |
74 | ```typescript
75 | import handler from "@tanstack/react-start/server-entry";
76 |
77 | export default {
78 | fetch(request: Request) {
79 | return handler.fetch(request, {
80 | context: {
81 | fromFetch: true,
82 | },
83 | });
84 | },
85 |
86 | // Add other Cloudflare Workers features:
87 | // - Queue consumers: queue(batch, env) { ... }
88 | // - Scheduled events: scheduled(event, env) { ... }
89 | // - Durable Object handlers
90 | // - etc.
91 | };
92 | ```
93 |
94 | ## 🎨 Styling & Components
95 |
96 | ### Tailwind CSS v4
97 | This project uses the latest Tailwind CSS v4 with CSS variables for theming:
98 |
99 | ```bash
100 | # Tailwind is pre-configured with the @tailwindcss/vite plugin
101 | # CSS variables are enabled for theme customization
102 | ```
103 |
104 | ### Shadcn/UI Components
105 | Add beautiful, accessible components using Shadcn/UI:
106 |
107 | ```bash
108 | # Add individual components
109 | pnpx shadcn@latest add button
110 | pnpx shadcn@latest add card
111 | pnpx shadcn@latest add form
112 |
113 | # Components use semantic color tokens and CSS variables
114 | # Perfect for light/dark theme support
115 | ```
116 |
117 |
118 |
119 | ## 🗂️ File-Based Routing
120 |
121 | This project uses [TanStack Router](https://tanstack.com/router/latest) with file-based routing. Routes are automatically generated from files in the `src/routes` directory:
122 |
123 | ### Adding A Route
124 |
125 | To add a new route to your application just add another a new file in the `./src/routes` directory.
126 |
127 | TanStack will automatically generate the content of the route file for you.
128 |
129 | Now that you have two routes you can use a `Link` component to navigate between them.
130 |
131 | ### Adding Links
132 |
133 | To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
134 |
135 | ```tsx
136 | import { Link } from "@tanstack/react-router";
137 | ```
138 |
139 | Then anywhere in your JSX you can use it like so:
140 |
141 | ```tsx
142 | About
143 | ```
144 |
145 | This will create a link that will navigate to the `/about` route.
146 |
147 | More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
148 |
149 | ### Using A Layout
150 |
151 | In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the ` ` component.
152 |
153 | Here is an example layout that includes a header:
154 |
155 | ```tsx
156 | import { Outlet, createRootRoute } from '@tanstack/react-router'
157 | import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
158 |
159 | import { Link } from "@tanstack/react-router";
160 |
161 | export const Route = createRootRoute({
162 | component: () => (
163 | <>
164 |
165 |
166 | Home
167 | About
168 |
169 |
170 |
171 |
172 | >
173 | ),
174 | })
175 | ```
176 |
177 | The ` ` component is not required so you can remove it if you don't want it in your layout.
178 |
179 | More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
180 |
181 |
182 | ## 🔄 Data Fetching & Server Functions
183 |
184 | This template demonstrates modern server-side patterns with TanStack Start's server functions, middleware, and seamless client integration.
185 |
186 | ### Server Functions with Middleware
187 |
188 | Server functions run exclusively on the server and maintain type safety across network boundaries:
189 |
190 | ```typescript
191 | // src/core/middleware/example-middleware.ts
192 | export const exampleMiddleware = createMiddleware({
193 | type: 'function'
194 | }).server(async ({ next }) => {
195 | console.log('Middleware executing on server');
196 | return next({
197 | context: {
198 | data: 'Context from middleware'
199 | }
200 | });
201 | });
202 |
203 | // src/core/functions/example-functions.ts
204 | const ExampleInputSchema = z.object({
205 | exampleKey: z.string().min(1),
206 | });
207 |
208 | type ExampleInput = z.infer;
209 |
210 | const baseFunction = createServerFn().middleware([
211 | exampleMiddleware,
212 | ]);
213 |
214 | export const exampleFunction = baseFunction
215 | .inputValidator((data: ExampleInput) => ExampleInputSchema.parse(data))
216 | .handler(async (ctx) => {
217 | // Access validated input: ctx.data
218 | // Access middleware context: ctx.context
219 | // Access Cloudflare env: env.MY_VAR
220 | return 'Server response';
221 | });
222 | ```
223 |
224 | ### Client Integration with TanStack Query
225 |
226 | Server functions integrate seamlessly with TanStack Query for optimal UX:
227 |
228 | ```tsx
229 | import { useMutation } from '@tanstack/react-query';
230 | import { exampleFunction } from '@/core/functions/example-functions';
231 |
232 | function MyComponent() {
233 | const mutation = useMutation({
234 | mutationFn: exampleFunction,
235 | onSuccess: (data) => console.log('Success:', data),
236 | onError: (error) => console.error('Error:', error),
237 | });
238 |
239 | return (
240 | mutation.mutate({ exampleKey: 'Hello Server!' })}
242 | disabled={mutation.isPending}
243 | >
244 | {mutation.isPending ? 'Loading...' : 'Call Server Function'}
245 |
246 | );
247 | }
248 | ```
249 |
250 | ### Key Benefits
251 |
252 | - **🔒 Type-Safe**: Full TypeScript support with Zod validation
253 | - **🚀 Server-First**: Secure server-side logic with client convenience
254 | - **⚡ Edge Computing**: Runs on Cloudflare's global edge network
255 | - **🔄 Seamless Integration**: Works perfectly with TanStack Query
256 | - **🧩 Composable**: Middleware chains for auth, logging, validation
257 |
258 | ### Interactive Demo
259 |
260 | This template includes a live demo showcasing the middleware and server function patterns. Check your server logs when running the demo to see the execution flow!
261 |
262 | ## 🧪 Testing
263 |
264 | This project uses [Vitest](https://vitest.dev/) for fast unit and integration testing:
265 |
266 | ```bash
267 | # Run tests
268 | pnpm test
269 |
270 | # Test configuration is in vite.config.ts
271 | # Uses jsdom environment for DOM testing
272 | # Includes @testing-library/react for component testing
273 | ```
274 |
275 | ## 📋 Tech Stack
276 |
277 | This template includes the latest and greatest from the React ecosystem:
278 |
279 | ### **Core Framework**
280 | - **TanStack Start** - Full-stack React framework with SSR
281 | - **React 19** - Latest React with concurrent features
282 | - **TypeScript** - Strict type checking enabled
283 |
284 | ### **Routing & Data**
285 | - **TanStack Router** - Type-safe, file-based routing
286 | - **TanStack Query** - Server state management with SSR integration
287 |
288 | ### **Styling & UI**
289 | - **Tailwind CSS v4** - Utility-first CSS with CSS variables
290 | - **Shadcn/UI** - Beautiful, accessible component library
291 | - **Lucide React** - Consistent icon set
292 |
293 | ### **Development Tools**
294 | - **Vite** - Lightning-fast build tool and dev server
295 | - **Vitest** - Unit testing with jsdom
296 | - **TypeScript** - Full type safety across client and server
297 |
298 | ### **Deployment**
299 | - **Cloudflare Workers** - Edge computing platform
300 | - **Wrangler** - Cloudflare deployment and development CLI
301 |
302 | ## 🚀 Learn More
303 |
304 | - **[TanStack Start](https://tanstack.com/start)** - Full-stack React framework
305 | - **[TanStack Router](https://tanstack.com/router)** - Type-safe routing
306 | - **[TanStack Query](https://tanstack.com/query)** - Server state management
307 | - **[Cloudflare Workers](https://workers.cloudflare.com/)** - Edge computing platform
308 | - **[Shadcn/UI](https://ui.shadcn.com/)** - Component library
309 | - **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS
310 |
311 | ## 📄 License
312 |
313 | This template is open source and available under the [MIT License](LICENSE).
314 |
--------------------------------------------------------------------------------
/src/components/demo/middleware-demo.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useMutation } from "@tanstack/react-query";
3 | import { Button } from "@/components/ui/button";
4 | import {
5 | Card,
6 | CardContent,
7 | CardDescription,
8 | CardHeader,
9 | CardTitle,
10 | } from "@/components/ui/card";
11 | import { Badge } from "@/components/ui/badge";
12 | import { Alert, AlertDescription } from "@/components/ui/alert";
13 | import {
14 | Loader2,
15 | Play,
16 | Server,
17 | Zap,
18 | CheckCircle,
19 | AlertCircle,
20 | Code2,
21 | } from "lucide-react";
22 | import { examplefunction } from "@/core/functions/example-functions";
23 |
24 | export function MiddlewareDemo() {
25 | const [inputValue, setInputValue] = React.useState("Hello TanStack Start!");
26 |
27 | const mutation = useMutation({
28 | mutationFn: examplefunction,
29 | onSuccess: (data) => {
30 | console.log("Client: Server function executed successfully:", data);
31 | },
32 | onError: (error) => {
33 | console.error("Client: Server function failed:", error);
34 | },
35 | });
36 |
37 | const handleExecute = () => {
38 | mutation.mutate({
39 | data: {
40 | exampleKey: "exampleValue",
41 | },
42 | });
43 | };
44 |
45 | return (
46 |
47 |
48 |
49 |
50 |
51 | Server Functions & Middleware
52 |
53 |
54 | Server-Side Data Flow
55 |
56 |
57 | See TanStack Start's middleware and server functions in action with
58 | TanStack Query. Check your server logs to see the execution flow!
59 |
60 |
61 |
62 |
63 |
64 | {/* Demo Card */}
65 |
66 |
67 |
68 |
69 | Interactive Demo
70 |
71 |
72 | Execute a server function with middleware through TanStack
73 | Query
74 |
75 |
76 |
77 |
78 |
82 | Message to Send
83 |
84 | setInputValue(e.target.value)}
89 | className="w-full px-3 py-2 border border-border rounded-lg bg-background focus:ring-2 focus:ring-primary focus:border-transparent transition-colors"
90 | placeholder="Enter a message..."
91 | />
92 |
93 |
94 |
99 | {mutation.isPending ? (
100 |
101 | ) : (
102 |
103 | )}
104 | Execute Server Function
105 |
106 |
107 | {/* Status Display */}
108 |
109 | {mutation.isPending && (
110 |
111 |
112 |
113 | Executing server function with middleware...
114 |
115 |
116 | )}
117 |
118 | {mutation.isSuccess && (
119 |
120 |
121 |
122 | Success! Response: "{mutation.data}"
123 |
124 |
125 | )}
126 |
127 | {mutation.isError && (
128 |
129 |
130 |
131 | Error: {" "}
132 | {mutation.error?.message || "Something went wrong"}
133 |
134 |
135 | )}
136 |
137 |
138 |
139 |
140 | {/* Architecture Info */}
141 |
142 |
143 |
144 |
145 | What's Happening
146 |
147 |
148 | The execution flow and server-side processing
149 |
150 |
151 |
152 |
153 |
154 |
155 | Execution Flow
156 |
157 |
158 |
159 |
160 | 1
161 |
162 |
163 | Client sends request via TanStack Query mutation
164 |
165 |
166 |
167 |
168 | 2
169 |
170 |
171 | Middleware executes first (adds context data)
172 |
173 |
174 |
175 |
176 | 3
177 |
178 | Input validation with Zod schema
179 |
180 |
181 |
182 | 4
183 |
184 | Server function handler executes
185 |
186 |
187 |
188 | 5
189 |
190 | Response sent back to client
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 | Check your server logs! You'll see
200 | console output from both the middleware and server
201 | function execution.
202 |
203 |
204 |
205 |
206 |
207 |
208 | Files involved:
209 |
210 |
211 |
212 | • src/core/middleware/example-middleware.ts
213 |
214 |
215 | • src/core/functions/example-functions.ts
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | {/* Additional Info */}
225 |
226 |
227 |
228 | Key Benefits
229 |
230 |
231 |
232 |
Type-Safe
233 |
234 | Full TypeScript support with Zod validation
235 |
236 |
237 |
238 |
Server-First
239 |
240 | Execute secure server-side logic seamlessly
241 |
242 |
243 |
244 |
Middleware Ready
245 |
246 | Composable middleware for authentication, logging, etc.
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 | );
255 | }
256 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | logo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------