Best for
- Client-only SPA with API calls - Router + Query
- Full-stack with SSR/server functions - Start + Query (Start includes Router)
tenequm/skills/skills/tanstack/SKILL.md
Builds type-safe React apps with TanStack Query (data fetching, caching, mutations), Router (file-based routing, search params, loaders), and Start (SSR, server functions, middleware). Use when working with react-query, server state, file-based routing, typed search params, route loaders, SSR, or server functions in a full-stack React app.
Decision brief
Type-safe libraries for React applications. Query manages server state (fetching, caching, mutations). Router provides file-based routing with validated search params and data loaders. Start extends Router with SSR, server functions, and middleware for full-stack apps.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/tenequm/skills --skill "skills/tanstack"Inspect the Agent Skill "tanstack" from https://github.com/tenequm/skills/blob/9b9fb5a29c103ed207dc255d753939e4e2ed29f5/skills/tanstack/SKILL.md at commit 9b9fb5a29c103ed207dc255d753939e4e2ed29f5. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Setup” section in the pinned source before continuing.
Review the “Setup (Vite)” section in the pinned source before continuing.
Query - data fetching, caching, mutations, optimistic updates, infinite scroll, streaming AI/SSE responses, tRPC v11 integration Router - file-based routing, type-safe navigation, validated search params, route loaders, code splitting, preloading Start - SSR/SSG, server function…
Query keys - hierarchical arrays for cache management:
Review the “Queries” section in the pinned source before continuing.
Permission review
The documentation includes network, browsing, or remote request actions.
const res = await fetch('/api/todos')The documentation includes network, browsing, or remote request actions.
fetch('/api/todos', { method: 'POST', body: JSON.stringify(newTodo) }).then(r => r.json()),The documentation asks the agent to run terminal commands or scripts.
pnpm add @tanstack/react-query-devtoolsThe documentation asks the agent to run terminal commands or scripts.
pnpm add @tanstack/react-router @tanstack/router-pluginEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 35 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Type-safe libraries for React applications. Query manages server state (fetching, caching, mutations). Router provides file-based routing with validated search params and data loaders. Start extends Router with SSR, server functions, and middleware for full-stack apps.
Query - data fetching, caching, mutations, optimistic updates, infinite scroll, streaming AI/SSE responses, tRPC v11 integration Router - file-based routing, type-safe navigation, validated search params, route loaders, code splitting, preloading Start - SSR/SSG, server functions (type-safe RPCs), middleware, API routes, deployment to Cloudflare/Vercel/Node
Decision tree:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
},
},
})
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
)
}
import { useQuery, queryOptions } from '@tanstack/react-query'
// Reusable query definition (recommended pattern)
const todosQueryOptions = queryOptions({
queryKey: ['todos'],
queryFn: async () => {
const res = await fetch('/api/todos')
if (!res.ok) throw new Error('Failed to fetch')
return res.json() as Promise<Todo[]>
},
})
// In component - full type inference from queryOptions
function TodoList() {
const { data, isLoading, error } = useQuery(todosQueryOptions)
if (isLoading) return <Spinner />
if (error) return <div>Error: {error.message}</div>
return <ul>{data.map(t => <li key={t.id}>{t.title}</li>)}</ul>
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
function CreateTodo() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: (newTodo: { title: string }) =>
fetch('/api/todos', { method: 'POST', body: JSON.stringify(newTodo) }).then(r => r.json()),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
return (
<button onClick={() => mutation.mutate({ title: 'New' })}>
{mutation.isPending ? 'Creating...' : 'Create'}
</button>
)
}
Query keys - hierarchical arrays for cache management:
['todos'] // all todos
['todos', 'list', { page, sort }] // filtered list
['todo', todoId] // single item
Dependent queries - chain with enabled:
const { data: user } = useQuery({ queryKey: ['user', id], queryFn: () => fetchUser(id) })
const { data: projects } = useQuery({
queryKey: ['projects', user?.id],
queryFn: () => fetchProjects(user!.id),
enabled: !!user?.id,
})
Important defaults: staleTime: 0, gcTime: 5min, retry: 3, refetchOnWindowFocus: true
Suspense - use useSuspenseQuery with <Suspense> boundaries
Streamed queries (experimental) - for AI chat/SSE:
import { experimental_streamedQuery as streamedQuery } from '@tanstack/react-query'
const { data: chunks } = useQuery(queryOptions({
queryKey: ['chat', sessionId],
queryFn: streamedQuery({ streamFn: () => fetchChatStream(sessionId), refetchMode: 'reset' }),
}))
pnpm add @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
// Add inside QueryClientProvider
<ReactQueryDevtools initialIsOpen={false} />
query-guide.md - Complete Query reference with all patternsinfinite-queries.md - useInfiniteQuery, pagination, virtual scrolloptimistic-updates.md - Optimistic UI, rollback, undoquery-performance.md - staleTime tuning, deduplication, prefetchingquery-invalidation.md - Cache invalidation strategies, filters, predicatesquery-typescript.md - Type inference, generics, custom hookspnpm add @tanstack/react-router @tanstack/router-plugin
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({ autoCodeSplitting: true }),
react(),
],
})
// src/router.ts
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({ routeTree, defaultPreload: 'intent' })
declare module '@tanstack/react-router' {
interface Register { router: typeof router }
}
Files in src/routes/ auto-generate route config:
| Convention | Purpose | Example |
|---|---|---|
__root.tsx | Root route (always rendered) | src/routes/__root.tsx |
index.tsx | Index route | src/routes/index.tsx -> / |
$param | Dynamic segment | posts.$postId.tsx -> /posts/:id |
_prefix | Pathless layout | _layout.tsx wraps children |
(folder) | Route group (no URL) | (auth)/login.tsx -> /login |
<Link to="/posts/$postId" params={{ postId: '123' }}>View Post</Link>
// Active styling
<Link to="/posts" activeProps={{ className: 'font-bold' }}>Posts</Link>
// Imperative
const navigate = useNavigate({ from: '/posts' })
navigate({ to: '/posts/$postId', params: { postId: post.id } })
Always provide from on Link and hooks - narrows types and improves TS performance.
import { zodValidator, fallback } from '@tanstack/zod-adapter'
import { z } from 'zod'
const searchSchema = z.object({
page: fallback(z.number(), 1).default(1),
sort: fallback(z.enum(['newest', 'oldest']), 'newest').default('newest'),
})
export const Route = createFileRoute('/products')({
validateSearch: zodValidator(searchSchema),
component: () => {
const { page, sort } = Route.useSearch()
// Writing
return <Link from={Route.fullPath} search={prev => ({ ...prev, page: prev.page + 1 })}>Next</Link>
},
})
Use fallback(...).default(...) from the Zod adapter (Zod v3); plain .catch() causes type loss. With Zod v4 the adapter is no longer needed - pass the schema directly to validateSearch, and .catch() retains type inference.
export const Route = createFileRoute('/posts')({
// loaderDeps: only extract what loader needs (not full search)
loaderDeps: ({ search: { page } }) => ({ page }),
loader: ({ deps: { page } }) => fetchPosts({ page }),
pendingComponent: () => <Spinner />,
component: () => {
const posts = Route.useLoaderData()
return <PostList posts={posts} />
},
})
// __root.tsx
interface RouterContext { queryClient: QueryClient }
export const Route = createRootRouteWithContext<RouterContext>()({ component: Root })
// router.ts
const router = createRouter({ routeTree, context: { queryClient } })
// Child route - queryClient available in loader
export const Route = createFileRoute('/posts')({
loader: ({ context: { queryClient } }) =>
queryClient.ensureQueryData(postsQueryOptions()),
})
router-guide.md - Complete Router reference with all patternssearch-params.md - Custom serialization, Standard Schema, sharing paramsdata-loading.md - Deferred loading, streaming SSR, shouldReloadrouting-patterns.md - Virtual routes, route masking, navigation blockingcode-splitting.md - Automatic/manual splitting strategiesrouter-ssr.md - SSR setup, streaming, hydrationFull-stack framework extending Router with SSR, server functions, middleware. Pre-1.0 (API stable, feature-complete, preparing for 1.0). React Server Components are available as an experimental feature - opt in with tanstackStart({ rsc: { enabled: true } }) + @vitejs/plugin-rsc (requires React 19, Vite 7+). Vite is the default bundler; Rsbuild is also supported.
npx @tanstack/cli@latest create # or use TanStack Builder: https://tanstack.com/builder
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
tanstackStart(),
viteReact(), // MUST come after tanstackStart()
],
})
Type-safe RPCs. Server code extracted from client bundles at build time.
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
// GET - no input
export const getUsers = createServerFn({ method: 'GET' })
.handler(async () => db.users.findMany())
// POST - validated input
export const createUser = createServerFn({ method: 'POST' })
.validator(z.object({ name: z.string(), email: z.string().email() }))
.handler(async ({ data }) => db.users.create(data))
// Call from loader
export const Route = createFileRoute('/users')({
loader: () => getUsers(),
component: () => {
const users = Route.useLoaderData()
return <UserList users={users} />
},
})
Critical: Loaders are isomorphic (run on server AND client). Never put secrets in loaders - use createServerFn() instead.
import { createMiddleware } from '@tanstack/react-start'
const authMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next }) => {
const user = await getCurrentUser()
if (!user) throw redirect({ to: '/login' })
return next({ context: { user } })
})
const getProfile = createServerFn()
.middleware([authMiddleware])
.handler(async ({ context }) => context.user) // typed
Global middleware via src/start.ts:
export const startInstance = createStart(() => ({
requestMiddleware: [logger], // all requests
functionMiddleware: [auth], // all server functions
}))
CSRF: Start auto-installs createCsrfMiddleware() for server functions only when there is no src/start.ts. Once you create src/start.ts, add it back explicitly, or non-GET server functions lose same-origin protection:
requestMiddleware: [createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === 'serverFn' }), logger]
| Mode | Use Case |
|---|---|
true (default) | SEO, performance |
false | Browser-only features |
'data-only' | Dashboards (data on server, render on client) |
SPA mode: tanstackStart({ spa: { enabled: true } }) in vite.config.ts
@cloudflare/vite-plugin), Netlify (@netlify/vite-plugin-tanstack-start), RailwaytanstackStart({ prerender: { enabled: true, crawlLinks: true } })start-guide.md - Complete Start reference with all patternsserver-functions.md - Streaming, FormData, progressive enhancementmiddleware.md - sendContext, custom fetch, global configssr-modes.md - Selective SSR, shellComponent, fallback renderingserver-routes.md - Dynamic params, wildcards, pathless layoutsqueryOptions() factory for reusable, type-safe query definitions['entity', 'action', { filters }]Infinity, dynamic: 0, moderate: 5minzodValidator + fallback().default()from on navigation - narrows types, catches route mismatchescreateRootRouteWithContextdefaultPreload: 'intent' globally for perceived performancebeforeLoad/route guards are UX, not the security boundary. Never put secrets in isomorphic loaders - use createServerFn()head() on every content route for SEO (title, description, OG tags)Frequently asked questions
Type-safe libraries for React applications. Query manages server state (fetching, caching, mutations). Router provides file-based routing with validated search params and data loaders. Start extends Router with SSR, server functions, and middleware for full-stack apps.
The source record exposes this install command: npx skills add https://github.com/tenequm/skills --skill "skills/tanstack". Inspect the command and pinned source before running it.
Static rules flagged network, exec-script in the source; the page lists the matching lines and excerpts.
Alternatives
UiPath/skills
UiPath Coded Apps — scaffold, build, run, and deploy Coded Web Apps and Coded Action Apps: React/TypeScript apps that call UiPath Cloud APIs via the `@uipath/uipath-typescript` SDK and ship to Automation Cloud (push/pull to Studio Web, pack, publish, deploy, OAuth-PKCE). Also generates live analytics & governance dashboards from a plain-language request, wired to tenant data via the Insights real-time API, with edit and deploy flows. For RPA→uipath-rpa, Python agents→uipath-agents, Maestro flows
awslabs/agent-plugins
Build and deploy full-stack web and mobile apps with AWS Amplify Gen2 (TypeScript code-first). Covers auth (Cognito), data (AppSync/DynamoDB including schema modeling, enum types, relationships, authorization rules), storage (S3), functions, APIs, and AI (Amplify AI Kit with Bedrock). Supports React, Next.js, Vue, Angular, React Native, Flutter, Swift, and Android. Always use this skill for Amplify Gen2 topics — even for questions you think you know — it contains validated, version-specific patt
github/awesome-copilot
Microsoft Store Developer CLI (msstore) for publishing Windows applications to the Microsoft Store. Use when asked to configure Store credentials, list Store apps, check submission status, publish submissions, manage package flights, set up CI/CD for Store publishing, or integrate with Partner Center. Supports Windows App SDK/WinUI, UWP, .NET MAUI, Flutter, Electron, React Native, and PWA applications.
tenequm/skills
Build Chrome extensions using WXT framework with TypeScript, React, Vue, or Svelte. Use when creating browser extensions, developing cross-browser add-ons, or working with Chrome Web Store projects. Triggers on phrases like "chrome extension", "browser extension", "WXT framework", "manifest v3", or file patterns like wxt.config.ts.