Vercel vs Netlify vs Cloudflare Pages
TL;DR
Vercel is the platform built for Next.js — zero-config deployment, edge/serverless functions, image optimization, analytics, the gold standard for React/Next.js apps. Netlify is the Jamstack platform — git-connected deploys, serverless functions, edge functions, forms, identity, split testing, framework-agnostic. Cloudflare Pages is the edge-first platform — global edge deployment, Workers integration, zero cold starts, generous free tier, built on Cloudflare's network. In 2026: Vercel for Next.js and React frameworks, Netlify for Jamstack and static sites, Cloudflare Pages for edge-first applications.
Key Takeaways
- Vercel: Best for Next.js — automatic optimization, ISR, RSC, middleware
- Netlify: Best for Jamstack — framework-agnostic, built-in forms/identity
- Cloudflare Pages: Best value — generous free tier, zero cold starts, Workers
- All three deploy from git with preview deployments
- Vercel has the tightest Next.js integration (they created Next.js)
- Cloudflare Pages has the most generous free tier
Vercel
Vercel — platform for frontend frameworks:
Deploy Next.js
# Connect repo and deploy:
npx vercel
# Or via git push (auto-deploy):
# 1. Connect GitHub repo in Vercel dashboard
# 2. Push to main → production deploy
# 3. Push to branch → preview deploy
vercel.json configuration
{
"framework": "nextjs",
"buildCommand": "next build",
"outputDirectory": ".next",
"regions": ["iad1", "sfo1"],
"env": {
"DATABASE_URL": "@database-url"
},
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
],
"redirects": [
{ "source": "/old-blog/:slug", "destination": "/blog/:slug", "permanent": true }
],
"rewrites": [
{ "source": "/api/:path*", "destination": "https://api.pkgpulse.com/:path*" }
]
}
Serverless functions
// api/packages/[name].ts
import type { VercelRequest, VercelResponse } from "@vercel/node"
export default async function handler(
req: VercelRequest,
res: VercelResponse
) {
const { name } = req.query
const data = await fetch(`https://registry.npmjs.org/${name}`)
const pkg = await data.json()
res.setHeader("Cache-Control", "s-maxage=3600, stale-while-revalidate")
res.json({
name: pkg.name,
version: pkg["dist-tags"].latest,
description: pkg.description,
})
}
Edge functions
// middleware.ts (Next.js middleware runs on Vercel Edge):
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export function middleware(request: NextRequest) {
// Geo-based routing:
const country = request.geo?.country || "US"
if (country === "DE") {
return NextResponse.rewrite(new URL("/de" + request.nextUrl.pathname, request.url))
}
// A/B testing:
const bucket = request.cookies.get("ab-bucket")?.value || (Math.random() < 0.5 ? "a" : "b")
const response = NextResponse.next()
response.cookies.set("ab-bucket", bucket)
response.headers.set("x-ab-bucket", bucket)
return response
}
export const config = { matcher: ["/((?!_next|api|favicon).*)"] }
Key features
Vercel highlights:
✅ Zero-config Next.js deployment
✅ Automatic HTTPS and CDN
✅ Preview deployments per branch/PR
✅ Edge Middleware (global, zero cold start)
✅ Serverless Functions (Node.js, Go, Python, Ruby)
✅ Image Optimization (next/image)
✅ ISR (Incremental Static Regeneration)
✅ Analytics and Speed Insights
✅ Vercel KV, Postgres, Blob storage
✅ Domain management
Netlify
Netlify — Jamstack platform:
Deploy
# CLI deploy:
npx netlify deploy --prod
# Or connect GitHub for auto-deploy
# netlify.toml for configuration:
netlify.toml configuration
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "20"
# Redirects:
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
[[redirects]]
from = "/old-blog/*"
to = "/blog/:splat"
status = 301
# Headers:
[[headers]]
for = "/api/*"
[headers.values]
Access-Control-Allow-Origin = "*"
Cache-Control = "public, max-age=3600"
# Functions:
[functions]
directory = "netlify/functions"
node_bundler = "esbuild"
Serverless functions
// netlify/functions/packages.ts
import type { Handler, HandlerEvent } from "@netlify/functions"
export const handler: Handler = async (event: HandlerEvent) => {
const name = event.queryStringParameters?.name
if (!name) {
return { statusCode: 400, body: JSON.stringify({ error: "Name required" }) }
}
const data = await fetch(`https://registry.npmjs.org/${name}`)
const pkg = await data.json()
return {
statusCode: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
},
body: JSON.stringify({
name: pkg.name,
version: pkg["dist-tags"].latest,
}),
}
}
Edge functions
// netlify/edge-functions/geolocation.ts
import type { Context } from "@netlify/edge-functions"
export default async function handler(request: Request, context: Context) {
const country = context.geo.country?.code || "US"
// Geo-based content:
if (country === "DE") {
const url = new URL(request.url)
url.pathname = "/de" + url.pathname
return context.rewrite(url)
}
return context.next()
}
export const config = { path: "/*" }
Built-in features
Netlify highlights:
✅ Git-connected deploys (auto PR previews)
✅ Netlify Forms (no backend needed)
✅ Netlify Identity (auth without code)
✅ Split Testing (A/B by branch)
✅ Edge Functions (Deno-based)
✅ Serverless Functions (Node.js)
✅ Build plugins ecosystem
✅ Large Media (Git LFS)
✅ Analytics
✅ Framework-agnostic (Astro, SvelteKit, Next.js, etc.)
Cloudflare Pages
Cloudflare Pages — edge-first platform:
Deploy
# CLI deploy:
npx wrangler pages deploy ./dist
# Or connect GitHub for auto-deploy
# wrangler.toml for configuration:
wrangler.toml configuration
name = "pkgpulse"
compatibility_date = "2026-03-09"
pages_build_output_dir = "./dist"
# Bindings (access Cloudflare services):
[[kv_namespaces]]
binding = "CACHE"
id = "abc123..."
[[d1_databases]]
binding = "DB"
database_name = "pkgpulse"
database_id = "def456..."
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "pkgpulse-assets"
[vars]
ENVIRONMENT = "production"
Functions (file-based routing)
// functions/api/packages/[name].ts
interface Env {
DB: D1Database
CACHE: KVNamespace
}
export const onRequestGet: PagesFunction<Env> = async (context) => {
const name = context.params.name as string
// Check cache:
const cached = await context.env.CACHE.get(`pkg:${name}`)
if (cached) {
return new Response(cached, {
headers: { "Content-Type": "application/json" },
})
}
// Fetch from registry:
const data = await fetch(`https://registry.npmjs.org/${name}`)
const pkg = await data.json()
const result = JSON.stringify({
name: pkg.name,
version: pkg["dist-tags"].latest,
})
// Cache for 1 hour:
await context.env.CACHE.put(`pkg:${name}`, result, { expirationTtl: 3600 })
return new Response(result, {
headers: { "Content-Type": "application/json" },
})
}
Full-stack with D1 and R2
// functions/api/reports.ts
interface Env {
DB: D1Database
ASSETS: R2Bucket
}
export const onRequestPost: PagesFunction<Env> = async (context) => {
const body = await context.request.json()
// Write to D1 (SQLite at the edge):
await context.env.DB.prepare(
"INSERT INTO reports (name, data, created_at) VALUES (?, ?, ?)"
).bind(body.name, JSON.stringify(body.data), new Date().toISOString()).run()
// Store file in R2:
await context.env.ASSETS.put(
`reports/${body.name}.json`,
JSON.stringify(body.data),
{ httpMetadata: { contentType: "application/json" } }
)
return new Response(JSON.stringify({ success: true }), {
headers: { "Content-Type": "application/json" },
})
}
Key features
Cloudflare Pages highlights:
✅ Global edge deployment (300+ locations)
✅ Zero cold starts (V8 isolates, not containers)
✅ Generous free tier (unlimited sites, 500 builds/month)
✅ Workers integration (full Cloudflare platform)
✅ D1 (edge SQLite), R2 (object storage), KV
✅ Preview deployments per branch
✅ Custom domains and automatic HTTPS
✅ Framework support (Next.js, Astro, SvelteKit, Remix)
✅ Web Analytics (free, privacy-first)
✅ DDoS protection included
Feature Comparison
| Feature | Vercel | Netlify | Cloudflare Pages |
|---|---|---|---|
| Best for | Next.js | Jamstack | Edge-first apps |
| Framework support | All (Next.js best) | All | All |
| Edge functions | ✅ (Middleware) | ✅ (Deno-based) | ✅ (Workers) |
| Serverless functions | ✅ | ✅ | ✅ (file-based) |
| Cold starts | ~250ms | ~250ms | ~0ms (V8 isolates) |
| Global CDN | ✅ | ✅ | ✅ (300+ PoPs) |
| Preview deploys | ✅ | ✅ | ✅ |
| Built-in DB | Vercel Postgres, KV | ❌ | D1, KV |
| Object storage | Vercel Blob | ❌ | R2 |
| Forms | ❌ | ✅ (built-in) | ❌ |
| Identity/auth | ❌ | ✅ (built-in) | ❌ |
| Image optimization | ✅ (next/image) | ✅ (Netlify Image CDN) | ✅ (Images) |
| Analytics | ✅ (paid) | ✅ (paid) | ✅ (free) |
| DDoS protection | ✅ | ✅ | ✅ (Cloudflare) |
| Free tier | 100GB bandwidth | 100GB bandwidth | Unlimited bandwidth |
| Pricing | Per-seat + usage | Per-seat + usage | Usage-based |
When to Use Each
Use Vercel if:
- Building with Next.js (best-in-class support)
- Want zero-config deployment with automatic optimizations
- Need ISR, RSC, and advanced Next.js features
- Want integrated Postgres, KV, and Blob storage
Use Netlify if:
- Building Jamstack or static sites with any framework
- Want built-in forms and identity (no backend needed)
- Need split testing by branch (A/B testing)
- Prefer a framework-agnostic platform
Use Cloudflare Pages if:
- Want zero cold starts and true edge computing
- Need the most generous free tier (unlimited bandwidth)
- Want D1 (edge SQLite) and R2 (object storage)
- Building on the Cloudflare ecosystem (Workers, KV, Queues)
Methodology
Feature comparison based on Vercel, Netlify, and Cloudflare Pages platforms and pricing as of March 2026.
Compare deployment and developer tooling on PkgPulse →
Compare Netlify and Vercel package health on PkgPulse.