machichdigital

IDE-Tools · Foto: Homedust, CC BY 2.0

RegelCursor RulesLizenz: CC0 1.0frei kopierbar

Tanstack Start

Zuletzt aktualisiert:

⬇ Als Datei laden

⧉ –× kopiert⬇ –× heruntergeladenBewertung:

Typ

Regel

Lizenz

CC0 1.0

Anwendungsfeld

Cursor Rules

Voraussetzungen

Keine besonderen — direkt loslegen.

Full-Stack-Konventionen für TanStack Start: SSR, Server-Functions und Deployment-Setup.

Original-Beschreibung der Autoren: Cursor rules for TanStack Start full-stack React framework including server functions, API routes, streaming with defer(), SSR, and multi-platform deployment.

Die Regel

---
description: "Cursor rules for TanStack Start full-stack React framework including server functions, API routes, streaming with defer(), SSR, and multi-platform deployment."
globs: **/*
alwaysApply: false
---
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

// 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' && (
          <>
            <TanStackRouterDevtools />
            <ReactQueryDevtools />
          </>
        )}
      </body>
    </html>
  )
}

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
// 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

// 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 <article><h1>{post.title}</h1></article>
}

Mutations with Server Functions

  • Call server functions directly in event handlers or via TanStack Query mutations
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 (
    <form onSubmit={(e) => {
      e.preventDefault()
      const fd = new FormData(e.currentTarget)
      mutation.mutate({ title: fd.get('title') as string, body: fd.get('body') as string })
    }}>
      <input name="title" />
      <textarea name="body" />
      <button type="submit" disabled={mutation.isPending}>
        {mutation.isPending ? 'Creating...' : 'Create'}
      </button>
    </form>
  )
}

API Routes

  • Use createAPIFileRoute for raw HTTP endpoints (webhooks, third-party integrations)
  • Place in src/routes/api/ directory
// src/routes/api/webhook.ts
import { createAPIFileRoute } from '@tanstack/start/api'

export const Route = createAPIFileRoute('/api/webhook')({
  POST: async ({ request }) => {
    const body = await request.json()
    // handle webhook
    return Response.json({ received: true })
  },
})

Streaming & Suspense

  • Use defer() to stream non-critical data after the initial render
  • Wrap deferred data consumers in <Suspense>
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 (deferred)
    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}>
          {(resolved) => <C
… (hier gekürzt — Kopieren/Download liefert die vollständige Regel)

So nutzt du sie

Die Regel kopieren (Button oben) oder als Datei herunterladen und im Projekt unter .cursor/rules/ ablegen — Cursor lädt sie beim nächsten Start automatisch. Ältere Cursor-Versionen lesen alternativ eine einzelne .cursorrules-Datei im Projektstamm; dort einfach den Regel-Text ohne den Kopfblock zwischen den ----Zeilen einfügen.

Der Regel-Text ist englisch — Cursor versteht ihn unabhängig von der Sprache, in der Sie mit dem Editor chatten.

Im Detail

Deckt die Full-Stack-Seite von TanStack Start ab: Server-Side-Rendering, typsichere Server-Functions statt separater API-Routen sowie das projektspezifische Deployment-Setup. Die Regel sorgt dafür, dass die KI Server- und Client-Code sauber trennt, Server-Functions mit korrekter Typisierung erstellt und nicht versehentlich Next.js- oder Remix-Muster überträgt, die in TanStack Start anders gelöst sind. Besonders wertvoll für Teams, die früh auf das noch junge Framework setzen und wollen, dass generierter Code sich an dessen spezifische Server-Function- und Routing-Architektur hält statt an etablierte, aber andersartige Full-Stack-Frameworks.

Praxis-Tipp

Lassen Sie neue Backend-Logik explizit als Server-Function anfragen, etwa „erstelle eine Server-Function zum Speichern eines Kommentars“, statt eine separate API-Route zu verlangen.

Lizenz & Quelle

Inhalt ansehen (tanstack-start.mdc)
Lade …

Erfahrungen & Kommentare.

Funktioniert der Regel bei Ihnen? Tipps, Stolperfallen, Varianten — teilen Sie es mit der Community.

Lade Kommentare …

Ihre IP-Adresse wird zum Schutz vor Missbrauch gespeichert und nach 14 Tagen automatisch entfernt (Datenschutz).

Passt dazu.