Production-ready starter for modern web apps
TanStack Start • Convex • WorkOS • shadcn/ui
Quick Start • Environment Variables • Deployment • What's Included
# Clone and install
git clone https://github.com/Stoffberg/project-zap.git my-app
cd my-app
bun install
# Set up Convex (creates .env.local)
bunx convex dev
# Add WorkOS Client ID to .env.local
echo "VITE_WORKOS_CLIENT_ID=client_01XXXXXX" >> .env.local
# Run
bun devOpen localhost:3000
Prerequisites - Installing Required Tools
# macOS/Linux
curl -fsSL https://bun.sh/install | bash
# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"
# Verify installation
bun --version| Service | Sign Up | Free Tier |
|---|---|---|
| Convex | convex.dev | Yes - generous limits |
| WorkOS | workos.com/sign-up | Yes - 1M MAUs |
These are created/required for local development:
| Variable | Description | How to Get |
|---|---|---|
CONVEX_DEPLOYMENT |
Your Convex deployment ID | Auto-generated by bunx convex dev |
VITE_CONVEX_URL |
Convex backend URL | Auto-generated by bunx convex dev |
VITE_WORKOS_CLIENT_ID |
WorkOS Client ID | WorkOS Dashboard → API Keys |
Example .env.local file
# Auto-generated by Convex
CONVEX_DEPLOYMENT=dev:your-deployment-name
VITE_CONVEX_URL=https://your-deployment.convex.cloud
# Add manually from WorkOS Dashboard
VITE_WORKOS_CLIENT_ID=client_01XXXXXXXXXXXXXXXXXXXXXXThese must be set in the Convex Dashboard under Settings → Environment Variables:
| Variable | Description | Required |
|---|---|---|
WORKOS_CLIENT_ID |
Same as VITE_WORKOS_CLIENT_ID |
Yes - Auth won't work without this |
Why is WORKOS_CLIENT_ID needed in Convex?
Convex needs the WorkOS Client ID to validate JWT tokens. The flow works like this:
- User logs in via WorkOS → receives JWT token
- Frontend sends JWT to Convex with every request
- Convex validates the JWT using your Client ID (from
auth.config.ts) - If valid, user is authenticated in Convex queries/mutations
Without WORKOS_CLIENT_ID in Convex, step 3 fails and all auth checks return false.
| Variable | Description | How to Get |
|---|---|---|
CONVEX_DEPLOY_KEY |
Allows Vercel to deploy to Convex | Convex Dashboard → Settings → Deploy Keys |
VITE_WORKOS_CLIENT_ID |
WorkOS Client ID | WorkOS Dashboard → API Keys |
Step-by-Step Convex Configuration
bunx convex devThis will:
- Prompt you to log in (or create an account)
- Create a new Convex project
- Generate
.env.localwith your deployment URLs - Start the Convex dev server
After running convex dev, verify your schema deployed:
# Should show your tables
bunx convex data- Go to dashboard.convex.dev
- Select your project
- Go to Settings → Environment Variables
- Add
WORKOS_CLIENT_IDwith your WorkOS Client ID value
convex/
├── schema.ts # Database schema with tables and indexes
├── auth.config.ts # WorkOS JWT validation configuration
├── lib/
│ ├── validators.ts # Shared type validators
│ ├── auth.ts # Auth helper functions
│ ├── validation.ts # Input validation
│ ├── constants.ts # Backend constants
│ └── errors.ts # Error types
├── todos.ts # Todo queries and mutations
├── users.ts # User management
├── preferences.ts # User preferences
└── http.ts # HTTP endpoints (webhooks, etc.)
# Start dev server (watches for changes)
bunx convex dev
# Deploy to production
bunx convex deploy
# View data in your tables
bunx convex data
# Run a function manually
bunx convex run todos:list
# View logs
bunx convex logsStep-by-Step WorkOS Configuration
- Go to workos.com/sign-up
- Create your account
- You'll be in the Staging environment by default (good for development)
- In the sidebar, go to Authentication → AuthKit
- Click Set up AuthKit (or Configure if already set up)
- Select Use AuthKit's customizable hosted UI
- Complete the setup wizard
- Still in AuthKit settings
- Under Redirect URIs, add:
http://localhost:3000/callback(development)https://your-domain.com/callback(production - add later)
- Go to Authentication → Sessions
- Find Cross-Origin Resource Sharing (CORS)
- Click Manage
- Add your origins:
http://localhost:3000(development)https://your-domain.com(production - add later)
- Go to API Keys in the sidebar
- Copy your Client ID (format:
client_01XXXXXX) - Add it to your
.env.local:VITE_WORKOS_CLIENT_ID=client_01XXXXXXXXXXXXXXXXXXXXXX
In AuthKit settings, you can enable:
- Email + Password - Basic email authentication
- Magic Link - Passwordless email login
- Google OAuth - Sign in with Google
- GitHub OAuth - Sign in with GitHub
- Microsoft OAuth - Sign in with Microsoft
- Apple OAuth - Sign in with Apple
Each social provider requires:
- Creating an OAuth app in that provider's dashboard
- Adding the Client ID and Secret to WorkOS
In AuthKit → Branding:
- Upload your logo
- Set primary color
- Customize button styles
- Add custom CSS
Step-by-Step Vercel Deployment
# Make sure everything is committed
git add -A
git commit -m "Ready for deployment"
git push origin main- Go to dashboard.convex.dev
- Select your project
- Go to Settings → Deploy Keys
- Click Generate Deploy Key
- Copy the key (you won't see it again)
- Go to vercel.com/new
- Import your GitHub repository
- Configure the project:
| Setting | Value |
|---|---|
| Framework Preset | Other |
| Build Command | bunx convex deploy --cmd 'bun run build' |
| Output Directory | .output |
| Install Command | bun install |
In Vercel project settings → Environment Variables, add:
| Name | Value |
|---|---|
CONVEX_DEPLOY_KEY |
Your deploy key from step 2 |
VITE_WORKOS_CLIENT_ID |
Your WorkOS Client ID |
Click Deploy and wait for the build to complete.
After deployment, update WorkOS settings:
- Add Redirect URI:
https://your-vercel-domain.vercel.app/callback - Add CORS Origin:
https://your-vercel-domain.vercel.app
- Go to dashboard.convex.dev
- Switch to your Production deployment
- Go to Settings → Environment Variables
- Add
WORKOS_CLIENT_IDwith your Client ID
Custom Domain Setup
- Go to your Vercel project → Settings → Domains
- Add your custom domain
- Follow DNS configuration instructions
Add to your WorkOS configuration:
- Redirect URI:
https://yourdomain.com/callback - CORS Origin:
https://yourdomain.com
If you have environment-specific configs, update them for your custom domain.
| Category | Tech |
|---|---|
| Framework | TanStack Start (React 19, SSR, file-based routing) |
| Database | Convex (real-time, serverless, type-safe) |
| Auth | WorkOS AuthKit (SSO, MFA, social login) |
| UI | shadcn/ui + Tailwind v4 |
| Tooling | TypeScript, Biome, Vite |
Features
- Buttons, inputs, forms, selects, checkboxes
- Cards, dialogs, sheets, popovers
- Tables with sorting, filtering, pagination
- Navigation menus, tabs, breadcrumbs
- Toast notifications, alerts, badges
- Dark mode with system preference detection
- Complete sign in/sign up flow
- Social login (Google, GitHub, etc.)
- Session management
- Protected routes with auth guards
- User profile management
- Real-time updates (Convex subscriptions)
- File uploads with Convex storage
- Server-side pagination and search
- Optimistic updates for mutations
- Progressive Web App - installable, works offline
- True device detection (not just screen size)
- Bottom navigation on mobile
- Sheet-based forms and inputs
- Card layouts for tables on mobile
- Touch-optimized (44px targets)
- Safe area support for notched devices
- Type-safe from database to UI
- File-based routing
- Hot module replacement
- ESLint + Biome for linting/formatting
- Pre-commit hooks with lint-staged
src/
├── routes/ # File-based routing
│ ├── index.tsx # Landing page (public)
│ ├── _app.tsx # Auth guard layout
│ └── _app/ # Protected pages
│ ├── dashboard.tsx
│ ├── todos.tsx
│ └── settings/
├── components/
│ ├── ui/ # shadcn/ui primitives
│ ├── features/ # Feature-specific components
│ └── layouts/ # Page layouts
├── hooks/ # Custom React hooks
├── lib/ # Utilities
└── integrations/ # Third-party integrations
├── convex/ # Convex provider setup
└── workos/ # WorkOS provider setup
convex/
├── schema.ts # Database schema
├── auth.config.ts # JWT validation config
├── lib/ # Shared utilities
└── *.ts # Domain modules (todos, users, etc.)
Routing Conventions
| File | Route | Description |
|---|---|---|
index.tsx |
/ |
Landing page |
about.tsx |
/about |
Static page |
_app.tsx |
Layout | Auth guard for nested routes |
_app/dashboard.tsx |
/dashboard |
Protected page |
_app/users/$userId.tsx |
/users/:userId |
Dynamic route |
_app/settings/index.tsx |
/settings |
Nested index |
__root.tsx |
Root layout | Wraps entire app |
Prefixes:
_= Layout (doesn't add to URL)$= Dynamic segment__= Special (root only)
| Command | Description |
|---|---|
bun dev |
Start dev server |
bun build |
Production build |
bun run lint |
Lint with Biome |
bun run check |
Lint + format (auto-fix) |
bun run typecheck |
TypeScript check |
bunx convex dev |
Start Convex dev server |
bunx convex deploy |
Deploy Convex to production |
bunx shadcn@latest add button
bunx shadcn@latest add card dialog sheet
bunx shadcn@latest add form input labelBrowse all components at ui.shadcn.com
Authentication Issues
-
Check Convex environment variable:
- Go to Convex Dashboard → Settings → Environment Variables
- Verify
WORKOS_CLIENT_IDis set and matches your WorkOS Client ID
-
Sync auth config:
bunx convex dev
-
Check CORS in WorkOS:
- Go to WorkOS Dashboard → Authentication → Sessions → CORS
- Verify your domain is listed
-
Check Redirect URI:
- Go to WorkOS Dashboard → Authentication → AuthKit
- Verify
http://localhost:3000/callbackis in Redirect URIs
Your redirect URI doesn't match what's configured in WorkOS. Add the exact URI shown in the error to your WorkOS AuthKit settings.
Add your domain to WorkOS CORS settings:
- WorkOS Dashboard → Authentication → Sessions
- Find CORS section → Manage
- Add your origin (e.g.,
http://localhost:3000)
Convex Issues
Run Convex to generate the environment file:
bunx convex dev- Check
.env.localhas correctCONVEX_DEPLOYMENT - Verify you're logged into the correct Convex account:
bunx convex logout bunx convex dev
Your local schema differs from deployed. Push your schema:
bunx convex devConvex functions might not be synced. Restart the dev server:
# Kill existing process, then:
bunx convex devBuild & Deployment Issues
- Check environment variables are set in Vercel project settings
- Verify build command:
bunx convex deploy --cmd 'bun run build' - Check logs for specific error messages
Generate a new deploy key:
- Convex Dashboard → Settings → Deploy Keys
- Generate new key
- Update in Vercel environment variables
Run typecheck locally first:
bun run typecheckDevelopment Issues
Kill the process using the port:
# macOS/Linux
lsof -ti:3000 | xargs kill -9
# Or use a different port
PORT=3001 bun devClear node_modules and reinstall:
rm -rf node_modules bun.lockb
bun installRegenerate Convex types:
bunx convex devThis generates fresh types in convex/_generated/.
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ User │────▶│ WorkOS │────▶│Frontend │────▶│ Convex │
│ clicks │ │ hosted │ │/callback│ │ backend │
│ sign in │ │ UI │ │ │ │ │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │
│ 1. Login │ │
│◀───────────────│ │
│ │ │
│ 2. JWT token │ │
│───────────────▶│ │
│ │ │
│ │ 3. JWT in │
│ │ requests │
│ │──────────────▶│
│ │ │
│ │ 4. Validate │
│ │ JWT │
│ │◀──────────────│
Important: Always use useConvexAuth() from convex/react to check auth state, not WorkOS's useAuth(). This ensures the Convex backend has validated the token.
MIT