Comprehensive guide for TanStack ecosystem in React - Query/DB for data fetching, Form for form handling, and Router for client-side routing. Use when working with collections, live queries, optimistic updates, forms, validation, routing, URL parameters, or navigation.
Scanned 6/5/2026
Install via CLI
openskills install compozy/compozy---
name: tanstack
description: Comprehensive guide for TanStack ecosystem in React - Query/DB for data fetching, Form for form handling, and Router for client-side routing. Use when working with collections, live queries, optimistic updates, forms, validation, routing, URL parameters, or navigation.
allowed-tools: Read, Grep, Glob
---
# TanStack Developer Guide
This skill provides comprehensive patterns and best practices for the TanStack ecosystem in React applications:
- **TanStack Query/DB**: Data fetching, caching, collections, live queries, and optimistic updates
- **TanStack Form**: Form state management, validation, and field components
- **TanStack Router**: File-based routing, type-safe navigation, and URL parameters
## Quick Start
For detailed examples and patterns, refer to the following files in the `references/` directory:
- `references/query-patterns.md` - TanStack Query and TanStack DB patterns
- `references/form-patterns.md` - TanStack Form patterns and components
- `references/router-patterns.md` - TanStack Router patterns and navigation
---
## TanStack Query/DB Overview
TanStack DB extends TanStack Query with collections, live queries, and optimistic mutations. Key principle: load data into typed collections and consume through live queries that auto-update on data changes.
### Critical Rules
1. **Never Use React Query Patterns with Collections** - Collections have built-in mutation handling. Do NOT use `useMutation` + `invalidateQueries`.
2. **Always Share Collection Instances** - Creating new collection instances for mutations causes "key not found" errors. The data-fetching hook must expose the collection, and mutation hooks must receive it as a parameter.
3. **Configure Persistence Handlers** - Put server writes in collection handlers (`onInsert`, `onUpdate`, `onDelete`), not mutation hooks.
4. **Single Canonical Collection Pattern** - Create ONE collection per entity type. Use live queries for filtered views.
5. **Check Field Changes Properly** - Verify fields actually changed in `onUpdate`, not just that they exist.
### Basic Collection Setup
```typescript
import { createCollection } from '@tanstack/react-db';
import { queryCollectionOptions } from '@tanstack/query-db-collection';
import { z } from 'zod';
const itemSchema = z.object({
id: z.string(),
name: z.string().min(1),
status: z.enum(['active', 'archived']),
});
const itemCollection = createCollection(
queryCollectionOptions({
queryKey: ['items'],
queryFn: async () => (await fetch('/api/items')).json(),
queryClient,
getKey: (item) => item.id,
schema: itemSchema,
})
);
```
### Sharing Collection Instance (Critical Pattern)
```typescript
// CORRECT - share the instance
export function useItems() {
const collection = useMemo(() => createItemsCollection(), []);
const { data } = useLiveQuery(collection);
return { data, collection }; // Expose collection
}
export function useUpdateItem(collection: ItemsCollection) {
return (id, data) => collection.update(id, data);
}
```
---
## TanStack Form Overview
TanStack Form provides headless form logic with automatic type inference and flexible validation.
### Core Principles
- **Type Safety**: Types are inferred from default values - avoid manual generic declarations.
- **Headless Design**: Build UI components to match your design system.
- **Schema-First Validation**: Use Zod for cleaner, more maintainable validation.
### Basic Form Setup with `createFormHook`
```typescript
import { createFormHookContexts, createFormHook } from '@tanstack/react-form'
export const { fieldContext, formContext, useFieldContext } =
createFormHookContexts()
export const { useAppForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: {
TextField,
SelectField,
},
formComponents: {
SubmitButton,
},
})
```
### Form Initialization
```typescript
const form = useAppForm({
defaultValues: {
username: '',
email: '',
age: 0,
},
validators: {
onChange: schema,
},
onSubmit: async ({ value }) => {
// Handle submission
},
})
```
### Async Validation with Debouncing
```typescript
<form.Field
name="username"
asyncDebounceMs={500}
validators={{
onChangeAsync: async ({ value }) => {
const isAvailable = await checkUsernameAvailability(value)
return isAvailable ? undefined : 'Username already taken'
},
}}
/>
```
---
## TanStack Router Overview
TanStack Router provides type-safe file-based routing with first-class TypeScript support.
### Core Principles
- **Type-Safe Routing**: Embrace type-safe routing as the primary benefit.
- **File-Based Routes**: Use file-based routing for scalability.
- **Generated Route Tree**: Leverage the generated route tree for type safety.
### File Structure
```
src/routes/
├── __root.tsx # Root layout with providers
├── _authenticated.tsx # Auth layout wrapper
├── index.tsx # Home page (/)
├── posts/
│ ├── index.tsx # /posts
│ └── $postId.tsx # /posts/:postId (typed params)
└── settings/
├── _layout.tsx # Settings layout
└── profile.tsx # /settings/profile
```
### Basic Route with Search Params
```typescript
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
const searchSchema = z.object({
page: z.number().min(1).catch(1),
search: z.string().optional(),
})
export const Route = createFileRoute('/posts/')({
validateSearch: searchSchema,
component: PostsList,
})
function PostsList() {
const { page, search } = Route.useSearch()
// Use search params...
}
```
### Authentication Layout
```typescript
// routes/_authenticated.tsx
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ location }) => {
const isAuthenticated = checkAuth()
if (!isAuthenticated) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
},
component: () => <Outlet />,
})
```
### Type-Safe Navigation
```typescript
import { Link, useNavigate } from '@tanstack/react-router'
function Navigation() {
const navigate = useNavigate()
return (
<>
<Link
to="/posts/$postId"
params={{ postId: '123' }}
search={{ tab: 'comments' }}
>
View Post
</Link>
<button onClick={() => navigate({ to: '/posts', search: { page: 1 } })}>
Go to Posts
</button>
</>
)
}
```
---
## Validation Checklist
Before finishing a task involving TanStack:
### Query/DB
- [ ] Collection instances are shared between data-fetching and mutation hooks
- [ ] Persistence handlers (`onInsert`, `onUpdate`, `onDelete`) are configured
- [ ] No `useMutation` + `invalidateQueries` patterns with collections
- [ ] One canonical collection per entity type
- [ ] Field changes properly verified in `onUpdate` handlers
### Form
- [ ] Use `createFormHook` with `useAppForm` instead of raw `useForm` for consistency
- [ ] Provide complete default values for proper type inference
- [ ] Use Zod schemas for validation when possible
- [ ] Debounce async validations (minimum 500ms recommended)
- [ ] Prevent default on form submission
- [ ] Display errors with proper accessibility (`role="alert"`)
### Router
- [ ] Route path in `createFileRoute` matches file location
- [ ] Search params use Zod validation with proper defaults (`.catch()`)
- [ ] Loader dependencies are correctly specified in `loaderDeps`
- [ ] Authentication routes use `beforeLoad` with proper redirects
- [ ] Navigation uses typed `Link` or `useNavigate` hooks
- [ ] Error boundaries are implemented at route level
### General
- [ ] Run `pnpm run typecheck` and `pnpm run test`
---
For complete examples, edge cases, and advanced patterns, see the reference files in this skill directory.
No comments yet. Be the first to comment!