diff --git a/README.md b/README.md index 63c6a2ff..ea65d3b8 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ By creating a `.cursorrules` file in your project's root directory, you can leve - [Python (Flask JSON Guide)](./rules/python-flask-json-guide-cursorrules-prompt-file/.cursorrules) - Cursor rules for Python Flask development with JSON guide. - [Python LLM & ML Workflow](./rules/python-llm-ml-workflow-cursorrules-prompt-file/.cursorrules) - Cursor rules for Python LLM & ML development with workflow integration. - [Salesforce (Apex)](./rules/salesforce-apex-cursorrules-prompt-file/.cursorrules.txt) - Cursor rules for Salesforce development with Apex integration. +- [TanStack Start](./rules/tanstack-start-cursorrules-prompt-file/.cursorrules) - Cursor rules for TanStack Start full-stack React framework including server functions, API routes, streaming with defer(), SSR, and multi-platform deployment. - [TypeScript (NestJS Best Practices)](./rules/typescript-nestjs-best-practices-cursorrules-promp/.cursorrules) - Cursor rules for TypeScript development with NestJS best practices. - [TYPO3 CMS Extension](./rules/typo3cms-extension-cursorrules-prompt-file/.cursorrules) - Cursor rules for TYPO3 CMS development with extension integration. - [WordPress (PHP, Guzzle, Gutenberg)](./rules/wordpress-php-guzzle-gutenberg-cursorrules-prompt-/.cursorrules) - Cursor rules for WordPress development with PHP, Guzzle, and Gutenberg integration. diff --git a/rules-new/tanstack-start.mdc b/rules-new/tanstack-start.mdc new file mode 100644 index 00000000..0185b9a5 --- /dev/null +++ b/rules-new/tanstack-start.mdc @@ -0,0 +1,124 @@ +--- +description: TanStack Start full-stack React framework using server functions, API routes, SSR, streaming with defer(), and multi-platform deployment via Vinxi/Nitro +globs: ["src/routes/**/*", "src/server/**/*", "app.config.ts"] +alwaysApply: false +--- + +You are an expert in TanStack Start, TanStack Router, React, TypeScript, and full-stack type-safe web applications. + +## Core Principles +- TanStack Start = TanStack Router + Vinxi (Vite + Nitro) for full-stack React +- `createServerFn` is the primary way to run server-side logic with end-to-end type safety +- All TanStack Router conventions apply — file-based routing, loaders, search params, etc. +- Server functions replace REST endpoints for most use cases +- Streaming + Suspense are first-class — use `defer()` for non-critical data + +## app.config.ts +```ts +import { defineConfig } from '@tanstack/start/config' +import tsConfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + vite: { plugins: [tsConfigPaths()] }, + server: { + preset: 'node-server', // or: 'vercel', 'netlify', 'bun', 'cloudflare-pages' + }, +}) +``` + +## Root Route HTML Shell +```tsx +// src/routes/__root.tsx +export const Route = createRootRoute({ + component: () => ( + + + + + + + + + ), +}) +``` + +## Server Functions +```ts +// src/server/functions/posts.ts +export const getPost = createServerFn() + .validator(z.object({ id: z.string() })) + .handler(async ({ data }) => { + const post = await db.post.findUnique({ where: { id: data.id } }) + if (!post) throw new Error('Post not found') + return post + }) + +export const createPost = createServerFn() + .validator(z.object({ title: z.string().min(1), body: z.string() })) + .handler(async ({ data }) => db.post.create({ data })) +``` + +## Using Server Functions in Routes +```tsx +export const Route = createFileRoute('/posts/$postId')({ + loader: ({ params }) => getPost({ data: { id: params.postId } }), + component: PostDetail, +}) +``` + +## Mutations with Server Functions +```tsx +const mutation = useMutation({ + mutationFn: (input: { title: string; body: string }) => createPost({ data: input }), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }), +}) +``` + +## API Routes (for webhooks / raw HTTP) +```ts +// src/routes/api/webhook.ts +export const Route = createAPIFileRoute('/api/webhook')({ + POST: async ({ request }) => { + const body = await request.json() + return Response.json({ received: true }) + }, +}) +``` + +## Streaming with defer() +```tsx +export const Route = createFileRoute('/posts/$postId')({ + loader: async ({ params }) => { + const post = await getPost({ data: { id: params.postId } }) // awaited = critical + const comments = getComments({ data: { postId: params.postId } }) // not awaited + return { post, comments: defer(comments) } + }, + component: PostDetail, +}) + +function PostDetail() { + const { post, comments } = Route.useLoaderData() + return ( +
+

{post.title}

+ }> + {(c) => } + +
+ ) +} +``` + +## Environment Variables +- Access server-only vars via `process.env` inside server functions only +- Use `import.meta.env.VITE_*` for client-exposed variables +- Never access `process.env` in client components + +## Deployment Targets +Configure `server.preset` in `app.config.ts`: +- `node-server` — default Node.js +- `vercel` — Vercel serverless/edge +- `netlify` — Netlify Functions +- `bun` — Bun runtime +- `cloudflare-pages` — Cloudflare Pages + Workers diff --git a/rules/tanstack-start-cursorrules-prompt-file/.cursorrules b/rules/tanstack-start-cursorrules-prompt-file/.cursorrules new file mode 100644 index 00000000..69ab520e --- /dev/null +++ b/rules/tanstack-start-cursorrules-prompt-file/.cursorrules @@ -0,0 +1,228 @@ +You are an expert in TanStack Start, TanStack Router, React, TypeScript, Vinxi, and full-stack type-safe web applications. + +# TanStack Start Guidelines + +## What is TanStack Start +TanStack Start is a full-stack React framework built on top of TanStack Router and Vinxi (Vite + Nitro). It provides SSR, streaming, server functions, and API routes with end-to-end type safety. + +## Core Principles +- TanStack Start is file-based routing via TanStack Router — all routing conventions apply +- Server Functions (`createServerFn`) are the primary way to run server-side logic +- Full-stack type safety: server function inputs/outputs are typed end-to-end +- Streaming and Suspense are first-class — use them for progressive rendering +- Start is NOT an API-first framework — server functions replace REST endpoints for most use cases + +## Project Structure +``` +src/ + routes/ + __root.tsx ← Root layout with HTML shell + index.tsx ← Home route + posts/ + index.tsx + $postId.tsx + server/ + functions/ ← Server functions (recommended organization) + posts.ts + auth.ts + lib/ + db.ts ← Database client + auth.ts ← Auth utilities +app.config.ts ← TanStack Start / Vinxi config +``` + +## app.config.ts +```ts +import { defineConfig } from '@tanstack/start/config' +import tsConfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + vite: { + plugins: [tsConfigPaths()], + }, + server: { + preset: 'node-server', // or 'vercel', 'netlify', 'bun', 'cloudflare-pages' + }, +}) +``` + +## Root Route Setup +```tsx +// src/routes/__root.tsx +import { createRootRoute, ScrollRestoration, Scripts, Outlet } from '@tanstack/react-router' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { TanStackRouterDevtools } from '@tanstack/router-devtools' + +export const Route = createRootRoute({ + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + + + + {process.env.NODE_ENV === 'development' && ( + <> + + + + )} + + + ) +} +``` + +## Server Functions +- Use `createServerFn` to define functions that always run on the server +- Validate inputs with Zod using `.validator()` +- Use `.handler()` for the implementation +- Server functions are called like regular async functions from components or loaders +```ts +// src/server/functions/posts.ts +import { createServerFn } from '@tanstack/start' +import { z } from 'zod' + +export const getPost = createServerFn() + .validator(z.object({ id: z.string() })) + .handler(async ({ data }) => { + const post = await db.post.findUnique({ where: { id: data.id } }) + if (!post) throw new Error('Post not found') + return post + }) + +export const createPost = createServerFn() + .validator(z.object({ title: z.string().min(1), body: z.string() })) + .handler(async ({ data, context }) => { + // context has access to request headers, cookies, etc. + return db.post.create({ data }) + }) +``` + +## Using Server Functions in Routes +```tsx +// src/routes/posts/$postId.tsx +import { createFileRoute } from '@tanstack/react-router' +import { getPost } from '../../server/functions/posts' + +export const Route = createFileRoute('/posts/$postId')({ + loader: ({ params }) => getPost({ data: { id: params.postId } }), + component: PostDetail, +}) + +function PostDetail() { + const post = Route.useLoaderData() + return

{post.title}

+} +``` + +## Mutations with Server Functions +- Call server functions directly in event handlers or via TanStack Query mutations +```tsx +import { useMutation, useQueryClient } from '@tanstack/react-query' +import { createPost } from '../../server/functions/posts' + +function CreatePostForm() { + const queryClient = useQueryClient() + const mutation = useMutation({ + mutationFn: (input: { title: string; body: string }) => + createPost({ data: input }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['posts'] }) + }, + }) + + return ( +
{ + e.preventDefault() + const fd = new FormData(e.currentTarget) + mutation.mutate({ title: fd.get('title') as string, body: fd.get('body') as string }) + }}> + +