Claude Agent Skill · by Sickn33

Vercel Deployment

The vercel-deployment skill provides developers with expert guidance on deploying Next.js applications to Vercel, covering environment variable configuration ac

Install
Terminal · npx
$npx skills add https://github.com/sickn33/antigravity-awesome-skills --skill vercel-deployment
Works with Paperclip

How Vercel Deployment fits into a Paperclip company.

Vercel Deployment drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md682 lines
Expand
---name: vercel-deploymentdescription: Expert knowledge for deploying to Vercel with Next.jsrisk: safesource: vibeship-spawner-skills (Apache 2.0)date_added: 2026-02-27--- # Vercel Deployment Expert knowledge for deploying to Vercel with Next.js ## Capabilities - vercel- deployment- edge-functions- serverless- environment-variables ## Prerequisites - Required skills: nextjs-app-router ## Patterns ### Environment Variables Setup Properly configure environment variables for all environments **When to use**: Setting up a new project on Vercel // Three environments in Vercel:// - Development (local)// - Preview (PR deployments)// - Production (main branch) // In Vercel Dashboard:// Settings → Environment Variables // PUBLIC variables (exposed to browser)NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.coNEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... // PRIVATE variables (server only)SUPABASE_SERVICE_ROLE_KEY=eyJ...  // Never NEXT_PUBLIC_!DATABASE_URL=postgresql://... // Per-environment values:// Production: Real database, production API keys// Preview: Staging database, test API keys// Development: Local/dev values (also in .env.local) // In code, check environment:const isProduction = process.env.VERCEL_ENV === 'production'const isPreview = process.env.VERCEL_ENV === 'preview' ### Edge vs Serverless Functions Choose the right runtime for your API routes **When to use**: Creating API routes or middleware // EDGE RUNTIME - Fast cold starts, limited APIs// Good for: Auth checks, redirects, simple transforms // app/api/hello/route.tsexport const runtime = 'edge' export async function GET() {  return Response.json({ message: 'Hello from Edge!' })} // middleware.ts (always edge)export function middleware(request: NextRequest) {  // Fast auth checks here} // SERVERLESS (Node.js) - Full Node APIs, slower cold start// Good for: Database queries, file operations, heavy computation // app/api/users/route.tsexport const runtime = 'nodejs'  // Default, can omit export async function GET() {  const users = await db.query('SELECT * FROM users')  return Response.json(users)} ### Build Optimization Optimize build for faster deployments and smaller bundles **When to use**: Preparing for production deployment // next.config.js/** @type {import('next').NextConfig} */const nextConfig = {  // Minimize output  output: 'standalone',  // For Docker/self-hosting   // Image optimization  images: {    remotePatterns: [      { hostname: 'your-cdn.com' },    ],  },   // Bundle analyzer (dev only)  // npm install @next/bundle-analyzer  ...(process.env.ANALYZE === 'true' && {    webpack: (config) => {      const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')      config.plugins.push(new BundleAnalyzerPlugin())      return config    },  }),} // Reduce serverless function size:// - Use dynamic imports for heavy libs// - Check bundle with: npx @next/bundle-analyzer ### Preview Deployment Workflow Use preview deployments for PR reviews **When to use**: Setting up team development workflow // Every PR gets a unique preview URL automatically // Protect preview deployments with password:// Vercel Dashboard → Settings → Deployment Protection // Use different env vars for preview:// - PREVIEW: Use staging database// - PRODUCTION: Use production database // In code, detect preview:if (process.env.VERCEL_ENV === 'preview') {  // Show "Preview" banner  // Use test payment processor  // Disable analytics} // Comment preview URL on PR (automatic with Vercel GitHub integration) ### Custom Domain Setup Configure custom domains with proper SSL **When to use**: Going to production // In Vercel Dashboard → Domains // Add domains:// - example.com (apex/root)// - www.example.com (subdomain) // DNS Configuration (at your registrar):// Type: A, Name: @, Value: 76.76.21.21// Type: CNAME, Name: www, Value: cname.vercel-dns.com // Redirect www to apex (or vice versa):// Vercel handles this automatically // In next.config.js for redirects:module.exports = {  async redirects() {    return [      {        source: '/old-page',        destination: '/new-page',        permanent: true,  // 308      },    ]  },} ## Sharp Edges ### NEXT_PUBLIC_ exposes secrets to the browser Severity: CRITICAL Situation: Using NEXT_PUBLIC_ prefix for sensitive API keys Symptoms:- Secrets visible in browser DevTools → Sources- Security audit finds exposed keys- Unexpected API access from unknown sources Why this breaks:Variables prefixed with NEXT_PUBLIC_ are inlined into the JavaScriptbundle at build time. Anyone can view them in browser DevTools.This includes all your users and potential attackers. Recommended fix: Only use NEXT_PUBLIC_ for truly public values: // SAFE to use NEXT_PUBLIC_NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.coNEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...  // Anon key is designed to be publicNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...NEXT_PUBLIC_GA_ID=G-XXXXXXX // NEVER use NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY=eyJ...     // Full database access!STRIPE_SECRET_KEY=sk_live_...         // Can charge cards!DATABASE_URL=postgresql://...          // Direct DB access!JWT_SECRET=...                         // Can forge tokens! // Access server-only vars in:// - Server Components (app router)// - API Routes// - Server Actions ('use server')// - getServerSideProps (pages router) ### Preview deployments using production database Severity: HIGH Situation: Not configuring separate environment variables for preview Symptoms:- Test data appearing in production- Production data corrupted after PR merge- Users seeing test accounts/content Why this breaks:Preview deployments run untested code. If they use production database,a bug in a PR can corrupt production data. Also, testers might createtest data that shows up in production. Recommended fix: Set up separate databases for each environment: // In Vercel Dashboard → Settings → Environment Variables // Production (production env only):DATABASE_URL=postgresql://prod-host/prod-db // Preview (preview env only):DATABASE_URL=postgresql://staging-host/staging-db // Or use Vercel's branching databases:// - Neon, PlanetScale, Supabase all support branch databases// - Auto-create preview DB for each PR // For Supabase, create a staging project:// Production:NEXT_PUBLIC_SUPABASE_URL=https://prod-xxx.supabase.co // Preview:NEXT_PUBLIC_SUPABASE_URL=https://staging-xxx.supabase.co ### Serverless function too large, slow cold starts Severity: HIGH Situation: API route or server component has slow initial load Symptoms:- First request takes 3-10+ seconds- Subsequent requests are fast- Function size limit exceeded error- Deployment fails with size error Why this breaks:Vercel serverless functions have a 50MB limit (compressed).Large functions mean slow cold starts (1-5+ seconds).Heavy dependencies like puppeteer, sharp can cause this. Recommended fix: Reduce function size: // 1. Use dynamic imports for heavy libsexport async function GET() {  const sharp = await import('sharp')  // Only loads when needed  // ...} // 2. Move heavy processing to edge or external serviceexport const runtime = 'edge'  // Much smaller, faster cold start // 3. Check bundle size// npx @next/bundle-analyzer// Look for large dependencies // 4. Use external services for heavy tasks// - Image processing: Cloudinary, imgix// - PDF generation: API service// - Puppeteer: Browserless.io // 5. Split into multiple functions// /api/heavy-task/start - Queue the job// /api/heavy-task/status - Check progress ### Edge runtime missing Node.js APIs Severity: HIGH Situation: Using Node.js APIs in edge runtime functions Symptoms:- X is not defined at runtime- Cannot find module fs- Works locally, fails deployed- Middleware crashes Why this breaks:Edge runtime runs on V8, not Node.js. Many Node APIs are missing:fs, path, crypto (partial), child_process, and most native modules.Your code will fail at runtime with "X is not defined". Recommended fix: Check API compatibility before using edge: // SUPPORTED in Edge:// - fetch, Request, Response// - crypto.subtle (Web Crypto)// - TextEncoder, TextDecoder// - URL, URLSearchParams// - Headers, FormData// - setTimeout, setInterval // NOT SUPPORTED in Edge:// - fs, path, os// - Buffer (use Uint8Array)// - crypto.createHash (use crypto.subtle)// - Most npm packages with native deps // If you need Node.js APIs:export const runtime = 'nodejs'  // Use Node runtime instead // For crypto hashing in edge:// WRONGimport { createHash } from 'crypto'  // Fails in edge // RIGHTasync function hash(message: string) {  const encoder = new TextEncoder()  const data = encoder.encode(message)  const hashBuffer = await crypto.subtle.digest('SHA-256', data)  return Array.from(new Uint8Array(hashBuffer))    .map(b => b.toString(16).padStart(2, '0'))    .join('')} ### Function timeout causes incomplete operations Severity: MEDIUM Situation: Long-running operations timing out Symptoms:- Task timed out after X seconds- Incomplete database operations- Partial file uploads- Function killed mid-execution Why this breaks:Vercel has timeout limits:- Hobby: 10 seconds- Pro: 60 seconds (can increase to 300)- Enterprise: 900 seconds Operations exceeding this are killed mid-execution. Recommended fix: Handle long operations properly: // 1. Return early, process asyncexport async function POST(request: Request) {  const data = await request.json()   // Queue for background processing  await queue.add('process-data', data)   // Return immediately  return Response.json({ status: 'queued' })} // 2. Use streaming for long responsesexport async function GET() {  const stream = new ReadableStream({    async start(controller) {      for (const chunk of generateChunks()) {        controller.enqueue(chunk)        await sleep(100)  // Prevents timeout      }      controller.close()    }  })  return new Response(stream)} // 3. Use external services for heavy processing// - Trigger serverless function, return job ID// - Process in background (Inngest, Trigger.dev)// - Client polls for completion // 4. Increase timeout (Pro plan)// vercel.json:{  "functions": {    "app/api/slow/route.ts": {      "maxDuration": 60    }  }} ### Environment variable missing at runtime but present at build Severity: MEDIUM Situation: Environment variable works in build but undefined at runtime Symptoms:- Env var is undefined in production- Value doesn't change after updating in dashboard- Works in dev, wrong value in production- Requires redeploy to update value Why this breaks:Some env vars are only available at build time (hardcoded into bundle).If you expect a runtime value but it was baked in at build, you getthe build-time value or undefined. Recommended fix: Understand when env vars are read: // BUILD TIME (baked into bundle):// - NEXT_PUBLIC_* variables// - next.config.js// - generateStaticParams// - Static pages // RUNTIME (read on each request):// - Server Components (without cache)// - API Routes// - Server Actions// - Middleware // To force runtime reading:export const dynamic = 'force-dynamic' // For config that must be runtime:// Don't use NEXT_PUBLIC_, read on server and pass to client // Check which env vars you need:// Build: URLs, public keys, feature flags (if static)// Runtime: Secrets, database URLs, user-specific config ### CORS errors calling API routes from different domain Severity: MEDIUM Situation: Frontend on different domain can't call API routes Symptoms:- CORS policy error in browser console- No Access-Control-Allow-Origin header- Requests work in Postman but not browser- Works same-origin, fails cross-origin Why this breaks:By default, browsers block cross-origin requests. Vercel doesn'tautomatically add CORS headers. If your frontend is on a differentdomain (or localhost in dev), requests fail. Recommended fix: Add CORS headers to API routes: // app/api/data/route.tsexport async function GET(request: Request) {  const data = await fetchData()   return Response.json(data, {    headers: {      'Access-Control-Allow-Origin': '*',  // Or specific domain      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',      'Access-Control-Allow-Headers': 'Content-Type, Authorization',    },  })} // Handle preflight requestsexport async function OPTIONS() {  return new Response(null, {    headers: {      'Access-Control-Allow-Origin': '*',      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',      'Access-Control-Allow-Headers': 'Content-Type, Authorization',    },  })} // Or use next.config.js for all routes:module.exports = {  async headers() {    return [      {        source: '/api/:path*',        headers: [          { key: 'Access-Control-Allow-Origin', value: '*' },        ],      },    ]  },} ### Page shows stale data after deployment Severity: MEDIUM Situation: Updated data not appearing after new deployment Symptoms:- Old content shows after deploy- Changes not visible immediately- Different users see different versions- Data updates but page doesn't Why this breaks:Vercel caches aggressively. Static pages are cached at the edge.Even dynamic pages may be cached if not configured properly.Old cached versions served until cache expires or is purged. Recommended fix: Control caching behavior: // Force no caching (always fresh)export const dynamic = 'force-dynamic'export const revalidate = 0 // ISR - revalidate every 60 secondsexport const revalidate = 60 // On-demand revalidation (after mutation)import { revalidatePath, revalidateTag } from 'next/cache' // In Server Action:async function updatePost(id: string) {  await db.post.update({ ... })  revalidatePath(`/posts/${id}`)  // Purge this page  revalidateTag('posts')          // Purge all with this tag} // Purge via API (deployment hook):// POST https://your-site.vercel.app/api/revalidate?path=/posts // Check caching in response headers:// x-vercel-cache: HIT = served from cache// x-vercel-cache: MISS = freshly generated ## Validation Checks ### Secret in NEXT_PUBLIC Variable Severity: CRITICAL Message: Secret exposed via NEXT_PUBLIC_ prefix. This will be visible in browser. Fix action: Remove NEXT_PUBLIC_ prefix and access only in server-side code ### Hardcoded Vercel URL Severity: WARNING Message: Hardcoded Vercel URL. Use VERCEL_URL environment variable instead. Fix action: Use process.env.VERCEL_URL or NEXT_PUBLIC_VERCEL_URL ### Node.js API in Edge Runtime Severity: ERROR Message: Node.js module used in Edge runtime. fs/path not available in Edge. Fix action: Use runtime = 'nodejs' or remove Node.js dependencies ### API Route Without CORS Headers Severity: WARNING Message: API route without CORS headers may fail cross-origin requests. Fix action: Add Access-Control-Allow-Origin header if API is called from other domains ### API Route Without Error Handling Severity: WARNING Message: API route without try/catch. Unhandled errors return 500 without details. Fix action: Wrap in try/catch and return appropriate error responses ### Secret Read in Static Context Severity: WARNING Message: Server secret accessed in static generation. Value baked into build. Fix action: Move secret access to runtime code or use NEXT_PUBLIC_ for public values ### Large Package Import Severity: WARNING Message: Large package imported. May cause slow cold starts. Consider alternatives. Fix action: Use lodash-es with tree shaking, date-fns instead of moment, @aws-sdk/client-* instead of aws-sdk ### Dynamic Page Without Revalidation Config Severity: WARNING Message: Dynamic page without revalidation config. Consider setting revalidation strategy. Fix action: Add export const revalidate = 60 for ISR, or 0 for no cache ## Collaboration ### Delegation Triggers - next.js|app router|pages|server components -> nextjs-app-router (Deployment needs Next.js patterns)- database|supabase|backend -> supabase-backend (Deployment needs database)- auth|authentication|session -> nextjs-supabase-auth (Deployment needs auth config)- monitoring|logs|errors|analytics -> analytics-architecture (Deployment needs monitoring) ### Production Launch Skills: vercel-deployment, nextjs-app-router, supabase-backend, nextjs-supabase-auth Workflow: ```1. App configuration (nextjs-app-router)2. Database setup (supabase-backend)3. Auth config (nextjs-supabase-auth)4. Deploy (vercel-deployment)``` ### CI/CD Pipeline Skills: vercel-deployment, devops, qa-engineering Workflow: ```1. Test automation (qa-engineering)2. Pipeline config (devops)3. Deploy strategy (vercel-deployment)``` ## Related Skills Works well with: `nextjs-app-router`, `supabase-backend` ## When to Use- User mentions or implies: vercel- User mentions or implies: deploy- User mentions or implies: deployment- User mentions or implies: hosting- User mentions or implies: production- User mentions or implies: environment variables- User mentions or implies: edge function- User mentions or implies: serverless function ## Limitations- Use this skill only when the task clearly matches the scope described above.- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.