---
id: vercel-deploy-wire
name: vercel-deploy-wire
description: Configure a Next.js project for Vercel deploy — vercel.json, env-var registration, preview/production setup, and CI auto-deploy via vercel.com integration.
version: 1.0.0
source: bundled
tags: [deploy, vercel, nextjs, saas]
---

# Skill: vercel-deploy-wire

Use this skill when the user wants to **prepare a project for Vercel
deployment**. Sets up `vercel.json`, env-var mappings, and explains the
two paths to production (CLI vs. git-integrated).

## Pre-flight checks

1. Confirm `vercel` CLI is installed (`vercel --version`)
2. Verify Next.js project (look for `next.config.mjs` or `next.config.js`)
3. Check if project is already linked (`.vercel/project.json` present)
4. Confirm the user has a Vercel account + a connected GitHub remote (if going
   the git-integrated route)
5. Inventory `.env.local` — collect every key that needs to be replicated in Vercel

## Files to create / modify

### `vercel.json`

```json
{
  "framework": "nextjs",
  "buildCommand": "next build",
  "installCommand": "npm install",
  "regions": ["iad1"],
  "env": {
    "NEXT_PUBLIC_SUPABASE_URL": "@next_public_supabase_url",
    "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY": "@next_public_clerk_publishable_key",
    "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY": "@next_public_stripe_publishable_key",
    "NEXT_PUBLIC_APP_URL": "@next_public_app_url"
  }
}
```

`@<name>` syntax references **Vercel Secrets** (separate from env vars
for legacy reasons). Modern alternative: use Vercel Dashboard → Project
→ Settings → Environment Variables.

### `.env.production.example`

Document EVERY production env var the user must register in Vercel:

```bash
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
CLERK_WEBHOOK_SECRET=

# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_PRO=
STRIPE_PRICE_TEAM=

# App
NEXT_PUBLIC_APP_URL=https://your-domain.com
```

### `.gitignore` additions

Ensure these patterns are present:

```
.vercel
.env*.local
.env.production
```

### `package.json` script additions

```json
{
  "scripts": {
    "deploy:preview": "vercel",
    "deploy:prod": "vercel --prod"
  }
}
```

## Two paths to production

### Path A: CLI-driven (manual deploys)

```bash
vercel link                  # one-time: bind directory to a Vercel project
vercel env pull               # download env vars from Vercel into .env.local
vercel                        # preview deploy → ephemeral URL
vercel --prod                 # production deploy
```

Or via OpenLife:

```bash
openlife deploy --preview     # → ephemeral URL
openlife deploy               # → production
```

### Path B: Git-integrated (auto-deploys)

1. Push the repo to GitHub
2. https://vercel.com/new → Import the repo
3. Vercel auto-detects Next.js (no extra config thanks to `vercel.json`)
4. Add the env vars from `.env.production.example` in the Vercel UI
5. Every push to `main` → production deploy. Every PR → preview deploy.
6. After import, `openlife ship` produces the PR; Vercel produces the preview URL in the PR comment

## Webhook URL registration (after first deploy)

Once you have a production domain (e.g. `https://your-app.vercel.app`),
register these webhook endpoints:

```bash
# Clerk dashboard → Webhooks
https://your-app.vercel.app/api/webhooks/clerk
# Events: user.created, user.updated, user.deleted

# Stripe dashboard → Webhooks
https://your-app.vercel.app/api/webhooks/stripe
# Events: customer.subscription.*, checkout.session.completed
```

Then copy each webhook's signing secret back into:

- `CLERK_WEBHOOK_SECRET` (Vercel Environment Variables)
- `STRIPE_WEBHOOK_SECRET` (Vercel Environment Variables)

## Post-deploy verification

```bash
# Smoke
curl https://your-app.vercel.app/ -I            # HTTP 200
curl https://your-app.vercel.app/sign-in -I     # 200 (Clerk page)
curl https://your-app.vercel.app/api/health -I  # if you have a health endpoint

# Webhook test
# In Stripe Dashboard → test card 4242 4242 4242 4242 → subscribe to Pro
# Check Supabase: `select * from subscriptions order by created_at desc limit 1;`
# → row should exist with status='active'
```

## Custom domain

```bash
vercel domains add your-domain.com
vercel domains inspect your-domain.com   # shows DNS records to add
# Update NEXT_PUBLIC_APP_URL env var in Vercel + redeploy
# Update Clerk + Stripe webhooks to point at the custom domain
```

## Anti-patterns to avoid

- ❌ Hardcoding env values in `vercel.json` instead of using `@secret_name` or Dashboard env vars
- ❌ Pushing `.env.local` to git (gitignored by default; double-check)
- ❌ Using preview URLs in Stripe/Clerk webhooks (they change on every deploy)
- ❌ Forgetting to set `NEXT_PUBLIC_APP_URL` — breaks Stripe checkout success/cancel redirects

## Composition

- Pairs with `clerk-wire`, `stripe-billing-wire`, `supabase-rls-wire`
  (they generate the env vars that this skill registers)
- Composes with `openlife ship` (push branch) and `openlife deploy` (trigger deploy)
