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)
Cold Starts, Runtime Models, and What They Mean for Real Apps
The cold start problem is one of the most misunderstood aspects of serverless deployment, and each platform handles it differently in ways that materially affect application latency.
Vercel and Netlify both use container-based serverless functions. When a function receives its first request after a period of inactivity, the platform must spin up a container, load the Node.js runtime, and initialize the function's module graph before the request is handled. This typically adds 200-500ms to the first request. Vercel mitigates this with its Edge Middleware, which runs on V8 isolates and has zero cold start, but Edge Middleware has restrictions: no Node.js built-ins, no fs, and a 2MB code size limit. Full serverless functions (in /api/) still face container cold starts on Vercel's Hobby and Pro tiers.
Cloudflare Pages Functions run on Cloudflare Workers, which use V8 isolates rather than containers. Isolates share a single V8 process and start in under 5ms globally — effectively eliminating cold starts. The tradeoff is that Workers run in a restricted environment: no Node.js compatibility layer by default (though Cloudflare's nodejs_compat flag enables most APIs), memory limits of 128MB per request, and CPU time limits of 50ms on the free tier (30 seconds on paid). These constraints force you to keep functions lean, which is a genuine architectural constraint for compute-heavy operations.
For a Next.js application with API routes that do database queries, the cold start difference between Vercel and Cloudflare Pages is real but often overstated. If you're querying Vercel Postgres (which runs on Neon) or Supabase, the database connection overhead usually exceeds the container cold start anyway. The zero-cold-start advantage of Cloudflare Pages is most valuable for latency-sensitive endpoints that are hit frequently enough to stay warm, or for globally distributed apps where edge latency to the database matters less than function startup time.
Pricing at Scale: Where Each Platform Becomes Expensive
All three platforms have generous free tiers, but the cost curves diverge sharply at production scale. Understanding where the bills accumulate helps you make a decision that holds up past the prototype stage.
Vercel's pricing for teams centers on per-seat costs ($20/member/month on Pro) plus usage-based charges for function invocations, bandwidth, and build minutes. The function invocation cost (typically $0.60 per million after the included allowance) is competitive, but Vercel's ISR and image optimization create additional billing surface. A high-traffic Next.js site with aggressive image optimization and many ISR routes can generate unexpected bills. Vercel's enterprise tier includes spend controls and custom limits.
Netlify's pricing is similarly seat-based ($19/member/month on Pro) with usage charges for build minutes and function invocations. Netlify's build minutes are a more common pain point than Vercel's — running 300 builds per month on a team with many deployments is easy to exceed. The Forms feature saves money on third-party form providers for content sites, but the limit of 100 form submissions per month on the free tier is very easy to hit.
Cloudflare Pages has the most developer-friendly pricing structure. The free tier includes unlimited sites, 500 builds per month, and unlimited bandwidth. Cloudflare Workers (which power Pages Functions) cost $5/month for 10 million function calls, then $0.50 per additional million — substantially cheaper than Vercel or Netlify at high volume. Cloudflare's KV storage and D1 database have their own pricing, but for applications already on the Cloudflare ecosystem (using R2, KV, Queues, or Durable Objects), the total platform cost often comes out lower than either competitor, especially for high-traffic APIs.
Framework Compatibility and Deployment Edge Cases
Each platform's official support for non-Next.js frameworks deserves scrutiny, since "framework support" often means different levels of optimization.
Vercel supports Astro, SvelteKit, Remix, Nuxt, and others with automatic framework detection, but the depth of optimization is not equal. Next.js gets first-class treatment: ISR, React Server Components, and Partial Prerendering are implemented natively in Vercel's infrastructure. SvelteKit and Astro get sensible defaults but do not benefit from the same deep integration. If you are building a SvelteKit app on Vercel versus SvelteKit on Netlify, the difference is negligible — both platforms support it well via adapters.
Netlify's build plugin ecosystem is a genuine differentiator. Plugins like netlify-plugin-cache-nextjs, the Gatsby cache plugin, and third-party integrations for Storybook and Lighthouse run as hooks in the build process. For teams with complex build pipelines that need pre- or post-build steps, Netlify's plugin architecture is more flexible than Vercel's vercel.json approach. The Netlify Adapter for SvelteKit and the Astro adapter are officially maintained.
Cloudflare Pages has the most constraints for framework compatibility. Next.js on Cloudflare Pages runs through the @cloudflare/next-on-pages adapter, which works well for most use cases but does not support all Next.js features — notably, Node.js-dependent API routes may need modification to work within the Workers runtime. The next-on-pages adapter is actively maintained and improves with each release, but teams with heavy use of Node.js-specific APIs should test thoroughly before committing to Cloudflare Pages for a Next.js project.
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.
See also: AVA vs Jest and Coolify vs CapRover vs Dokku 2026, Gitea vs Forgejo vs Gogs (2026).