Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

Provides comprehensive Vercel deployment standards optimized for Next.js applications, covering environment configuration, edge functions, serverless architecture, database integration, cron jobs, and production best practices

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name deploy-vercel
description Provides comprehensive Vercel deployment standards optimized for Next.js applications, covering environment configuration, edge functions, serverless architecture, database integration, cron jobs, and production best practices

Vercel Deployment Standards

This skill provides complete guidelines for deploying applications to Vercel, with specific focus on Next.js optimizations and serverless architecture patterns.

Pre-Deployment Checklist

Repository Requirements

  • Code pushed to GitHub/GitLab/Bitbucket
  • Next.js project properly configured
  • package.json with build scripts
  • .gitignore includes .env, .vercel, node_modules
  • Dependencies correctly categorized
  • Database migrations strategy defined
  • API routes follow serverless best practices

Vercel-Specific Requirements

  • vercel.json configured (optional but recommended)
  • Environment variables documented
  • Build output optimized
  • Edge runtime considered for performance-critical routes
  • ISR/SSG strategy defined

Initial Setup

Connect Repository

Via Dashboard:

  1. Go to vercel.com
  2. Click "Add New" → "Project"
  3. Import Git Repository
  4. Select repository
  5. Configure project settings
  6. Deploy

Via CLI:

# Install Vercel CLI
npm i -g vercel

# Login
vercel login

# Deploy
vercel

# Production deploy
vercel --prod

Project Configuration

vercel.json:

{
  "buildCommand": "npm run build",
  "outputDirectory": ".next",
  "framework": "nextjs",
  "rewrites": [
    { "source": "/api/:path*", "destination": "/api/:path*" }
  ],
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-XSS-Protection",
          "value": "1; mode=block"
        }
      ]
    }
  ],
  "regions": ["iad1", "sfo1"],
  "crons": [
    {
      "path": "/api/cron/daily-cleanup",
      "schedule": "0 0 * * *"
    }
  ]
}

Environment Variables

Configuration

Via Dashboard:

  1. Project Settings → Environment Variables
  2. Add variables for each environment:
    • Production
    • Preview
    • Development

Via CLI:

# Add environment variable
vercel env add DATABASE_URL production

# Pull environment variables locally
vercel env pull .env.local

Required Variables for Next.js

# Core Next.js
NODE_ENV=production  # Auto-set by Vercel
NEXT_PUBLIC_VERCEL_URL=${VERCEL_URL}  # Auto-provided

# Application URLs
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NEXTAUTH_URL=https://yourdomain.com
NEXTAUTH_SECRET=your-secret-min-32-chars

# Database (Vercel Postgres / Supabase / PlanetScale)
DATABASE_URL=postgresql://...
POSTGRES_URL=postgresql://...
POSTGRES_PRISMA_URL=postgresql://...  # For Prisma
POSTGRES_URL_NON_POOLING=postgresql://...  # For migrations

# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...

# External Services
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
SENDGRID_API_KEY=SG...
OPENAI_API_KEY=sk-...

# Storage (Vercel Blob)
BLOB_READ_WRITE_TOKEN=vercel_blob_...

# KV (Vercel KV - Redis)
KV_REST_API_URL=https://...
KV_REST_API_TOKEN=...
KV_REST_API_READ_ONLY_TOKEN=...

# Edge Config (Feature flags)
EDGE_CONFIG=https://edge-config.vercel.com/...

Environment Variable Types

Public (Client-Side):

  • Must start with NEXT_PUBLIC_
  • Exposed to browser
  • Embedded at build time

Private (Server-Side Only):

  • No prefix needed
  • Available in API routes and Server Components
  • Never exposed to client

System Variables (Auto-provided by Vercel):

  • VERCEL_URL - Deployment URL
  • VERCEL_ENV - production | preview | development
  • VERCEL_GIT_COMMIT_SHA - Git commit hash
  • VERCEL_GIT_COMMIT_REF - Git branch name

Build Configuration

Next.js Optimization

next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Production optimizations
  reactStrictMode: true,
  swcMinify: true,
  
  // Image optimization
  images: {
    domains: ['yourdomain.com', 'images.unsplash.com'],
    formats: ['image/avif', 'image/webp'],
  },
  
  // Headers
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'X-DNS-Prefetch-Control',
            value: 'on'
          },
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=63072000; includeSubDomains; preload'
          }
        ]
      }
    ]
  },
  
  // Redirects
  async redirects() {
    return [
      {
        source: '/old-page',
        destination: '/new-page',
        permanent: true,
      }
    ]
  },
  
  // Experimental features
  experimental: {
    serverActions: {
      bodySizeLimit: '2mb',
    },
  },
}

module.exports = nextConfig

Build Performance

Optimize Build Times:

// package.json
{
  "scripts": {
    "build": "next build",
    "postbuild": "next-sitemap"
  }
}

Turbopack (Faster Builds):

# Use Turbopack for builds
next build --turbo

Serverless Functions

API Routes Configuration

Function Config:

// app/api/users/route.ts
import { NextResponse } from 'next/server'

export const runtime = 'edge'  // or 'nodejs' (default)
export const maxDuration = 60  // seconds (Pro plan: 300s)
export const dynamic = 'force-dynamic'  // Disable caching

export async function GET(request: Request) {
  // Your logic here
  return NextResponse.json({ users: [] })
}

Runtime Options:

Runtime Use Case Execution Time Cold Start
edge Low-latency, simple logic 30s (Hobby), 900s (Pro) ~5ms
nodejs Complex logic, libraries 10s (Hobby), 300s (Pro) ~100ms

Edge Functions

When to Use Edge:

  • Authentication checks
  • Geolocation-based routing
  • A/B testing
  • Bot detection
  • Simple data fetching

Example:

// app/api/geo/route.ts
export const runtime = 'edge'

export async function GET(request: Request) {
  const geo = request.headers.get('x-vercel-ip-country') || 'unknown'
  
  return new Response(JSON.stringify({ country: geo }), {
    headers: { 'content-type': 'application/json' }
  })
}

Database Integration

Vercel Postgres

Setup:

  1. Dashboard → Storage → Create Database → Postgres
  2. Automatically provisions connection strings
  3. Environment variables auto-added

Environment Variables (Auto-provided):

POSTGRES_URL="postgresql://..."
POSTGRES_PRISMA_URL="postgresql://..."  # With pgBouncer
POSTGRES_URL_NON_POOLING="postgresql://..."  # Direct connection

Connection:

// lib/db.ts
import { Pool } from '@vercel/postgres'

export const db = new Pool({
  connectionString: process.env.POSTGRES_URL,
})

// Usage
const { rows } = await db.query('SELECT * FROM users')

Prisma Integration

Prisma Setup:

// lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

export const prisma = globalForPrisma.prisma ?? new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

Database Migrations:

// package.json
{
  "scripts": {
    "postinstall": "prisma generate",
    "db:migrate": "prisma migrate deploy",
    "vercel-build": "prisma generate && prisma migrate deploy && next build"
  }
}

Vercel Build Settings:

# Build Command
npm run vercel-build

# This ensures migrations run before build

Supabase Integration

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

Cron Jobs

Configuration

vercel.json:

{
  "crons": [
    {
      "path": "/api/cron/daily-cleanup",
      "schedule": "0 0 * * *"
    },
    {
      "path": "/api/cron/send-reminders",
      "schedule": "0 9 * * *"
    },
    {
      "path": "/api/cron/weekly-report",
      "schedule": "0 10 * * 1"
    }
  ]
}

Cron API Route:

// app/api/cron/daily-cleanup/route.ts
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  // Verify cron secret
  const authHeader = request.headers.get('authorization')
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  try {
    // Run cleanup logic
    await cleanupOldData()
    
    return NextResponse.json({ success: true })
  } catch (error) {
    return NextResponse.json({ error: error.message }, { status: 500 })
  }
}

async function cleanupOldData() {
  // Your cleanup logic
  console.log('Running daily cleanup...')
}

Cron Schedule Format:

* * * * *
│ │ │ │ │
│ │ │ │ └─ Day of week (0-7)
│ │ │ └─── Month (1-12)
│ │ └───── Day of month (1-31)
│ └─────── Hour (0-23)
└───────── Minute (0-59)

Examples:

  • Every hour: 0 * * * *
  • Daily at 2 AM: 0 2 * * *
  • Every Monday: 0 0 * * 1
  • First of month: 0 0 1 * *

Static & Dynamic Rendering

Rendering Strategies

Static Generation (SSG):

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <Article post={post} />
}

Incremental Static Regeneration (ISR):

// app/products/[id]/page.tsx
export const revalidate = 3600  // Revalidate every hour

export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await getProduct(params.id)
  return <Product data={product} />
}

Server-Side Rendering (SSR):

// app/dashboard/page.tsx
export const dynamic = 'force-dynamic'  // Always SSR

export default async function Dashboard() {
  const session = await getSession()
  const data = await getUserData(session.userId)
  return <DashboardView data={data} />
}

Vercel KV (Redis)

Setup

  1. Dashboard → Storage → Create Database → KV
  2. Select region
  3. Environment variables auto-added

Usage:

// lib/kv.ts
import { kv } from '@vercel/kv'

// Set value
await kv.set('user:123', { name: 'John' })

// Get value
const user = await kv.get('user:123')

// Set with expiration
await kv.setex('session:abc', 3600, { userId: '123' })

// Delete
await kv.del('user:123')

// Increment
await kv.incr('page:views')

Rate Limiting Example:

// app/api/protected/route.ts
import { kv } from '@vercel/kv'
import { NextResponse } from 'next/server'

export async function POST(request: Request) {
  const ip = request.headers.get('x-forwarded-for') || 'unknown'
  const key = `ratelimit:${ip}`
  
  const requests = await kv.incr(key)
  
  if (requests === 1) {
    await kv.expire(key, 60)  // 60 second window
  }
  
  if (requests > 10) {
    return NextResponse.json(
      { error: 'Too many requests' },
      { status: 429 }
    )
  }
  
  // Process request
  return NextResponse.json({ success: true })
}

Vercel Blob (File Storage)

Setup & Usage

// Upload file
import { put } from '@vercel/blob'

export async function POST(request: Request) {
  const form = await request.formData()
  const file = form.get('file') as File
  
  const blob = await put(file.name, file, {
    access: 'public',
    token: process.env.BLOB_READ_WRITE_TOKEN,
  })
  
  return Response.json({ url: blob.url })
}

// List files
import { list } from '@vercel/blob'

const { blobs } = await list()

// Delete file
import { del } from '@vercel/blob'

await del(url)

Monitoring & Logs

Real-Time Logs

Via Dashboard:

  • Project → Logs
  • Filter by function, status, time range
  • Search logs

Via CLI:

# Stream logs
vercel logs my-app --follow

# Logs from specific deployment
vercel logs my-app --deployment dpl_xxx

# Filter by function
vercel logs my-app --function /api/users

Analytics

Web Analytics (Built-in):

  • Dashboard → Analytics
  • Page views, unique visitors
  • Top pages, referrers
  • Real-time data

Speed Insights:

  • Core Web Vitals
  • Performance metrics
  • Real user monitoring

Enable in next.config.js:

module.exports = {
  experimental: {
    webVitalsAttribution: ['CLS', 'LCP', 'FCP', 'FID', 'TTFB']
  }
}

Error Tracking

Integrate Sentry:

// sentry.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  environment: process.env.VERCEL_ENV,
  tracesSampleRate: 1.0,
})

Custom Domains

Setup

Add Domain:

  1. Project Settings → Domains
  2. Enter domain name
  3. Choose domain type (apex or subdomain)

Configure DNS:

For subdomain (www, app, etc.):

Type: CNAME
Name: www
Value: cname.vercel-dns.com
TTL: 3600

For apex domain (yourdomain.com):

Type: A
Name: @
Value: 76.76.21.21

Type: AAAA (IPv6)
Name: @
Value: 2606:4700:4700::1111

SSL Certificate:

  • Automatically provisioned
  • Free via Let's Encrypt
  • Auto-renewal

Performance Optimization

Image Optimization

// Use Next.js Image component
import Image from 'next/image'

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority  // For above-the-fold images
  placeholder="blur"  // Better UX
/>

next.config.js:

module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  }
}

Edge Config (Feature Flags)

// lib/feature-flags.ts
import { get } from '@vercel/edge-config'

export async function isFeatureEnabled(flag: string): Promise<boolean> {
  try {
    return await get(flag) || false
  } catch {
    return false
  }
}

// Usage
const newUIEnabled = await isFeatureEnabled('new-ui')

Bundle Analysis

# Analyze bundle size
npm install -D @next/bundle-analyzer

# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
  // Your config
})

# Run analysis
ANALYZE=true npm run build

Deployment Strategies

Preview Deployments

Automatic:

  • Every push to non-production branch creates preview
  • Unique URL: my-app-git-branch-username.vercel.app
  • Test changes before merging

Comments on PRs:

  • Vercel bot comments with preview URL
  • QA team can review
  • Automatic updates on new commits

Production Deployments

Automatic:

  • Push to main branch triggers production deploy
  • Alias to custom domain

Manual:

# Deploy to production
vercel --prod

# Rollback to previous deployment
vercel rollback

Environment-Specific Builds

// lib/config.ts
const config = {
  apiUrl: process.env.VERCEL_ENV === 'production'
    ? 'https://api.yourdomain.com'
    : process.env.VERCEL_ENV === 'preview'
    ? 'https://api-staging.yourdomain.com'
    : 'http://localhost:3001',
}

Deployment Checklist

Pre-Deployment

  • All environment variables set
  • Database migrations tested
  • Build passes locally (npm run build)
  • No console errors or warnings
  • Images optimized
  • API routes tested
  • Authentication working
  • Third-party integrations configured

Post-Deployment

  • Custom domain resolving correctly
  • SSL certificate active
  • Health check endpoint responding
  • Database connection working
  • Cron jobs running (check logs)
  • Analytics tracking
  • Error tracking configured
  • Performance metrics monitored
  • SEO meta tags correct
  • Social share previews working

Troubleshooting

Build Errors

# Common issues:
- Missing dependencies → Check package.json
- TypeScript errors → Run tsc --noEmit
- Environment variable missing → Set in dashboard
- Out of memory → Upgrade plan or optimize build

Runtime Errors

# Check:
- Function logs in dashboard
- Environment variables in deployment
- Database connection
- External API availability

Performance Issues

# Investigate:
- Bundle size (use bundle analyzer)
- Slow database queries
- Unoptimized images
- Blocking server-side operations
- Cold starts (consider edge runtime)

Best Practices

Security

  • Use environment secrets
  • Implement CSRF protection
  • Rate limit API routes
  • Validate all inputs
  • Sanitize user content
  • Use secure headers
  • Keep dependencies updated

Performance

  • Use ISR for dynamic content
  • Implement edge caching
  • Optimize images with next/image
  • Minimize JavaScript bundle
  • Use compression
  • Lazy load components
  • Prefetch critical routes

Cost Optimization

  • Use Edge functions where possible (cheaper)
  • Implement proper caching strategies
  • Optimize serverless function duration
  • Use ISR instead of SSR when possible
  • Monitor bandwidth usage
  • Clean up unused deployments

Resources


Pro Tip: Use preview deployments aggressively for QA. Every PR should have a working preview URL before merging to production.