home / skills / sstobo / convex-skills / convex-tanstack

convex-tanstack skill

/convex-tanstack

This skill guides you through Convex and TanStack Start integration, enabling reactive full-stack apps with best practices for queries, auth, routing, and SSR.

npx playbooks add skill sstobo/convex-skills --skill convex-tanstack

Review the files below or copy the command above to add this skill to your agents.

Files (14)
SKILL.md
5.4 KB
---
name: convex-tanstack
description: 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.
---

# 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

```typescript
// 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:

```typescript
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

```typescript
if (data === undefined) return <Skeleton />  // Loading
if (data === null) return <NotFound />       // Not found
return <Content data={data} />               // Success
```

### Function Syntax (Always Include Returns Validator)

```typescript
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

```typescript
// 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)

```typescript
import { authComponent } from "./auth"

const user = await authComponent.getAuthUser(ctx)
if (!user) throw new Error("Not authenticated")
```

## Core Principles

1. **Use queries for reads** - Queries are reactive, cacheable, and consistent
2. **Keep functions fast** - Finish in < 100ms, work with < a few hundred records
3. **Prefer queries/mutations over actions** - Actions are for external API calls only
4. **Always use indexes** - Never do table scans with `.filter()`
5. **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 || 0` | `count ?? 0` (0 is falsy) |

## 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`

Overview

This skill is a practical guide for building full-stack apps with Convex as the backend and TanStack Start as the React meta-framework. It focuses on schema design, reactive queries, mutations/actions, SSR, authentication with Better Auth, file storage, scheduling, and AI agents. Use it to implement features, debug issues, and apply best practices for a type-safe, real-time stack.

How this skill works

The skill explains how to wire Convex server functions and indexes to TanStack Start routes and loaders, showing the correct imports and patterns for cached queries, suspense queries, and mutations. It describes schema validators, index usage, authentication checks, and when to use actions versus queries/mutations. It also covers deployment considerations, file storage workflows, scheduling, and connecting Convex agents for AI features.

When to use it

  • Implementing Convex queries, mutations, or actions
  • Setting up or troubleshooting Better Auth authentication
  • Configuring TanStack Router routes, loaders, and SSR
  • Designing schema and indexes for efficient queries
  • Implementing file uploads, scheduling jobs, or AI agents
  • Debugging hydration, SSR, or realtime update issues

Best practices

  • Always use the cached helpers import for hooks (avoid direct convex/react imports)
  • Never call hooks conditionally—use the "skip" pattern for optional params
  • Write validators and include returns in server functions to keep end-to-end typesafe
  • Create indexes that match query field order and avoid table scans (.filter)
  • Keep backend functions fast (<100ms) and avoid working over hundreds of records
  • Favor queries/mutations for DB work; use actions for external API calls only

Example use cases

  • Build a protected route with SSR that prefetches user data via a TanStack loader and Convex query
  • Create a paginated list using an index and withIndex to ensure efficient reads
  • Implement Better Auth sign-in flow and backend auth checks before mutations
  • Schedule a recurring report via scheduler.runAfter or cron job that writes to Convex storage
  • Wire an AI agent using @convex-dev/agent and a RAG pipeline stored in Convex documents

FAQ

When should I use an action instead of a mutation?

Use actions for side effects that contact external APIs or perform long-running tasks. Prefer mutations for DB writes and queries for reads.

How do I avoid SSR hydration issues with Convex?

Use useSuspenseQuery for server loaders and match the three-state pattern (undefined = loading, null = not found, value = success). Keep hooks usage consistent and avoid conditional hooks.