Vercel Deployment Complete Guide 2026: Next.js, Edge Functions, and Analytics

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Vercel 2026: The Best Platform for Next.js

Vercel is where Next.js lives. Zero-config deployment, instant previews per PR, global CDN, edge functions — it's the fastest way to get a production-grade app live.

Getting Started

npm install -g vercel

# Deploy
vercel

# Production deploy
vercel --prod

# Deploy with env
vercel --prod --env DATABASE_URL="postgresql://..."

vercel.json Configuration

{
  "buildCommand": "npm run build",
  "installCommand": "npm ci",
  "framework": "nextjs",
  "regions": ["iad1", "sin1"],

  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-XSS-Protection", "value": "1; mode=block" }
      ]
    },
    {
      "source": "/_next/static/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    }
  ],

  "rewrites": [
    { "source": "/old-blog/:slug", "destination": "/blog/:slug" }
  ],

  "redirects": [
    { "source": "/about-us", "destination": "/about", "permanent": true }
  ],

  "crons": [
    { "path": "/api/cron/daily-digest", "schedule": "0 8 * * *" }
  ]
}

Environment Variables

# Set environment variables
vercel env add DATABASE_URL production
vercel env add DATABASE_URL preview
vercel env add DATABASE_URL development

# Pull to local .env.local
vercel env pull

# List all
vercel env ls
// Different variables per environment
// .env.production    → used in production
// .env.preview       → used in preview deployments
// .env.development   → used locally

// next.config.js — expose to browser
const nextConfig = {
  env: {
    NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
  },
}

Edge Functions

// app/api/geo-redirect/route.ts — runs at the Edge
import { NextRequest, NextResponse } from 'next/server'

export const runtime = 'edge'

const COUNTRY_REDIRECTS: Record<string, string> = {
  IN: 'https://in.webcoderspeed.com',
  GB: 'https://uk.webcoderspeed.com',
}

export async function GET(request: NextRequest) {
  const country = request.geo?.country || 'US'
  const city = request.geo?.city

  // Redirect based on geography
  if (country in COUNTRY_REDIRECTS) {
    return NextResponse.redirect(COUNTRY_REDIRECTS[country])
  }

  // Personalize response
  return NextResponse.json({
    message: `Hello from ${city || 'unknown city'}!`,
    country,
    region: request.geo?.region,
  })
}

Vercel Analytics and Speed Insights

// 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 />       {/* Page views and custom events */}
        <SpeedInsights />    {/* Real user Core Web Vitals */}
      </body>
    </html>
  )
}

// Track custom events
import { track } from '@vercel/analytics'

function SignupButton() {
  return (
    <button onClick={() => {
      track('signup_clicked', { plan: 'pro', source: 'hero' })
    }}>
      Sign up
    </button>
  )
}

Preview Deployments for Every PR

Every pull request gets a unique URL automatically. Enable in your GitHub settings:

# .github/workflows/preview-comment.yml
name: Preview URL Comment

on:
  deployment_status:

jobs:
  comment:
    runs-on: ubuntu-latest
    if: github.event.deployment_status.state == 'success'
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const url = context.payload.deployment_status.target_url
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.deployment.payload.pr_number,
              body: `✅ Preview deployed: ${url}`
            })

Custom Domains

# Add domain
vercel domains add webcoderspeed.com
vercel alias my-project-git-main-username.vercel.app webcoderspeed.com

# SSL is automatic — Let's Encrypt
# DNS: Add CNAME record → cname.vercel-dns.com

Vercel Postgres (Built-in Database)

// Vercel Postgres — PostgreSQL without the ops
import { sql } from '@vercel/postgres'

const { rows } = await sql`
  SELECT * FROM posts
  WHERE published = true
  ORDER BY created_at DESC
  LIMIT 10
`

// Works in Edge Runtime too (HTTP-based connection)

ISR: Incremental Static Regeneration

// Generate static at build, revalidate in background
export const revalidate = 3600  // Revalidate every hour

export default async function BlogPage() {
  const posts = await getPosts()  // Cached for 1 hour
  return <PostList posts={posts} />
}

// Or on-demand revalidation from a webhook
// app/api/revalidate/route.ts
export async function POST(request: Request) {
  const { secret, slug } = await request.json()

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

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

Vercel's free tier handles most hobby projects. Pro ($20/month) adds team features, higher limits, and Web Analytics. Enterprise for high-traffic production apps.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro