package.json engines AND in the platform dashboard. Then reproduce locally: rm -rf .next node_modules && npm ci && npm run build. If that fails, you've saved 10 deploy attempts. Other 58%: Turbopack (Next 16 default) catching barrel exports/missing 'use client'; RSC force-dynamic needed on routes hitting cookies()/headers(); env vars missing from the right scope (Production vs Preview vs Build are 3 separate buckets).If your next build passes locally but dies on Vercel, Netlify, or your VPS in 2026 — 9 times out of 10 it's one of four things. Below is the real fix list, updated for Next 15.x / 16-canary, Node 22, and the new Turbopack default.
ERR_REQUIRE_ESM or crypto.hash is not a function. Pin Node 22 LTS in your package.json engines field and in Vercel/Netlify dashboard settings.next build uses Turbopack unless you pass --webpack. Common failures: barrel exports from lucide-react, dynamic requires in CJS deps, and missing "use client" directives that webpack silently tolerated. Temporary fallback: next build --webpack while you fix root causes.Error: Dynamic server usage errors exploded in 2026 after the cache-semantics change. Fix: add export const dynamic = 'force-dynamic' or export const revalidate = 0 to routes that hit cookies(), headers(), or uncached fetches.NEXT_PUBLIC_* variable is baked in at build. Missing ones throw cryptic errors like ReferenceError: process is not defined inside client bundles. Double-check your deploy platform's environment variable scope — "Production" vs "Preview" vs "Build" are three separate settings on most platforms in 2026.Add this to package.json and mirror it in your host's build settings. Don't trust platform defaults — they lag behind Next.js requirements.
"engines": { "node": ">=22.11.0" }
Then on Vercel: Settings → General → Node.js Version → 22.x. On Netlify: NODE_VERSION=22 env var. On Railway/Render: same pattern.
"Works on my machine" fails because local next dev uses different caching than next build. Run the actual production build:
rm -rf .next node_modules && npm ci && npm run build
If that fails locally, you've just saved 10 deploy attempts. 90% of the "mystery" Vercel failures reproduce instantly with a clean npm ci.
I've shipped Next.js apps from 12 to 16 and debugged every deploy failure flavor there is. Text me the error line — I'll usually tell you the fix before we even get on a call. $100/hr, no retainer.
Send me the last 30 lines of your build log. Most Next.js deploy failures I diagnose in under 15 minutes.
💬 Text the error to PJDon't see what you were looking for?
Text PJ a sentence about what you actually need — I'll build you a free custom shareable on the house. No email, no funnel, no SOW.
📲 Text PJ — free shareable42% of the time it's Node version mismatch. Next.js 15.2+ dropped Node 18 support; if your platform still defaults to Node 18 you'll see ERR_REQUIRE_ESM or crypto.hash is not a function. Pin Node 22 in two places: package.json engines field AND in the platform dashboard. Then reproduce locally: rm -rf .next node_modules && npm ci && npm run build.
As of Next.js 16, next build uses Turbopack instead of webpack by default. Turbopack is 3.1x faster but stricter — it surfaces bugs webpack silently tolerated: barrel exports from lucide-react/radix/heroicons, dynamic require() in CJS dependencies, missing 'use client' directives. Temporary fallback: next build --webpack. Permanent fix: import lucide icons individually, add 'use client' to any file using useState/useEffect.
These fire when a route uses cookies(), headers(), or an uncached fetch but Next.js is trying to statically render it. Add export const dynamic = "force-dynamic" to the route file, or export const revalidate = 0. For opt-out at the fetch level: fetch(url, { cache: 'no-store' }).
Any NEXT_PUBLIC_* variable is baked in at BUILD time, not runtime — missing values throw at build, not first request. Most platforms in 2026 have three env scopes: Production, Preview, and Build — they're independent. Common pattern that breaks: env var set for 'Production' only, but Preview deploys fail because the same var isn't in the Build scope.
Three things differ: (1) next dev uses different caching and module resolution than next build — local dev hides production-only failures. (2) Your local node_modules has packages installed in a different order than npm ci produces on Vercel. (3) Vercel builds on fresh Linux; you're on macOS where filesystem case-sensitivity differs. Reproduce the deploy locally with: rm -rf .next node_modules && npm ci && npm run build.