agentsclimarketplace

Vercel deploy

Skill medy-gribkov/arcana/skills/vercel-deploy

Deploy to Vercel with edge functions, ISR, middleware, and proper caching. Use when deploying Next.js apps, configuring preview environments, or optimizing Vercel builds.From its SKILL.md

Install
npx -y skills add medy-gribkov/arcana --skill vercel-deploy

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

10.7 KB, ~2.7k tokens by cl100k_base, as published. Nobody here has run it

Vercel Deployment Skill

Core Concept

Deploy applications to Vercel with proper runtime selection, caching strategies, and environment configuration. Edge Functions run at CDN edge nodes for low latency (under 50ms cold start), Serverless Functions run in regional data centers (up to 10s execution).

vercel.json Configuration

BAD: Kitchen sink configuration

{
  "functions": {
    "api/**/*.js": {
      "runtime": "nodejs18.x",
      "maxDuration": 10
    }
  },
  "headers": [],
  "rewrites": []
}

GOOD: Targeted runtime and caching

{
  "functions": {
    "api/slow-process.ts": {
      "runtime": "nodejs20.x",
      "maxDuration": 60,
      "memory": 1024
    }
  },
  "headers": [
    {
      "source": "/api/static-data",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=3600, stale-while-revalidate=86400"
        }
      ]
    }
  ],
  "crons": [
    {
      "path": "/api/cron/cleanup",
      "schedule": "0 0 * * *"
    }
  ]
}

Edge vs Serverless Selection

BAD: Everything as serverless

// app/api/geo/route.ts
export async function GET(request: Request) {
  // Runs in us-east-1 every time, slow for global users
  const country = request.headers.get('x-forwarded-for')
  return Response.json({ country })
}

GOOD: Edge for geo/auth, serverless for heavy compute

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

export async function GET(request: Request) {
  const country = request.headers.get('x-vercel-ip-country')
  const city = request.headers.get('x-vercel-ip-city')
  return Response.json({ country, city })
}

// app/api/process-video/route.ts
export const runtime = 'nodejs'
export const maxDuration = 300 // 5 minutes, requires Pro plan

export async function POST(request: Request) {
  const formData = await request.formData()
  // Heavy video processing requires serverless
  const result = await processVideo(formData)
  return Response.json(result)
}

Middleware for Auth and Geo-Routing

BAD: Auth in every route

// app/api/protected/route.ts
import { verify } from 'jsonwebtoken'

export async function GET(request: Request) {
  const token = request.headers.get('authorization')
  try {
    verify(token, process.env.JWT_SECRET)
  } catch {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }
  // Protected logic
}

GOOD: Middleware at edge for auth and geo

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export const config = {
  matcher: ['/api/protected/:path*', '/dashboard/:path*']
}

export function middleware(request: NextRequest) {
  // Runs at edge, low latency globally
  const token = request.cookies.get('session')?.value
  const country = request.geo?.country

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  // Geo-block sanctioned countries
  if (['CU', 'IR', 'KP', 'SY'].includes(country || '')) {
    return NextResponse.json(
      { error: 'Service unavailable in your region' },
      { status: 451 }
    )
  }

  // Add custom headers for downstream routes
  const response = NextResponse.next()
  response.headers.set('x-user-country', country || 'unknown')
  return response
}

Environment Variables Strategy

BAD: Hardcoded secrets, no environment separation

// lib/config.ts
export const config = {
  apiKey: 'sk_live_abc123', // NEVER hardcode
  dbUrl: process.env.DATABASE_URL // Same DB for preview and production
}

GOOD: Environment-specific variables

// lib/config.ts
export const config = {
  apiKey: process.env.API_KEY!, // Set in Vercel dashboard
  dbUrl: process.env.DATABASE_URL!,
  isProduction: process.env.VERCEL_ENV === 'production',
  isPreview: process.env.VERCEL_ENV === 'preview',
  deploymentUrl: process.env.VERCEL_URL
}

// Vercel dashboard environment settings:
// API_KEY (production): sk_live_abc123
// API_KEY (preview): sk_test_xyz789
// DATABASE_URL (production): postgres://prod.db
// DATABASE_URL (preview): postgres://staging.db

Vercel automatically provides: VERCEL, VERCEL_ENV, VERCEL_URL, VERCEL_GIT_COMMIT_SHA, VERCEL_GIT_COMMIT_REF.

Preview Deployments Workflow

Every git push to non-production branch creates preview deployment:

# Automatic preview on PR
git checkout -b feature/new-ui
git push origin feature/new-ui
# Vercel creates: https://myapp-git-feature-new-ui-user.vercel.app

# Preview-specific env vars for testing
# Set in Vercel dashboard: ENABLE_FEATURE_FLAG=true (preview only)

Workflow for preview comments:

// app/api/comments/route.ts
export async function POST(request: Request) {
  const isPreview = process.env.VERCEL_ENV === 'preview'

  if (isPreview) {
    // Use test Stripe key, mock email service
    return Response.json({
      success: true,
      note: 'Preview mode, no real charges'
    })
  }

  // Production logic with real services
  const result = await stripe.charges.create({...})
  return Response.json(result)
}

Caching Headers and ISR

BAD: No caching, every request hits origin

// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }) {
  const post = await fetchPost(params.slug)
  return <article>{post.content}</article>
}

GOOD: ISR with on-demand revalidation

// app/blog/[slug]/page.tsx
export const revalidate = 3600 // ISR: regenerate after 1 hour

export async function generateStaticParams() {
  const posts = await fetchAllPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

export default async function BlogPost({ params }) {
  const post = await fetchPost(params.slug)
  return <article>{post.content}</article>
}

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache'

export async function POST(request: Request) {
  const { slug, secret } = await request.json()

  if (secret !== process.env.REVALIDATION_SECRET) {
    return Response.json({ error: 'Invalid secret' }, { status: 401 })
  }

  revalidatePath(`/blog/${slug}`)
  return Response.json({ revalidated: true })
}

GOOD: API route caching with stale-while-revalidate

// app/api/stats/route.ts
export async function GET() {
  const stats = await fetchStats()

  return Response.json(stats, {
    headers: {
      'Cache-Control': 's-maxage=60, stale-while-revalidate=300'
      // Cache for 60s, serve stale for 5min while revalidating
    }
  })
}

Image Optimization

BAD: Unoptimized images

<img src="/hero.png" alt="Hero" /> {/* Serves original 5MB PNG */}

GOOD: next/image with formats and sizes

import Image from 'next/image'

// next.config.js
module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200],
    imageSizes: [16, 32, 48, 64, 96, 128, 256],
    minimumCacheTTL: 31536000,
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.example.com',
        pathname: '/images/**'
      }
    ]
  }
}

// Component
<Image
  src="/hero.png"
  alt="Hero"
  width={1200}
  height={600}
  priority
  sizes="(max-width: 768px) 100vw, 50vw"
/>

Monorepo Setup

GOOD: Turborepo with Vercel

// vercel.json (root)
{
  "buildCommand": "turbo run build --filter=web",
  "outputDirectory": "apps/web/.next",
  "installCommand": "pnpm install"
}

// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    }
  }
}

// apps/web/package.json
{
  "name": "web",
  "scripts": {
    "build": "next build"
  }
}

Build Optimization

BAD: No output file tracing

// next.config.js
module.exports = {
  // Default includes all node_modules in function bundles
}

GOOD: Output file tracing and standalone build

// next.config.js
module.exports = {
  output: 'standalone', // Only includes necessary files
  experimental: {
    outputFileTracingRoot: require('path').join(__dirname, '../../')
  },
  webpack: (config, { isServer }) => {
    if (isServer) {
      // Externalize large dependencies
      config.externals.push('canvas', 'bufferutil', 'utf-8-validate')
    }
    return config
  }
}

Custom Domains and SSL

# Add custom domain (Vercel dashboard or CLI)
vercel domains add example.com
vercel domains add www.example.com

# DNS records (automatic with Vercel nameservers):
# A record: @ -> 76.76.21.21
# CNAME: www -> cname.vercel-dns.com

# SSL is automatic, renews via Let's Encrypt

Analytics and Speed Insights

GOOD: Integrate Vercel Analytics

// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
import { SpeedInsights } from '@vercel/speed-insights/next'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
        <SpeedInsights />
      </body>
    </html>
  )
}

// package.json
{
  "dependencies": {
    "@vercel/analytics": "^1.1.1",
    "@vercel/speed-insights": "^1.0.2"
  }
}

Deployment Commands

# Install Vercel CLI
npm i -g vercel

# Initial setup (links project)
vercel

# Deploy to preview
vercel

# Deploy to production
vercel --prod

# Set environment variable
vercel env add API_KEY production
vercel env add API_KEY preview

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

# Check deployment logs
vercel logs https://myapp-abc123.vercel.app

# List deployments
vercel ls

# Promote preview to production
vercel promote https://myapp-git-feature.vercel.app

Common Deployment Checks

Before deploying:

  1. Run next build locally to catch build errors
  2. Check bundle size: npx @next/bundle-analyzer
  3. Verify environment variables are set in Vercel dashboard
  4. Test middleware logic doesn't block legitimate traffic
  5. Ensure ISR paths are properly configured
  6. Validate edge runtime compatibility (no Node.js APIs like fs)

Edge runtime restrictions:

  • No fs, child_process, crypto (use crypto.subtle)
  • Max 1MB per edge function after compression
  • No native modules or binaries

Troubleshooting

Error: Function exceeds maximum size Solution: Use output: 'standalone' and externalize large deps.

Error: Middleware blocking all traffic Solution: Check matcher config, ensure auth logic has fallback.

Slow cold starts on serverless Solution: Switch to edge runtime or reduce dependencies.

Preview using production database Solution: Set environment-specific DATABASE_URL for preview environment.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most containers cloud skills give in ~2.7k tokens

Counted across 607 of the 657 authors here whose files we hold, read 2026-08-07

  • Run containers as a non-root userin 66 of 607, across 46 files
  • Use multi-stage buildsin 53 of 607, across 44 files
  • Use Promise.all for independent operationsin 47 of 607, across 13 files
  • Import directly instead of barrel filesin 46 of 607, across 12 files
  • Use ternary instead of AND for conditionalsin 45 of 607, across 12 files
  • Use Set or Map for O(1) lookupsin 42 of 607, across 10 files
  • Create a .dockerignore filein 41 of 607, across 31 files
  • Read individual rule files for detailsin 39 of 607, across 9 files
  • Copy dependency files before source codein 36 of 607, across 23 files
  • Authenticate server actions like API routesin 35 of 607, across 7 files
  • Use next/dynamic for heavy componentsin 34 of 607, across 9 files
  • Use React.cache for per-request deduplicationin 34 of 607, across 10 files

Said here and by no other author read

  • Use targeted runtimes and caching in configuration
  • Use edge runtime for geo and auth
  • Use serverless runtime for heavy compute
  • Handle auth and geo-routing in middleware
  • Enable ISR for dynamic pages

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.