last update
Jun 24, 2026
license
MITv1.0.0
format
SKILL.md
- Install
// Downloads SKILL.md into ~/.claude/skills/tanstack-start-fullstack-react/
$ curl -fsSL --create-dirs -o ~/.claude/skills/tanstack-start-fullstack-react/SKILL.md https://claudeskil.com/skills/tanstack-start-fullstack-react/SKILL.md - Invoke
// Inside Claude Code, type the skill name as a slash command
> /tanstack-start-fullstack-react give my landing page a hero treatment - Or copy this from the README
// extracted tsx block
$ // src/routes/posts/$postId.tsx import { createFileRoute } from '@tanstack/react-router' import { createServerFn } from '@tanstack/start' import { z } from 'zod' const getPost = createServerFn({ method: 'GET' })
- name:
- TanStack Start Builder
- slug:
- tanstack-start-fullstack-react
- version:
- v1.0.0
- license:
- MIT
- author:
- @chiragthummar16
- categories:
- tags:
- #tanstack-start#react#ssr#server-functions#type-safe-routing
- description:
Ship production TanStack Start v1 apps — type-safe file routing, server functions, loaders, streaming SSR, deploy.
README.md
TanStack Start Builder
This skill scaffolds and extends production TanStack Start v1 applications — the full-stack React framework built on TanStack Router, Vinxi/Vite, and Nitro. It generates correctly-typed file routes, server functions, loaders, and streaming SSR boundaries, and it knows the wiring (router instance, StartClient/StartServer, the route tree) that trips people up on a fresh setup. It's for React developers who want end-to-end type safety from URL params to server responses without hand-rolling an API layer.
When to use it
- Spinning up a new TanStack Start app and you want the router, root route, and entry points configured the first time.
- Adding a route with typed path params and validated search params instead of stringly-typed
useSearchParams. - Moving data fetching server-side: replacing ad-hoc fetch calls with
createServerFnplus loaders that hydrate on the client. - Introducing streaming SSR so slow data renders behind
<Suspense>without blocking first paint.
How it works
Routes live under src/routes/ and are defined with createFileRoute('/path'). Path params come from the file name ($postId.tsx); search params are parsed and typed via the validateSearch option (pair it with a Zod/Valibot schema). Loaders run through loader and beforeLoad — beforeLoad is where you do auth checks or seed the loader context, and its data is available to children.
Server-only logic uses createServerFn({ method: 'GET' | 'POST' })().handler(...). These are isomorphic call sites: you import and call them from a loader or a component, but the body only ever runs on the server. Attach .middleware([...]) for auth/logging and read request context inside the handler. For streaming, return a promise (or a defer-style deferred value) from a loader, then render it with <Await> inside <Suspense> — the shell streams immediately and the chunk flushes when the data resolves.
Example
curl -fsSL --create-dirs -o ~/.claude/skills/tanstack-start-fullstack-react/SKILL.md https://claudeskil.com/skills/tanstack-start-fullstack-react/SKILL.md
// src/routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/start'
import { z } from 'zod'
const getPost = createServerFn({ method: 'GET' })
.validator((id: string) => id)
.handler(async ({ data: id }) => db.post.find(id))
export const Route = createFileRoute('/posts/$postId')({
validateSearch: z.object({ tab: z.enum(['comments', 'edits']).default('comments') }),
loader: ({ params }) => getPost({ data: params.postId }),
component: Post,
})
function Post() {
const post = Route.useLoaderData()
const { tab } = Route.useSearch()
return <article>{post.title} — {tab}</article>
}
Tips & gotchas
- Server functions must validate their input with
.validator(...)to stay type-safe across the network boundary — unvalidateddataisunknown. - The route tree (
routeTree.gen.ts) is generated; run the dev server so the plugin regenerates it, and don't edit it by hand. useSearch/useLoaderDatafrom theRouteobject are pre-narrowed to that route — prefer them over the generic hooks to keep inference.beforeLoadredirects (viathrow redirect(...)) run before the loader, so use it for auth gates rather than the component.
FAQ
Do I still need a separate API server? No. createServerFn and route loaders cover most data needs; add Nitro API routes (src/routes/api/) only for third-party webhooks or non-React clients.
How is this different from Next.js App Router? Routing and data are fully type-inferred end to end, and server functions are plain typed function calls rather than convention-based server components.
Where does it deploy? Anywhere Nitro targets — Node, Vercel, Netlify, Cloudflare Workers — by setting the preset in the Vite/app config.
$ cat reviews/