Use Bun when your Next.js pain is local: cold `node_modules` installs, slow TypeScript seed scripts, and a package manager that makes branch switches feel expensive. Keep Node.js when the bill is production—Vercel’s managed runtime, native addons like older `bcrypt` builds, and incident playbooks that still assume V8. In 2026 that split is the useful Bun vs Node decision for App Router teams. “Is Bun production ready?” is the wrong question. Ready for what: `bun install`, `bun seed.ts`, or `bun --bun next start` behind real traffic?
Kickoffs still treat this as a runtime religion. It is whether your inner loop waits on npm and tsx, or whether your deploy surface can absorb JavaScriptCore and N-API surprises. The wrong pick is either staying on a 40-second cold install out of habit, or forcing Bun as the production server because a blog post said it was faster.
What each runtime owns in a Next.js repo
Node.js is still the default Next.js execution story. `create-next-app`, `next build`, and Vercel production assume a Node-compatible process for server rendering, Route Handlers, and most adapters. Node 22+ also strips types for many scripts, which narrows, but does not erase, Bun’s “run `.ts` with no loader” advantage. For App Router products that already ship on Vercel, local Bun does not change what serves production HTML.
Bun is a runtime, package manager, bundler, and test runner in one binary (Zig + JavaScriptCore). `bun install` reads the same `package.json` and npm registry your team already uses. `bunx` replaces many `npx` calls. Native TypeScript execution removes `tsx`/`ts-node` for internal tooling. Official and community guidance in 2026 converges on the same pattern: Bun wins installs and scripts; Node remains the boring production default until you measure your tree.
They are not competitors with Turbopack. Turbopack is Next.js’s incremental bundler (`next dev --turbopack`). Bun executes the CLI and resolves modules; Turbopack still owns framework bundling when you enable it. Searching “Bun vs Turbopack” as a either/or choice wastes a sprint.
Choose Bun when the friction is the toolchain
Use Bun (at least as package manager and script runner) when most of these are true:
Cold installs dominate onboarding and CI cache misses; you already accept changing the lockfile and CI cache keys.
Internal scripts are TypeScript: Drizzle migrations, seed jobs, codegen, one-off data fixes. You want `bun ./scripts/seed.ts` without a compile step.
The monorepo workspace graph is large enough that npm/yarn waits show up in daily work.
You can keep production on Node (Vercel or a Node container) while local and CI use Bun.
You will audit `binding.gyp` / native addons before anyone runs `bun --bun next start` in prod.
On App Router, the low-risk path is mechanical: install Bun beside Node, run `bun install`, keep `next build` / production start on Node, and move seed and codegen scripts to Bun first. `bun create next-app` scaffolds the familiar `app/` layout; speed of scaffolding is not the product win—repeat installs and script startup are.
The cost is honest. Native addons compiled for V8 do not load on JavaScriptCore. Classic `bcrypt` and `canvas` still break; use `bcryptjs`, `Bun.password`, or stay on Node for those paths. Middleware and Auth.js adapters need an explicit click-through when you try Bun as the Next.js process runtime. Installer-only adoption is not enough for that path.
Choose Node when production surprises cost more than install minutes
Stay Node-first (runtime and often package manager) when most of these are true:
Production already runs on Vercel or another Node-managed host and you have no self-host Bun plan.
The dependency tree includes hard native addons without clean replacements.
Monitoring, APM, and on-call runbooks assume Node/V8 behavior and your team cannot budget Bun-specific incidents.
Middleware does non-trivial edge work and you have not regression-tested it under `bun --bun next dev`.
The team will not maintain two mental models: Bun locally, Node in prod, without a written boundary.
Node still wins the “boring server” slot for Next.js in 2026. Bun can run `next dev` / `next build` for many apps, and some teams use `bun --bun` for local speed. That is an experiment with a rollback path—not a mandate to replace the production process manager because install benchmarks looked good on a laptop.
App Router patterns that change the vote
Package manager ≠ runtime. Switching to `bun install` does not put Bun behind your production URL on Vercel. Document that in the README so juniors stop asking why `bun --bun next start` is not in the Dockerfile.
Prefer one lockfile strategy. Do not commit `package-lock.json` and `bun.lock` wars across branches. Pick Bun or npm/pnpm for the repo; CI must use the same installer that developers use.
Keep Next.js on its own bundler. Do not replace `next build` with `bun build` for App Router apps. Bun’s bundler is for general JS tooling; Next owns RSC, flight data, and route manifests.
When you trial Bun as the Next process (`bun --bun next dev`), walk Server Actions, streaming RSC, image optimization (`sharp`), and auth redirects. Failures here are usually package-specific, not “Bun is fake.” Fix the package or keep that path on Node.
A practical split that survives a real team
Greenfield Next.js on Vercel: `bun install` + Node production; Bun for seed/migrate/codegen.
Large monorepo with painful installs: Bun as the package manager first; defer runtime cutover.
Self-hosted Next with no native addons and a green test suite: trial Bun runtime in staging with the same load tests you trust for Node.
Existing npm + Node shop with on-call debt: do not big-bang rewrite; steal Bun only for the slowest TypeScript scripts.
Never claim “we migrated to Bun” when you only changed the installer. Name the boundary in the architecture doc.
PR checklist before you lock the choice
Name the Bun surface: package manager only, script runner, `next dev` runtime, or production server.
Run `bun install` and read postinstall / gyp warnings on a clean machine.
List native addons (`binding.gyp` under `node_modules`) and mark each keep-on-Node or replace.
Move one seed or migrate script to Bun; measure wall-clock before/after on the same machine.
Keep CI production build on Node until staging proves `bun --bun next build` for your app.
Update CI cache keys when the lockfile format changes.
Refuse dual package managers in one repo without a written exception.
Where teams usually get this wrong
One mistake is equating faster installs with a production runtime migration, then discovering Auth middleware or an image pipeline differs under Bun. Another is keeping npm locally while CI uses Bun so lockfiles thrash every PR. Teams also debate microbenchmarks while the real waste is a 90-second seed script still running under `tsx` on Node. Mixing `Bun.serve` APIs into a Next.js app you still deploy to Vercel is common, and pointless.
What I tell teams in kickoff
If your Next.js App Router product ships on Vercel or any Node host, adopt Bun where you feel friction every day: installs and TypeScript tooling. Keep Node on the path that serves customers until a staging audit of native modules, middleware, and RSC streaming says otherwise. If you are building a Bun-native API with Hono and no Next.js, that is a different decision. Do not drag Next’s production story into it.
Bun and Node both ship real Next.js work in 2026. The failure mode is choosing runtime novelty when your users never wait on your laptop install—or refusing Bun’s package manager when every branch switch costs a coffee break. Make that call on the cold install after `git pull`, not on which logo is winning social feeds this week.