tanstack-start

📁 fellipeutaka/leon 📅 10 days ago
8
总安装量
8
周安装量
#34407
全站排名
安装命令
npx skills add https://github.com/fellipeutaka/leon --skill tanstack-start

Agent 安装分布

opencode 8
antigravity 8
claude-code 8
github-copilot 8
codex 8
kimi-cli 8

Skill 文档

TanStack Start

Version: @tanstack/react-start@1.x Requires: React 18.0+, TypeScript 5.0+, Vite, TanStack Router

Quick Setup

npm install @tanstack/react-start @tanstack/react-router
npm install -D @tanstack/router-plugin

Vite Config

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'

export default defineConfig({
  plugins: [tanstackStart(), react()],
})

Root Route

// src/routes/__root.tsx
import {
  HeadContent,
  Outlet,
  Scripts,
  createRootRoute,
} from '@tanstack/react-router'

export const Route = createRootRoute({
  head: () => ({
    meta: [
      { charSet: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
    ],
  }),
  component: RootComponent,
})

function RootComponent() {
  return (
    <html lang="en">
      <head>
        <HeadContent />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  )
}

Devtools

Standalone (TanStack Router only):

npm install -D @tanstack/react-router-devtools
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

// In root layout, before </body>
<TanStackRouterDevtools position="bottom-right" />
<Scripts />

Unified TanStack Devtools (recommended with multiple TanStack libraries):

If using Start + Query (or other TanStack libraries), use the unified TanStackDevtools shell instead of individual devtools components:

npm install -D @tanstack/react-devtools
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
import { TanStackDevtools } from '@tanstack/react-devtools'

// In root layout, before </body>
<TanStackDevtools
  config={{ position: 'bottom-right' }}
  plugins={[
    { name: 'TanStack Router', render: <TanStackRouterDevtoolsPanel /> },
    // Add more plugins: Query, etc.
  ]}
/>
<Scripts />

Use *Panel variants (TanStackRouterDevtoolsPanel, ReactQueryDevtoolsPanel) when embedding inside TanStackDevtools.

Rule Categories

Priority Rule Description
CRITICAL sf-server-functions createServerFn, GET/POST methods, handler pattern
HIGH sf-middleware createMiddleware, client/server execution, chaining
HIGH sf-validation Input validation with Zod/Standard Schema, trust boundaries
HIGH ssr-streaming Streaming SSR with Suspense, TTFB optimization
HIGH seo-head-management head() per route, meta tags, HeadContent, OG tags
HIGH auth-patterns beforeLoad guards, session server fns, cookie forwarding
MEDIUM ssr-hydration Hydration mismatch prevention, client-only components
MEDIUM ssr-prerendering Static prerendering, ISR via Cache-Control
MEDIUM api-routes createAPIFileRoute, REST handlers, webhooks
MEDIUM deployment Hosting adapters, env vars, production config
MEDIUM config-project-setup Vite plugin, root route, project structure

Critical Rules

ALWAYS use createServerFn for server-side code

import { createServerFn } from '@tanstack/react-start'

const getUser = createServerFn({ method: 'GET' })
  .handler(async () => {
    return db.users.findFirst()
  })

ALWAYS validate server function inputs

const updateUser = createServerFn({ method: 'POST' })
  .inputValidator((data: { name: string; email: string }) => data)
  .handler(async ({ data }) => {
    return db.users.update({ where: { email: data.email }, data })
  })

NEVER access window/document in components rendered on the server

// BAD — hydration mismatch
function Bad() {
  return <span>{window.innerWidth}px</span>
}

// GOOD — use useEffect for client-only state
function Good() {
  const [width, setWidth] = useState<number | null>(null)
  useEffect(() => setWidth(window.innerWidth), [])
  return <span>{width ?? '...'}px</span>
}

ALWAYS use head() for SEO metadata

export const Route = createFileRoute('/about')({
  head: () => ({
    meta: [
      { title: 'About Us' },
      { name: 'description', content: 'Learn about our company' },
    ],
  }),
})

Key Patterns

Auth Guard with Redirect

// routes/_authed.tsx
export const Route = createFileRoute('/_authed')({
  beforeLoad: async ({ location }) => {
    const user = await getCurrentUserFn()
    if (!user) {
      throw redirect({
        to: '/login',
        search: { redirect: location.href },
      })
    }
    return { user }
  },
})

Server Function with Validation

const createPost = createServerFn({ method: 'POST' })
  .inputValidator((data: { title: string; body: string }) => data)
  .handler(async ({ data }) => {
    const post = await db.posts.create({ data })
    return post
  })

Streaming Dashboard

export const Route = createFileRoute('/dashboard')({
  loader: async ({ context: { queryClient } }) => {
    // Await critical data
    await queryClient.ensureQueryData(userQueries.profile())
    // Prefetch non-critical (streams in via Suspense)
    queryClient.prefetchQuery(statsQueries.dashboard())
  },
  component: DashboardPage,
})

function DashboardPage() {
  const { data: user } = useSuspenseQuery(userQueries.profile())
  return (
    <div>
      <Header user={user} />
      <Suspense fallback={<StatsSkeleton />}>
        <DashboardStats />
      </Suspense>
    </div>
  )
}