-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
feat: TanStack Start cursorrules #250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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: () => ( | ||
| <html lang="en"> | ||
| <head /> | ||
| <body> | ||
| <Outlet /> | ||
| <ScrollRestoration /> | ||
| <Scripts /> | ||
| </body> | ||
| </html> | ||
| ), | ||
| }) | ||
| ``` | ||
|
|
||
| ## 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 ( | ||
| <div> | ||
| <h1>{post.title}</h1> | ||
| <Suspense fallback={<CommentsSkeleton />}> | ||
| <Await promise={comments}>{(c) => <CommentsList comments={c} />}</Await> | ||
| </Suspense> | ||
| </div> | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| ## 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 | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -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 ( | ||||||
| <html lang="en"> | ||||||
| <head /> | ||||||
| <body> | ||||||
| <Outlet /> | ||||||
| <ScrollRestoration /> | ||||||
| <Scripts /> | ||||||
| {process.env.NODE_ENV === 'development' && ( | ||||||
|
||||||
| {process.env.NODE_ENV === 'development' && ( | |
| {import.meta.env.DEV && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
In TanStack Start (Vite-based), what is the recommended way to gate dev-only UI inside route components: process.env.NODE_ENVorimport.meta.env.DEV?
💡 Result:
In TanStack Start (Vite-based), the recommended way to gate dev-only UI inside route components is import.meta.env.DEV.
Citations:
- 1: https://tanstack.com/start/latest/docs/framework/react/guide/environment-variables
- 2: https://tanstack.com/router/latest/docs/how-to/use-environment-variables
- 3: https://tanstack.com/devtools/latest/docs/framework/react/examples/start%3Fpath%3Dexamples%252Freact%252Fstart%252Fsrc%252Fcomponents%252Fdevtools.tsx
🏁 Script executed:
# First, find the .cursorrules file
find . -name ".cursorrules" -type fRepository: PatrickJS/awesome-cursorrules
Length of output: 11792
🏁 Script executed:
# Read the relevant lines from the .cursorrules file
# Get lines around 68-73 and line 223
if [ -f "rules/tanstack-start-cursorrules-prompt-file/.cursorrules" ]; then
echo "=== Lines 65-75 (context around line 68-73) ==="
sed -n '65,75p' "rules/tanstack-start-cursorrules-prompt-file/.cursorrules" | cat -n
echo ""
echo "=== Lines 220-225 (context around line 223) ==="
sed -n '220,225p' "rules/tanstack-start-cursorrules-prompt-file/.cursorrules" | cat -n
fiRepository: PatrickJS/awesome-cursorrules
Length of output: 814
Fix contradictory env guidance in client-rendered example.
Line 223 states "Never access process.env in client components," but Lines 68-73 use process.env.NODE_ENV in the RootComponent. This violates the stated guideline. In TanStack Start (Vite-based), use import.meta.env.DEV for dev-only rendering checks.
Suggested doc fix
- {process.env.NODE_ENV === 'development' && (
+ {import.meta.env.DEV && (
<>
<TanStackRouterDevtools />
<ReactQueryDevtools />
</>
)}Also applies to: 220-223
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@rules/tanstack-start-cursorrules-prompt-file/.cursorrules` around lines 68 -
73, The client RootComponent currently checks process.env.NODE_ENV to
conditionally render TanStackRouterDevtools and ReactQueryDevtools which
contradicts the guideline to never access process.env in client components;
replace that check by using the Vite/TanStack-safe flag import.meta.env.DEV in
the RootComponent (the conditional around TanStackRouterDevtools and
ReactQueryDevtools) so dev-only rendering uses import.meta.env.DEV instead of
process.env.NODE_ENV.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # TanStack Start Cursor Rules | ||
|
|
||
| Cursor rules for TanStack Start — the full-stack React framework built on TanStack Router and Vinxi. Covers server functions, API routes, streaming, SSR, authentication, and multi-platform deployment. | ||
|
|
||
| ## What's covered | ||
| - Project structure and `app.config.ts` setup | ||
| - Root route HTML shell configuration | ||
| - `createServerFn` with Zod validation | ||
| - Using server functions in loaders and mutation handlers | ||
| - Raw API routes with `createAPIFileRoute` | ||
| - Streaming with `defer()` and Suspense | ||
| - Auth via server-side cookie/session access | ||
| - Deployment targets (Vercel, Netlify, Cloudflare, Bun, Node) | ||
| - TanStack Query integration for cache-first data loading | ||
|
|
||
| ## Author | ||
| Created by [usm4nhafeez](https://github.com/usm4nhafeez) | ||
|
|
||
| Contributed to [awesome-cursorrules](https://github.com/PatrickJS/awesome-cursorrules) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frontmatter
globsis expressed as a YAML inline array here, but other.mdcfiles inrules-new/use a plain string/comma-separated pattern forglobs(e.g.rules-new/nextjs.mdc). To avoid inconsistent parsing/ingestion, consider switching this to the same string format used elsewhere in the repo.