Convex + TanStack Start
Overview
This skill provides guidance for building reactive, real-time full-stack applications using Convex (reactive backend-as-a-service) with TanStack Start (full-stack React meta-framework). The stack provides live-updating queries, type-safe end-to-end development, SSR support, and automatic cache invalidation.
When to Use This Skill
- Implementing Convex queries, mutations, or actions
- Setting up or troubleshooting Better Auth authentication
- Configuring TanStack Router routes and loaders
- Writing schema definitions and indexes
- Implementing data fetching patterns (useQuery, useSuspenseQuery)
- Working with file storage, scheduling, or cron jobs
- Building AI agents with @convex-dev/agent
- Debugging SSR or hydration issues
Quick Reference
Essential Imports
// Data fetching (always use cached version)
import { useQuery } from 'convex-helpers/react/cache'
import { useMutation, useAction } from 'convex/react'
// SSR with React Query
import { useSuspenseQuery } from '@tanstack/react-query'
import { convexQuery } from '@convex-dev/react-query'
// API and types
import { api } from '~/convex/_generated/api'
import type { Id, Doc } from '~/convex/_generated/dataModel'
// Backend functions
import { query, mutation, action } from "./_generated/server"
import { v } from "convex/values"
The Skip Pattern
Never call hooks conditionally. Use "skip" instead:
const user = useQuery(api.users.get, userId ? { userId } : "skip")
const org = useQuery(api.orgs.get, user?.orgId ? { orgId: user.orgId } : "skip")
Three-State Query Handling
if (data === undefined) return <Skeleton /> // Loading
if (data === null) return <NotFound /> // Not found
return <Content data={data} /> // Success
Function Syntax (Always Include Returns Validator)
export const getUser = query({
args: { userId: v.id("users") },
returns: v.union(
v.object({ _id: v.id("users"), name: v.string() }),
v.null()
),
handler: async (ctx, args) => {
return await ctx.db.get(args.userId)
},
})
Index Best Practices
// Schema - name includes all fields
.index("by_organizationId_status", ["organizationId", "status"])
// Query - fields in same order as index
.withIndex("by_organizationId_status", (q) =>
q.eq("organizationId", orgId).eq("status", "published")
)
Auth Check (Backend)
import { authComponent } from "./auth"
const user = await authComponent.getAuthUser(ctx)
if (!user) throw new Error("Not authenticated")
Core Principles
- Use queries for reads - Queries are reactive, cacheable, and consistent
- Keep functions fast - Finish in < 100ms, work with < a few hundred records
- Prefer queries/mutations over actions - Actions are for external API calls only
- Always use indexes - Never do table scans with
.filter()
- Minimize client state - Rely on Convex's real-time sync
Common Anti-Patterns
| Wrong |
Correct |
import { useQuery } from 'convex/react' |
import { useQuery } from 'convex-helpers/react/cache' |
if (id) useQuery(...) |
useQuery(..., id ? {...} : "skip") |
.filter(x => x.field === val) |
.withIndex("by_field", q => q.eq("field", val)) |
Action with ctx.db |
Use ctx.runQuery/runMutation |
| `count |
|
Reference Files
Load the appropriate reference file based on the task:
| File |
Use When |
references/01-setup.md |
Project setup, config files, environment variables |
references/02-router.md |
Router setup, root route, file-based routing, layouts |
references/03-auth.md |
Better Auth setup, sign up/in/out, protected routes, SSR auth |
references/04-data-fetching.md |
useQuery, useSuspenseQuery, mutations, loaders, prefetching |
references/05-backend.md |
Schema, queries, mutations, actions, internal functions, HTTP endpoints |
references/06-types.md |
TypeScript patterns, validators, type mapping |
references/07-storage.md |
File upload, download, metadata, deletion |
references/08-scheduling.md |
scheduler.runAfter, cron jobs |
references/09-agents.md |
AI agents, tools, RAG setup |
references/10-frontend.md |
Component patterns, loading states, Tailwind/shadcn |
references/11-permissions.md |
Role hierarchy, feature access patterns |
references/12-deployment.md |
Dev commands, Convex CLI, Vercel deployment |
references/13-quick-reference.md |
Import cheatsheet, common patterns summary |
When to Load References
- Starting a new project: Load
01-setup.md
- Adding authentication: Load
03-auth.md
- Writing backend functions: Load
05-backend.md
- Implementing data fetching: Load
04-data-fetching.md
- Building UI components: Load
10-frontend.md
- Need quick syntax: Load
13-quick-reference.md
1---2name: convex-tanstack3description: Comprehensive guide for building full-stack applications with Convex and TanStack Start. This skill should be used when working on projects that use Convex as the backend database with TanStack Start (React meta-framework). Covers schema design, queries, mutations, actions, authentication with Better Auth, routing, data fetching patterns, SSR, file storage, scheduling, AI agents, and frontend patterns. Use this when implementing features, debugging issues, or needing guidance on Convex + TanStack Start best practices.4---5
6# Convex + TanStack Start
7
8## Overview
9
10This skill provides guidance for building reactive, real-time full-stack applications using Convex (reactive backend-as-a-service) with TanStack Start (full-stack React meta-framework). The stack provides live-updating queries, type-safe end-to-end development, SSR support, and automatic cache invalidation.
11
12## When to Use This Skill
13
14- Implementing Convex queries, mutations, or actions
15- Setting up or troubleshooting Better Auth authentication
16- Configuring TanStack Router routes and loaders
17- Writing schema definitions and indexes
18- Implementing data fetching patterns (useQuery, useSuspenseQuery)
19- Working with file storage, scheduling, or cron jobs
20- Building AI agents with @convex-dev/agent
21- Debugging SSR or hydration issues
22
23## Quick Reference
24
25### Essential Imports
26
27```typescript
28// Data fetching (always use cached version)
29import { useQuery } from 'convex-helpers/react/cache'
30import { useMutation, useAction } from 'convex/react'
31
32// SSR with React Query
33import { useSuspenseQuery } from '@tanstack/react-query'
34import { convexQuery } from '@convex-dev/react-query'
35
36// API and types
37import { api } from '~/convex/_generated/api'
38import type { Id, Doc } from '~/convex/_generated/dataModel'
39
40// Backend functions
41import { query, mutation, action } from "./_generated/server"
42import { v } from "convex/values"
43```
44
45### The Skip Pattern
46
47Never call hooks conditionally. Use `"skip"` instead:
48
49```typescript
50const user = useQuery(api.users.get, userId ? { userId } : "skip")
51const org = useQuery(api.orgs.get, user?.orgId ? { orgId: user.orgId } : "skip")
52```
53
54### Three-State Query Handling
55
56```typescript
57if (data === undefined) return <Skeleton /> // Loading
58if (data === null) return <NotFound /> // Not found
59return <Content data={data} /> // Success
60```
61
62### Function Syntax (Always Include Returns Validator)
63
64```typescript
65export const getUser = query({
66 args: { userId: v.id("users") },
67 returns: v.union(
68 v.object({ _id: v.id("users"), name: v.string() }),
69 v.null()
70 ),
71 handler: async (ctx, args) => {
72 return await ctx.db.get(args.userId)
73 },
74})
75```
76
77### Index Best Practices
78
79```typescript
80// Schema - name includes all fields
81.index("by_organizationId_status", ["organizationId", "status"])
82
83// Query - fields in same order as index
84.withIndex("by_organizationId_status", (q) =>
85 q.eq("organizationId", orgId).eq("status", "published")
86)
87```
88
89### Auth Check (Backend)
90
91```typescript
92import { authComponent } from "./auth"
93
94const user = await authComponent.getAuthUser(ctx)
95if (!user) throw new Error("Not authenticated")
96```
97
98## Core Principles
99
1001. **Use queries for reads** - Queries are reactive, cacheable, and consistent
1012. **Keep functions fast** - Finish in < 100ms, work with < a few hundred records
1023. **Prefer queries/mutations over actions** - Actions are for external API calls only
1034. **Always use indexes** - Never do table scans with `.filter()`
1045. **Minimize client state** - Rely on Convex's real-time sync
105
106## Common Anti-Patterns
107
108| Wrong | Correct |
109|-------|---------|
110| `import { useQuery } from 'convex/react'` | `import { useQuery } from 'convex-helpers/react/cache'` |
111| `if (id) useQuery(...)` | `useQuery(..., id ? {...} : "skip")` |
112| `.filter(x => x.field === val)` | `.withIndex("by_field", q => q.eq("field", val))` |
113| Action with `ctx.db` | Use `ctx.runQuery/runMutation` |
114| `count || 0` | `count ?? 0` (0 is falsy) |
115
116## Reference Files
117
118Load the appropriate reference file based on the task:
119
120| File | Use When |
121|------|----------|
122| `references/01-setup.md` | Project setup, config files, environment variables |
123| `references/02-router.md` | Router setup, root route, file-based routing, layouts |
124| `references/03-auth.md` | Better Auth setup, sign up/in/out, protected routes, SSR auth |
125| `references/04-data-fetching.md` | useQuery, useSuspenseQuery, mutations, loaders, prefetching |
126| `references/05-backend.md` | Schema, queries, mutations, actions, internal functions, HTTP endpoints |
127| `references/06-types.md` | TypeScript patterns, validators, type mapping |
128| `references/07-storage.md` | File upload, download, metadata, deletion |
129| `references/08-scheduling.md` | scheduler.runAfter, cron jobs |
130| `references/09-agents.md` | AI agents, tools, RAG setup |
131| `references/10-frontend.md` | Component patterns, loading states, Tailwind/shadcn |
132| `references/11-permissions.md` | Role hierarchy, feature access patterns |
133| `references/12-deployment.md` | Dev commands, Convex CLI, Vercel deployment |
134| `references/13-quick-reference.md` | Import cheatsheet, common patterns summary |
135
136### When to Load References
137
138- **Starting a new project**: Load `01-setup.md`
139- **Adding authentication**: Load `03-auth.md`
140- **Writing backend functions**: Load `05-backend.md`
141- **Implementing data fetching**: Load `04-data-fetching.md`
142- **Building UI components**: Load `10-frontend.md`
143- **Need quick syntax**: Load `13-quick-reference.md`