tanstack-start
Build full-stack React apps with TanStack Start. Use when a user asks to create a full-stack React application with type-safe server functions, set up file-based routing with SSR/SSG/SPA modes, build APIs with middleware and validation, implement server-side data fetching with TanStack Router, or deploy to Cloudflare/Netlify/Vercel/Node.
What this skill does
# TanStack Start
## Overview
TanStack Start is a full-stack React framework built on TanStack Router and Vite. It provides type-safe server functions (`createServerFn`), file-based routing with loaders, composable middleware, Zod validation across the network boundary, and flexible rendering modes (SSR, SSG, SPA, ISR). Unlike Next.js, it uses Vite (not Webpack), supports any deployment target, and gives you explicit control over server/client code boundaries.
## Instructions
### Step 1: Project Setup
```bash
npx create-start@latest my-app
cd my-app
npm install
npm run dev
```
```text
my-app/
├── app/
│ ├── routes/
│ │ ├── __root.tsx # Root layout
│ │ ├── index.tsx # / route
│ │ ├── about.tsx # /about route
│ │ ├── posts/
│ │ │ ├── index.tsx # /posts route
│ │ │ └── $postId.tsx # /posts/:postId dynamic route
│ │ └── api/
│ │ └── health.ts # /api/health server route
│ ├── utils/
│ │ ├── posts.functions.ts # Server function wrappers
│ │ ├── posts.server.ts # Server-only DB queries
│ │ └── schemas.ts # Shared Zod schemas
│ ├── router.tsx
│ └── client.tsx
├── app.config.ts # TanStack Start config
└── package.json
```
### Step 2: Server Functions
Server functions are the core primitive — define server-only logic callable from anywhere (loaders, components, event handlers). They cross the network boundary with full type safety.
```typescript
// app/utils/posts.server.ts — Server-only database queries
import { db } from '~/db'
export async function findPosts(limit: number) {
return db.query.posts.findMany({
limit,
orderBy: (posts, { desc }) => [desc(posts.createdAt)],
with: { author: true },
})
}
export async function findPostById(id: string) {
return db.query.posts.findFirst({
where: (posts, { eq }) => eq(posts.id, id),
with: { author: true, comments: { with: { author: true } } },
})
}
export async function createPost(data: { title: string; content: string; authorId: string }) {
return db.insert(posts).values(data).returning()
}
```
```typescript
// app/utils/posts.functions.ts — Server functions with validation
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { findPosts, findPostById, createPost } from './posts.server'
// GET — fetch posts (callable from loaders and components)
export const getPosts = createServerFn({ method: 'GET' })
.inputValidator(z.object({ limit: z.number().min(1).max(100).default(20) }))
.handler(async ({ data }) => {
return findPosts(data.limit)
})
// GET — fetch single post
export const getPost = createServerFn({ method: 'GET' })
.inputValidator(z.object({ id: z.string().uuid() }))
.handler(async ({ data }) => {
const post = await findPostById(data.id)
if (!post) throw notFound()
return post
})
// POST — create post (requires auth via middleware)
export const createNewPost = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(z.object({
title: z.string().min(1).max(200),
content: z.string().min(1).max(50000),
}))
.handler(async ({ data, context }) => {
// context.user comes from authMiddleware
return createPost({ ...data, authorId: context.user.id })
})
```
### Step 3: Routes with Loaders
```tsx
// app/routes/posts/index.tsx — Posts list page with loader
import { createFileRoute } from '@tanstack/react-router'
import { getPosts } from '~/utils/posts.functions'
export const Route = createFileRoute('/posts/')({
// Loader runs on the server during SSR, fetches data before render
loader: () => getPosts({ data: { limit: 20 } }),
component: PostsPage,
})
function PostsPage() {
const posts = Route.useLoaderData()
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<Link to="/posts/$postId" params={{ postId: post.id }}>
{post.title}
</Link>
<span> by {post.author.name}</span>
</li>
))}
</ul>
</div>
)
}
```
```tsx
// app/routes/posts/$postId.tsx — Dynamic post page
import { createFileRoute, notFound } from '@tanstack/react-router'
import { getPost } from '~/utils/posts.functions'
export const Route = createFileRoute('/posts/$postId')({
loader: ({ params }) => getPost({ data: { id: params.postId } }),
// Error boundary for not-found
notFoundComponent: () => <div>Post not found</div>,
component: PostPage,
})
function PostPage() {
const post = Route.useLoaderData()
return (
<article>
<h1>{post.title}</h1>
<p>By {post.author.name} · {new Date(post.createdAt).toLocaleDateString()}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
<h2>Comments ({post.comments.length})</h2>
{post.comments.map(comment => (
<div key={comment.id}>
<strong>{comment.author.name}</strong>
<p>{comment.body}</p>
</div>
))}
</article>
)
}
```
### Step 4: Middleware
Composable middleware for auth, logging, rate limiting — chains execute in order with `next()`.
```typescript
// app/middleware/auth.ts — Authentication middleware
import { createMiddleware } from '@tanstack/react-start'
import { redirect } from '@tanstack/react-router'
import { getRequestHeader } from '@tanstack/react-start/server'
import { verifyToken } from '~/utils/auth.server'
// Request middleware — runs on all server requests that use it
export const authMiddleware = createMiddleware()
.server(async ({ next }) => {
const token = getRequestHeader('Authorization')?.replace('Bearer ', '')
if (!token) {
throw redirect({ to: '/login' })
}
const user = await verifyToken(token)
if (!user) {
throw redirect({ to: '/login' })
}
// Pass user to the next middleware / server function via context
return next({ context: { user } })
})
// Logging middleware — logs request timing
export const loggingMiddleware = createMiddleware()
.server(async ({ next }) => {
const start = Date.now()
const result = await next()
console.log(`Request took ${Date.now() - start}ms`)
return result
})
// Compose middleware — auth depends on logging
export const protectedMiddleware = createMiddleware()
.middleware([loggingMiddleware, authMiddleware])
.server(async ({ next, context }) => {
// context.user is available from authMiddleware
console.log(`Authenticated request from ${context.user.email}`)
return next()
})
```
### Step 5: Server Functions in Components
```tsx
// app/routes/posts/new.tsx — Form with server function mutation
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useServerFn } from '@tanstack/react-start'
import { createNewPost } from '~/utils/posts.functions'
export const Route = createFileRoute('/posts/new')({
component: NewPostForm,
})
function NewPostForm() {
const navigate = useNavigate()
const createPost = useServerFn(createNewPost)
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
const formData = new FormData(e.currentTarget)
const post = await createPost({
data: {
title: formData.get('title') as string,
content: formData.get('content') as string,
},
})
// Navigate to the new post
navigate({ to: '/posts/$postId', params: { postId: post.id } })
}
return (
<form onSubmit={handleSubmit}>
<input name="title" placeholder="Post title" required />
<textarea name="content" placeholder="Write your post..." required rows={10} />
<button type="submit">Publish</button>
</form>
)
}
```
### Step 6: Server Routes (API Endpoints)
```typescript
// app/routes/api/health.ts — API-only route (no React component)
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/apRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.