Skip to content
NetlifyVercel
Migration Guide

Netlify to Vercel Migration Guide

For a Next.js app this migration is an afternoon, not a project — I moved this site. The checklist below is what actually needs attention: redirects, functions, DNS, and the things that silently differ.

What Changes
  1. netlify.toml redirects/headers become next.config or vercel.json
  2. Netlify Functions become Vercel Functions or Next.js route handlers
  3. Netlify Forms has no Vercel equivalent — forms need a real backend
  4. Environment variables re-enter by hand (and per-environment)
  5. DNS: either move the zone or point records at Vercel from Netlify DNS
Migration Path

Deploy to Vercel alongside Netlify first — connect the repo, set env vars, verify the preview URL fully works — and only then touch DNS. You can keep Netlify DNS hosting the zone and point an A record at Vercel; the zone and the hosting are separate decisions. This site runs exactly that split.

In detail

When the move makes sense

For Next.js specifically, Vercel is the platform built by the framework's maintainers, and it shows at the edges: new Next.js features (partial prerendering, the newest caching semantics, App Router refinements) work on Vercel the day they ship, while other hosts' adapter layers catch up on a lag. Netlify's Next.js runtime is genuinely good and most apps run fine on it — but when something breaks in the gap between framework and adapter, you're debugging the adapter. If your site is Astro, Eleventy, or plain static, this argument evaporates and Netlify is every bit as good; this migration is mostly a Next.js story.

The other honest reasons: image optimization included on Vercel's free tier where Netlify's is metered, preview deployments that behave identically to production for App Router apps, and consolidating where your other projects already live. What's not a reason: pricing at small scale, where the two free tiers are near-equivalent, and both get expensive at high bandwidth in similar ways. If Netlify currently works and you're not on Next.js, staying is a fine decision — migrations without a driver are just risk.

Translating the config

Everything in netlify.toml needs a new home. Redirects move to redirects() in next.config for a Next.js app (or vercel.json otherwise) — note the syntax shift from Netlify's from/to/status to source/destination/permanent, and that Netlify's catch-all SPA redirect (/* → /index.html 200) is simply unnecessary on Vercel for a Next.js app. Custom headers translate the same way via headers(). Build settings mostly disappear — Vercel detects Next.js and does the right thing — but check the Node version pin and any NPM_FLAGS workarounds you'd accumulated.

Netlify Functions port to Vercel with small signature changes, but for a Next.js app the better move is folding them into the app as route handlers (app/api/*/route.ts) — one deploy, one runtime, one place to look. Scheduled functions become Vercel cron jobs defined in vercel.json. The one true gap is Netlify Forms: the attribute-based form handling has no Vercel equivalent at all, so every form needs an actual backend — a route handler posting to Resend covers the contact-form case in about twenty lines and honestly ends up more controllable than the Netlify magic it replaces. Inventory forms before you migrate, not after the first missed lead.

The DNS split nobody tells you about

You do not have to move your DNS zone to move your hosting. This site's zone stays on Netlify DNS — the nameservers still point at NS1 — while an A record for the apex points to Vercel and a CNAME carries www. The zone is just a phone book; it can list any host. This matters when the zone carries things that predate the site: MX records for email, SPF/DKIM/DMARC TXT records, verification records for a dozen services. Re-creating all of that at a new DNS provider is where "afternoon migration" becomes "why is email bouncing" — leaving the zone alone and changing two records is the low-risk path.

Sequence it so there's no gap: add the domain in Vercel first (it will show as unverified/misconfigured — fine), then update the A and CNAME records at your DNS host, then wait out the TTL. Vercel provisions the TLS certificate automatically once the records resolve to it. Keep the Netlify site alive during propagation — stragglers on cached DNS still land somewhere real — and delete it days later, not minutes. If you do want to consolidate DNS onto Vercel eventually, do it as a separate change on a separate day; bundling a zone transfer into a hosting cutover doubles the blast radius of either going wrong.

The silent differences

Environment variables don't transfer — re-enter them in Vercel's dashboard, and use the moment to sort them into production/preview/development scopes, which Netlify's flat deploy contexts encouraged you to skip. Watch for Netlify-injected variables your code may have quietly depended on: URL, DEPLOY_PRIME_URL, CONTEXT all have Vercel equivalents (VERCEL_URL, VERCEL_ENV) with different names and slightly different semantics — VERCEL_URL has no protocol prefix, a classic five-minute bug.

Behavioral differences to verify on the preview URL before DNS moves: trailing-slash handling defaults differ (pin it explicitly in next.config so URLs don't fork), Netlify's _redirects file is ignored silently if you'd used one alongside the toml, and ISR/caching semantics are Vercel-native rather than adapter-emulated, so revalidation timing can shift. Function limits differ in the details — execution duration and payload caps — which only matters if a Netlify function was already flirting with its limits. None of these are hard problems; all of them are invisible until production traffic finds them, which is exactly why the parallel deploy exists. Run the full user journey on the .vercel.app URL, then move DNS with confidence.

Other migration guidesView all →
Related

Planning this migration?