Skip to main content
skill CX Codex

tanstack-start · tanstack-skills — Claude Code skill: what it does, how to install

Guides agents through building full-stack React apps with TanStack Start, streaming SSR, and type-safe server functions.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add tanstack-skills/tanstack-skills --skill tanstack-start -a claude-code
Open on GitHub

What tanstack-start does

This skill supplies coding agents with architectural conventions and patterns for TanStack Start. The stack pairs TanStack Router with Vinxi (Vite plus Nitro) via @tanstack/react-start. It instructs the agent on proper file routing under app/routes, configuring app.config.ts, and maintaining routeTree.gen.ts.

It standardizes backend communication using createServerFn with Zod schema validation, context-driven auth chains via createMiddleware, and custom endpoints via createAPIFileRoute. Routing logic covers streaming SSR patterns that split instant data from deferred promises using defer and Suspense.

The framework is currently in Release Candidate status, and React Server Components support is still under active development. Keep in mind that server functions extract into separate bundles: they must return serializable data and cannot capture client variables in closures.

What it helps with

  • set up streaming SSR with deferred data fetching using Suspense
  • write type-safe RPC endpoints using createServerFn with Zod validation
  • implement auth guard middleware with typed context passing
  • configure Nitro deployment presets for Node.js, Vercel, or Cloudflare Pages

Example prompts for the agent

  • Create a POST server function with Zod validation for updating user profiles
  • Write an auth middleware that redirects unauthenticated users to /login
  • Set up a streaming route loader with defer for dashboard analytics

How to install tanstack-start

1Skills CLI (Vercel)

npx skills add tanstack-skills/tanstack-skills --skill tanstack-start -a claude-code

2Manually: put the files in place

~/.claude/skills/tanstack-start/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/tanstack-start/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about tanstack-start

How to install tanstack-start in Claude Code?

Run npx skills add tanstack-skills/tanstack-skills --skill tanstack-start -a claude-code. The skill file will be placed in ~/.claude/skills/tanstack-start/SKILL.md.

What does the tanstack-start skill do?

It teaches coding assistants the project layout, RPC patterns, SSR streaming setups, and deployment configs for the TanStack Start framework.

Are React Server Components supported in TanStack Start?

RSC support is currently in development. It is slated to arrive as a non-breaking addition during the v1.x release cycle.

Source text

SKILL.md · MIT
---
name: tanstack-start
description: Full-stack React framework powered by TanStack Router with SSR, streaming, server functions, and deployment to any hosting provider.
---

# TanStack Start Skills

## Overview

TanStack Start is a full-stack React framework built on TanStack Router, powered by Vite and Nitro (via Vinxi). It provides server-side rendering, streaming, server functions (RPC), middleware, API routes, and deploys to any platform via Nitro presets.

**Package:** `@tanstack/react-start`
**Router Plugin:** `@tanstack/router-plugin`
**Build Tool:** Vinxi (Vite + Nitro)
**Status:** RC (Release Candidate)
**RSC Support:** React Server Components support is in active development and will land as a non-breaking v1.x addition

## Installation & Project Setup

```bash
npx @tanstack/cli create my-app
# Or manually:
npm install @tanstack/react-start @tanstack/react-router react react-dom
npm install -D @tanstack/router-plugin typescript vite vite-tsconfig-paths
```

### Project Structure

```
my-app/
  app/
    routes/
      __root.tsx          # Root layout
      index.tsx           # / route
      posts.$postId.tsx   # /posts/:postId
      api/
        users.ts          # /api/users API route
    client.tsx            # Client entry
    router.tsx            # Router creation
    ssr.tsx               # SSR entry
    routeTree.gen.ts      # Auto-generated route tree
  app.config.ts           # TanStack Start config
  tsconfig.json
  package.json
```

### Configuration (`app.config.ts`)

```typescript
import { defineConfig } from '@tanstack/react-start/config'
import viteTsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  vite: {
    plugins: [
      viteTsConfigPaths({ projects: ['./tsconfig.json'] }),
    ],
  },
  server: {
    preset: 'node-server', // 'vercel' | 'netlify' | 'cloudflare-pages' | etc.
  },
  tsr: {
    appDirectory: './app',
    routesDirectory: './app/routes',
    generatedRouteTree: './app/routeTree.gen.ts',
  },
})
```

## Server Functions (`createServerFn`)

Server functions provide type-safe RPC calls between client and server.

### Basic Server Functions

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

// GET (data fetching, cacheable)
const getUsers = createServerFn()
  .handler(async () => {
    const users = await db.query.users.findMany()
    return users
  })

// POST (mutations, side effects)
const createUser = createServerFn({ method: 'POST' })
  .validator((data: { name: string; email: string }) => data)
  .handler(async ({ data }) => {
    const user = await db.insert(users).values(data).returning()
    return user
  })
```

### With Zod Validation

```typescript
import { z } from 'zod'

const updateUser = createServerFn({ method: 'POST' })
  .validator(
    z.object({
      id: z.string(),
      name: z.string().min(1),
      email: z.string().email(),
    })
  )
  .handler(async ({ data }) => {
    // data is fully typed: { id: string; name: string; email: string }
    return await db.update(users).set(data).where(eq(users.id, data.id))
  })
```

## Middleware

### Creating Middleware

```typescript
import { createMiddleware } from '@tanstack/react-start'

const loggingMiddleware = createMiddleware().handler(async ({ next }) => {
  console.log('Request started')
  const result = await next()
  console.log('Request completed')
  return result
})
```

### Auth Middleware with Context

```typescript
const authMiddleware = createMiddleware().handler(async ({ next }) => {
  const request = getWebRequest()
  const session = await getSession(request)

  if (!session?.user) {
    throw redirect({ to: '/login' })
  }

  // Pass typed context to handler
  return next({ context: { user: session.user } })
})
```

### Chaining Middleware

```typescript
const adminMiddleware = createMiddleware()
  .middleware([authMiddleware])
  .handler(async ({ next, context }) => {
    // context.user is typed from authMiddleware
    if (context.user.role !== 'admin') {
      throw redirect({ to: '/unauthorized' })
    }
    return next({ context: { isAdmin: true } })
  })

// Usage
const adminAction = createServerFn({ method: 'POST' })
  .middleware([adminMiddleware])
  .handler(async ({ context }) => {
    // context: { user: User; isAdmin: boolean }
    return { success: true }
  })
```

## API Routes (Server Routes)

```typescript
// app/routes/api/users.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'

export const APIRoute = createAPIFileRoute('/api/users')({
  GET: async ({ request }) => {
    const users = await db.query.users.findMany()
    return Response.json(users)
  },
  POST: async ({ request }) => {
    const body = await request.json()
    const user = await db.insert(users).values(body).returning()
    return new Response(JSON.stringify(user), { status: 201 })
  },
})
```

## SSR Strategies

### Streaming SSR (Default)

```typescript
export const Route = createFileRoute('/dashboard')({
  loader: async () => ({
    criticalData: await fetchCriticalData(),
    deferredData: defer(fetchSlowData()),
  }),
  component: Dashboard,
})

function Dashboard() {
  const { criticalData, deferredData } = Route.useLoaderData()
  return (
    <div>
      <CriticalSection data={criticalData} />
      <Suspense fallback={<Loading />}>
        <Await promise={deferredData}>
          {(data) => <SlowSection data={data} />}
        </Await>
      </Suspense>
    </div>
  )
}
```

## Deployment

### Supported Platforms (Nitro Presets)

```typescript
// app.config.ts
export default defineConfig({
  server: {
    preset: 'node-server',        // Self-hosted Node.js
    // preset: 'vercel',          // Vercel
    // preset: 'netlify',         // Netlify
    // preset: 'cloudflare-pages', // Cloudflare Pages
    // preset: 'aws-lambda',      // AWS Lambda
    // preset: 'deno-server',     // Deno Deploy
    // preset: 'bun',             // Bun
  },
})
```

## Best Practices

1. **Use validators for all server function inputs** - runtime safety and TypeScript inference
2. **Compose middleware** for cross-cutting concerns (auth, logging, rate limiting)
3. **Use `createServerFn` GET** for data fetching (cacheable, preloadable)
4. **Use `createServerFn` POST** for mutations and side effects
5. **Use `beforeLoad`** for route-level auth guards
6. **Use `defer()`** for non-critical data to improve TTFB
7. **Set `defaultPreload: 'intent'`** on the router for instant navigation
8. **Co-locate server functions** with the routes that use them

## Common Pitfalls

- Server functions cannot close over client-side variables (they're extracted to separate bundles)
- Data returned from server functions must be serializable
- Forgetting `await` in loaders leads to streaming issues
- Importing server-only code in client bundles causes build errors
- Missing `declare module '@tanstack/react-router'` loses all type safety

© tanstack-skills · MIT · tanstack-skills/tanstack-skills

Similar skills

Everything for Frontend and UI →

tanstack-start by other authors comes first.

From the same set12

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor