Vercel Deployment Guide 2026 — Next.js, Edge Functions, and Production Optimization
Advertisement
Introduction
Why This Matters
Vercel is the platform Next.js was built for. It provides zero-config deployment, automatic HTTPS, global CDN, preview deployments on every PR, and Edge Functions that run in 100+ locations worldwide. For Next.js applications in 2026, Vercel is often the fastest path from code to production with the best developer experience available.
Getting Started with the Vercel CLI
# Install and deploy
npm install -g vercel
vercel # Interactive setup on first run
vercel --prod # Deploy to production
# Link an existing project
vercel link
vercel env pull # Pull environment variables to .env.localvercel.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
# Add variables per environment
vercel env add DATABASE_URL production
vercel env add DATABASE_URL preview
vercel env add DATABASE_URL development
# Pull all to local
vercel env pull
# List all
vercel env lsEnvironment variable scoping in Next.js:
// Server-only (never exposed to browser)
process.env.DATABASE_URL
process.env.JWT_SECRET
// Exposed to browser — must have NEXT_PUBLIC_ prefix
process.env.NEXT_PUBLIC_API_URL
process.env.NEXT_PUBLIC_POSTHOG_KEYEdge Functions for Geo-Personalization
Edge Functions run in 100+ locations close to users — ideal for routing, personalization, and A/B testing:
// app/api/geo/route.ts
import { NextRequest, NextResponse } from 'next/server'
export const runtime = 'edge'
const COUNTRY_ROUTES: Record<string, string> = {
IN: 'https://in.myapp.com',
GB: 'https://uk.myapp.com',
AU: 'https://au.myapp.com',
}
export async function GET(request: NextRequest) {
const country = request.geo?.country || 'US'
const city = request.geo?.city
if (country in COUNTRY_ROUTES) {
return NextResponse.redirect(COUNTRY_ROUTES[country])
}
return NextResponse.json({
message: `Hello from ${city || 'your location'}!`,
country,
region: request.geo?.region,
timezone: request.geo?.timezone,
})
}// middleware.ts — Edge middleware runs before every request
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const country = request.geo?.country
// Add country header for downstream use
const response = NextResponse.next()
response.headers.set('x-country', country || 'unknown')
// Block specific countries
if (country === 'XX') {
return new NextResponse('Service not available in your region', { status: 451 })
}
return response
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}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 }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
)
}Track custom events:
import { track } from '@vercel/analytics'
// Track conversions and user actions
function SignupButton() {
return (
<button
onClick={() => {
track('signup_clicked', { plan: 'pro', source: 'hero' })
}}
>
Get started
</button>
)
}Incremental Static Regeneration (ISR)
// Static at build time, revalidated in the background
export const revalidate = 3600 // Re-generate every hour
export default async function BlogPage() {
const posts = await getPosts() // Cached and served statically
return <PostList posts={posts} />
}On-demand revalidation via webhook:
// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache'
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}`)
revalidatePath('/blog')
return Response.json({ revalidated: true, slug })
}Preview Deployments and GitHub Integration
Every pull request automatically gets a unique preview URL. Notify the PR with the URL:
# .github/workflows/preview-comment.yml
name: Preview URL
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
const prNumber = context.payload.deployment.payload.pr_number
if (!prNumber) return
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `Preview deployed: ${url}`,
})Common Mistakes
- Using
process.envvalues at module level — they are not available during edge runtime build; access them inside request handlers - No
revalidateon data-heavy pages — without ISR, every request hits your database even for identical content - Storing large files in the repo — Vercel has a 250MB uncompressed source limit; use S3 or Vercel Blob for media
- Not scoping Edge Functions correctly — middleware matcher patterns that are too broad slow down static asset serving
- Exceeding the 1MB response size limit on Edge — break large API responses into paginated chunks
Best Practices
- Use
revalidateon pages that fetch database content to avoid cold database hits on every request - Store secrets in Vercel Environment Variables, not in
.envfiles committed to the repository - Use
@vercel/postgresor Neon for serverless-compatible database connections that work in Edge runtime - Enable Vercel Web Analytics and Speed Insights on all projects — they are free and surface real user performance data
- Use preview environments to review visual changes, test API integrations, and share work with stakeholders before merging
Key Takeaways
- Vercel auto-deploys on every push to main and creates unique preview URLs for every pull request automatically
- Edge Functions run in 100+ locations worldwide with near-zero cold starts — use them for auth checks, redirects, and personalization
- ISR with
revalidateserves static HTML instantly while regenerating content in the background — no server needed NEXT_PUBLIC_prefix is required for environment variables that need to be accessible in browser-side code- Vercel Analytics tracks page views and custom events; Speed Insights shows real user Core Web Vitals from production traffic
- Cron jobs in
vercel.jsonreplace the need for external schedulers for simple recurring tasks - The Vercel free tier includes unlimited preview deployments, 100GB bandwidth, and analytics for personal projects
- On-demand revalidation via
revalidatePath()lets a CMS webhook trigger page regeneration instantly after content updates
Advertisement
Related reading
Next.js Deployment Guide — Vercel, Docker & Self-Hosted in 20266 min readGitHub Actions for Node.js — Complete CI/CD Pipeline Guide 20265 min readAWS Lambda — Serverless Functions Complete Guide5 min readGCP Cloud Run — Serverless Container Deployment Guide5 min readAzure Container Apps — Serverless Containers on Microsoft Azure5 min readNext.js Middleware — Authentication, Routing, and Edge Logic6 min read