--- name: vercel-deployment description: Deploy Next.js and React applications to Vercel — project setup, environment variables, edge functions, build troubleshooting, preview deployments, monorepo configuration. NOT for AWS/GCP/Azure deployment, self-hosted solutions, or Cloudflare Pages. allowed-tools: Read,Write,Edit,Bash(npm:*,npx:*,vercel:*) metadata: category: DevOps & Site Reliability tags: - devops - automation - web - react pairs-with: - skill: nextjs-app-router-expert reason: Vercel is optimized for Next.js deployment with native App Router feature support - skill: devops-automator reason: Vercel deployment automation integrates with broader CI/CD pipeline workflows - skill: github-actions-pipeline-builder reason: Vercel preview deployments trigger from GitHub Actions PR workflows --- # Vercel Deployment This skill helps you deploy and configure Next.js applications on Vercel following best practices. ## Quick Deploy Checklist - [ ] Environment variables set in Vercel dashboard - [ ] Build command configured (default: `next build`) - [ ] Output directory correct (default: `.next`) - [ ] Node.js version specified (20.x recommended) - [ ] Database accessible from Vercel's network - [ ] Secrets not committed to git ## Environment Variables ### Setting Variables **Vercel Dashboard** (Recommended for secrets): 1. Project Settings → Environment Variables 2. Add variable with appropriate scope: - **Production**: Only production deployments - **Preview**: PR and branch previews - **Development**: Local `vercel dev` **Via CLI**: ```bash vercel env add VARIABLE_NAME production vercel env pull .env.local # Pull to local ``` ### Variable Naming ```bash # Server-only (never exposed to browser) DATABASE_URL= SESSION_SECRET= ANTHROPIC_API_KEY= # Client-exposed (prefixed with NEXT_PUBLIC_) NEXT_PUBLIC_APP_URL= NEXT_PUBLIC_ANALYTICS_ID= ``` ### Size Limits | Context | Limit | |---------|-------| | Total per deployment | 64 KB | | Edge Functions | 5 KB per variable | | Single variable | 64 KB max | ### Required Variables for This Project ```bash # Authentication (required) SESSION_SECRET=your-32-char-minimum-secret-here # AI Integration (required for chat) ANTHROPIC_API_KEY=sk-ant-api... # Database (if using external) DATABASE_URL=file:./data/app.db # Push Notifications (optional) VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= VAPID_SUBJECT=mailto:admin@example.com # OAuth (optional) GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= APPLE_CLIENT_ID= APPLE_CLIENT_SECRET= ``` ## vercel.json Configuration ```json { "buildCommand": "npm run build", "framework": "nextjs", "regions": ["iad1"], "headers": [ { "source": "/api/(.*)", "headers": [ { "key": "Cache-Control", "value": "no-store, must-revalidate" } ] }, { "source": "/(.*)", "headers": [ { "key": "X-Content-Type-Options", "value": "nosniff" }, { "key": "X-Frame-Options", "value": "DENY" }, { "key": "X-XSS-Protection", "value": "1; mode=block" } ] } ], "redirects": [ { "source": "/old-path", "destination": "/new-path", "permanent": true } ], "rewrites": [ { "source": "/api/v1/:path*", "destination": "/api/:path*" } ] } ``` ## TypeScript Configuration (New in 2025) ```typescript // vercel.ts - Type-safe configuration import { defineConfig } from '@vercel/config'; export default defineConfig({ regions: ['iad1'], headers: async () => [ { source: '/api/:path*', headers: [ { key: 'Cache-Control', value: 'no-store' }, ], }, ], redirects: async () => [ { source: '/old', destination: '/new', permanent: true, }, ], }); ``` ## Edge Functions ### When to Use Edge Good for: - Authentication/authorization - A/B testing - Geolocation-based content - Request/response transforms - Simple, fast operations Not suitable for: - Database connections (use serverless instead) - Long-running operations - Large dependencies ### Edge Function Example ```typescript // src/middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export const config = { matcher: ['/api/:path*', '/protected/:path*'], }; export function middleware(request: NextRequest) { // Check auth token const token = request.cookies.get('session')?.value; if (!token && request.nextUrl.pathname.startsWith('/protected')) { return NextResponse.redirect(new URL('/login', request.url)); } // Add headers const response = NextResponse.next(); response.headers.set('X-Request-Id', crypto.randomUUID()); return response; } ``` ### Edge Runtime in API Routes ```typescript // src/app/api/edge-example/route.ts export const runtime = 'edge'; export async function GET(request: Request) { // Limited to edge-compatible APIs return Response.json({ timestamp: Date.now() }); } ``` ## Build Configuration ### package.json Scripts ```json { "scripts": { "build": "next build", "postbuild": "npm run db:generate" } } ``` ### Build Environment ```bash # Set Node.js version # In Vercel Dashboard → Settings → General → Node.js Version # Or in package.json: { "engines": { "node": "20.x" } } ``` ### Build Output ```bash # Check build locally npm run build # Analyze bundle ANALYZE=true npm run build ``` ## Database Considerations ### SQLite on Vercel SQLite with better-sqlite3 works in Vercel's serverless functions, but: - Filesystem is **read-only** except `/tmp` - Data doesn't persist between invocations - Not suitable for production data storage ### Production Database Options 1. **Turso** (SQLite edge database) ```typescript import { createClient } from '@libsql/client'; const db = createClient({ url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN, }); ``` 2. **Vercel Postgres** ```typescript import { sql } from '@vercel/postgres'; const result = await sql`SELECT * FROM users`; ``` 3. **PlanetScale** (MySQL) 4. **Neon** (Postgres) ## Preview Deployments ### Branch Previews Every git push creates a preview deployment: - `https://--.vercel.app` - Separate environment variables for preview ### Preview Environment Variables ```bash # Different values for preview vs production # In Vercel Dashboard, set both: DATABASE_URL (Production): postgres://prod-db... DATABASE_URL (Preview): postgres://staging-db... ``` ### Commenting on PRs Vercel automatically comments on PRs with: - Preview URL - Build status - Performance metrics ## Troubleshooting ### Build Failures ```bash # Check build locally first npm run build # Common issues: # - Missing environment variables # - TypeScript errors # - ESLint errors (strict mode) # - Missing dependencies ``` ### Environment Variable Issues ```bash # Verify variables are set vercel env ls # Pull to local for debugging vercel env pull .env.local ``` ### Function Timeout ```typescript // Increase timeout (max 60s on Pro, 10s on Hobby) // In vercel.json: { "functions": { "api/long-running.ts": { "maxDuration": 60 } } } ``` ### Memory Issues ```typescript // Increase memory (affects cost) { "functions": { "api/heavy-processing.ts": { "memory": 1024 } } } ``` ## Monitoring ### Vercel Analytics ```typescript // src/app/layout.tsx import { Analytics } from '@vercel/analytics/react'; export default function RootLayout({ children }) { return ( {children} ); } ``` ### Speed Insights ```typescript import { SpeedInsights } from '@vercel/speed-insights/next'; export default function RootLayout({ children }) { return ( {children} ); } ``` ### Function Logs ```bash # View logs via CLI vercel logs # Real-time logs vercel logs --follow ``` ## Domains ### Custom Domain Setup 1. Vercel Dashboard → Domains 2. Add domain 3. Configure DNS: - A record: `76.76.21.21` - Or CNAME: `cname.vercel-dns.com` 4. SSL automatically provisioned ### Redirects ```json // vercel.json { "redirects": [ { "source": "/:path((?!api/).*)", "has": [{ "type": "host", "value": "old-domain.com" }], "destination": "https://new-domain.com/:path", "permanent": true } ] } ``` ## Security ### Protected Routes Use middleware for authentication checks (see Edge Functions above). ### Rate Limiting Implement application-level rate limiting since Vercel doesn't provide built-in rate limiting for serverless functions. ### Secrets Management - Never commit `.env` files - Use Vercel's encrypted environment variables - Rotate secrets regularly - Different secrets for preview vs production ## References - [Vercel Documentation](https://vercel.com/docs) - [Environment Variables](https://vercel.com/docs/environment-variables) - [Edge Functions](https://vercel.com/docs/functions/edge-functions) - [Next.js on Vercel](https://vercel.com/docs/frameworks/nextjs)