React + Vite + Convex Template
This skill provides comprehensive guidelines for working with a full-stack TypeScript application using React, Vite, TanStack Router, Convex backend, and Clerk authentication.
Project Overview
**Stack**: React + Vite + TanStack Router (frontend), Convex (backend), Clerk (auth)**Development**: Use `mcp__shell-commands__launch-dev-all` to start servers, monitor output streams for validation**Import alias**: `@/` maps to `src/` directory**Styling**: Tailwind CSS 4, daisyUI 5 (config in `src/index.css` via CSS syntax, NOT tailwind.config.js)**Typography**: `@tailwindcss/typography` with `prose prose-invert` at root, use `not-prose` to escape**Environment**: Client vars need `VITE_` prefix, Convex vars set in dashboard**Package manager**: Always use `pnpm` and `pnpx`, NOT `npm` or `npx`Git Workflow
1. **Commit after each user request**: When completing what the user asked for, immediately commit without asking:
```bash
git add -A && git commit -m "[action]: [what was accomplished]"
```
2. **Commits are restore points**: If user says "let's go back to before X" or "undo that", use:
```bash
git log # or git reflog to see all commits including "lost" ones
git reset --hard [commit-hash]
```
3. **ALWAYS update claude-notes.md** and include it in EVERY commit. Maintain:
- Current feature being worked on
- List of commits made during the session/feature
- Progress status and next steps
- Important context or decisions made
- Relevant file locations or dependencies
4. **Squash when feature complete**:
```bash
pnpm run lint
git reset --soft [starting-commit]
# CLEAR claude-notes.md
git commit -m "feat: [complete feature description]"
```
5. **Before major feature work**: Tell user "Starting [feature], will make frequent commits as checkpoints then squash when complete"
Testing & Validation
Check BOTH vite and convex stdout AND stderr output streams for TypeScript/compilation errorsTest UI with Playwright MCP: full browser automation with element interaction and console accessResponsive testing: `mcp__playwright__browser_resize` to test mobile (375x667), tablet (768x1024), desktop (1200x800)Clerk verification: sign in with `[email protected]` and 424242 as verification code (type all 6 digits at once in first field)Debug with `mcp__playwright__browser_console_messages` to view browser console outputIf stuck, look for relevant documentation or reference implementationsConvex Backend
Core Concepts
`_creationTime` and `_id` automatically added to all documentsAdding required fields breaks existing data - ask user to clear database if early in developmentUse `ConvexError` for client-friendly errors, not generic ErrorQueries have 16MB/10s limits - always use indexes, never full table scansDefault query order is ascending by `_creationTime`Transactions are per-mutation - can't span multiple mutationsUse `import { Doc, Id } from "./_generated/dataModel";` and `v.id("table")` for type safetyAlways add `"use node";` to the top of files containing actions that use Node.js built-in modulesFunction Guidelines
Import `query`, `internalQuery`, `mutation`, `internalMutation`, `action`, `internalAction` from `./_generated/server`Use `ctx.runQuery`, `ctx.runMutation`, `ctx.runAction` to call functions from other functionsActions can't directly access DB - use `ctx.runQuery` / `ctx.runMutation`Always use an args validator for functionsQuery Guidelines
Do NOT use `filter` in queries. Instead, define an index in the schema and use `withIndex`Convex queries do NOT support `.delete()`. Instead, `.collect()` results, iterate, and call `ctx.db.delete(row._id)` on eachUse `.unique()` to get a single document from a query (throws error if multiple matches)When using async iteration, don't use `.collect()` or `.take(n)` - use `for await (const row of query)` syntaxValidator Guidelines
`v.bigint()` is deprecated for signed 64-bit integers - use `v.int64()` insteadUse `v.record()` for defining a record type. `v.map()` and `v.set()` are not supportedFile Uploads
1. Generate upload URL in mutation: `ctx.storage.generateUploadUrl()`
2. POST from client
3. Store ID: `v.id("_storage")`
4. Serve with `ctx.storage.getUrl(fileId)` in queries
Convex + Clerk Integration
Always use Convex's auth hooks (`useConvexAuth`) and components (`<Authenticated>`, `<Unauthenticated>`, `<AuthLoading>`) instead of Clerk's hooks/components. This ensures auth tokens are properly validated by the Convex backend.
TanStack Router
Avoid `const search = useSearch()` - use `select` option insteadSearch params as filters: validate with zod schema in route definitionNavigate programmatically: `const navigate = useNavigate()` then `navigate({ to: '/path' })`Type-safe links: always use `<Link to="/path">` not `<a href>`Nested routes require parent to have `<Outlet />`, use `.index.tsx` files to show content at parent pathsTanStack Query + Convex Integration
Use `convexQuery()` from `@convex-dev/react-query` to create query options: ```typescript
const queryOptions = convexQuery(api.module.function, { status: "active" })
```
Preload in route loaders: ```typescript
loader: async ({ context: { queryClient } }) => await queryClient.ensureQueryData(queryOptions)
```
Use `useSuspenseQuery` in components: ```typescript
const { data } = useSuspenseQuery(queryOptions)
```
For mutations, continue using Convex's `useMutation` directlyStyling with DaisyUI
Class Organization
`component`: Main class (btn)`part`: Child elements (card-title)`style`: Visual variants (btn-outline)`behavior`: State (btn-active)`color`: Colors (btn-primary)`size`: Sizes (btn-lg)`placement`: Position (dropdown-top)`direction`: Orientation (menu-horizontal)`modifier`: Special (btn-wide)Key v5 Changes from v4
`btm-nav` → `dock``online/offline/placeholder` (avatars) → `avatar-online/avatar-offline/avatar-placeholder``card-bordered` → `card-border``.active/.disabled` (menus) → `menu-active/menu-disabled``tabs-bordered/tabs-boxed/tabs-lifted` → `tabs-border/tabs-box/tabs-lift``btn-group/input-group` → `join` + `join-item` on each child`input-bordered/select-bordered/textarea-border` → removed (base classes include border; use `--ghost` variants for no border)Usage Rules
Responsive patterns: `lg:menu-horizontal`, `sm:card-horizontal`Prefer daisyUI colors (`bg-primary`) over Tailwind colors (`bg-blue-500`) for theme consistencyUse `*-content` colors for text on colored backgroundsTypography plugin adds default margins to headings - use `mt-0` to override when precise spacing is neededColor System
Semantic: `primary`, `secondary`, `accent`, `neutral`, `base-100/200/300`Status: `info`, `success`, `warning`, `error`Each color has matching `-content` variant for contrasting textCustom themes use OKLCH formatOther Guidelines
When stuck: check official docs first (docs.convex.dev, tanstack.com, daisyui.com)Ask before installing new dependenciesVerify responsive design at multiple breakpointsDocument non-obvious implementation choicesImport icons from `lucide-react`When making identical changes to multiple occurrences, use Edit with `replace_all: true` instead of MultiEditNever leave floating promises, use void when neededExample Usage
When initializing a new project from this template, use the `project:init-app` custom command. After initialization, follow the git workflow for all development work, making frequent checkpoint commits and squashing when features are complete.