esbuild & Turbopack Version Compatibility Reference

This reference pins the version relationships across the esbuild and Turbopack toolchains: which esbuild releases run on which Node versions, which Turbopack flag and config shape ships in each Next.js major, and how Turborepo 1.x and 2.x differ. It exists because the most expensive failures in this stack are not bugs but mismatches — an esbuild context() call against a pre-0.17 API, a next.config still using experimental.turbo after the key was promoted, or a turbo.json using pipeline under Turborepo 2.x. For the workflows these versions power, see esbuild & Turbopack Workflows; use this page to decide exactly which versions to install before you write a line of config.

Version timeline for esbuild, Turbopack/Next.js, and Turborepo Three parallel timelines marking the breaking releases of esbuild, Turbopack within Next.js, and Turborepo that this reference pins. esbuild 0.17 context API 0.19 0.21 0.25 defaults Turbopack Next 13 alpha Next 14 beta Next 15 stable dev --turbo --turbopack, turbopack key Turborepo 1.x pipeline 2.x tasks strict env, renamed key Left to right: older to newer releases; red marks a breaking change.
Figure: the breaking releases across esbuild, Turbopack within Next.js, and Turborepo that this reference pins.

What This Page Pins and Why

Three independent tools share this section but version on their own cadence. esbuild ships frequently with occasional behavior changes inside the 0.x range — there is no 1.0, so a minor like 0.17 or 0.25 can carry a breaking change a semver-trained eye would miss. Turbopack does not version independently at all; it is embedded in Next.js, so “which Turbopack” is really “which Next.js,” and the dev-flag and config-key names changed as it moved from alpha to stable. Turborepo versions normally, and its 1.x → 2.x jump renamed the central turbo.json key. Pinning all three together is the only way to keep a monorepo’s local, CI, and deploy environments hashing and building identically — the same determinism concern that drives remote caching.

esbuild ↔ Node Compatibility

esbuild’s Go binary is largely Node-agnostic for the build itself, but the JavaScript API wrapper, the context() incremental API, and the watch/serve features assume a modern Node. The table below maps the practical floor.

esbuild Node floor Context/watch API Notable behavior
0.17.x Node 12+ New context() API introduced; old incremental/rebuild/watch() on build() removed The breaking re-architecture — build({ incremental: true }) no longer exists.
0.18.x Node 12+ context() stable Minor option cleanups; safe upgrade from 0.17.
0.19.x Node 12+ context() stable Common modern baseline; widely embedded by Vite 5.
0.20.x Node 18+ context() stable Drops older Node in the published wrapper; align CI Node.
0.21.x Node 18+ context() stable Incremental refinements; no API breaks.
0.23.x Node 18+ context() stable Continued option additions.
0.25.x Node 18+ context() stable Default-tightening release (see deprecations); current baseline for this section.

The single most disruptive line is 0.17: any code written against esbuild 0.16 that called build({ incremental: true, watch: {...} }) must be rewritten to const ctx = await esbuild.context({...}); await ctx.watch();. The watch-mode workflow built on the modern API is covered in Using esbuild context watch mode for incremental rebuilds.

Turbopack ↔ Next.js ↔ Node Compatibility

Turbopack ships inside Next.js. The dev flag and config key evolved with maturity; production next build --turbopack only stabilized in the Next 15 line.

Next.js Turbopack stage Dev flag Config key Node floor
13.x alpha next dev --turbo experimental.turbo Node 16+
14.x beta (dev) next dev --turbo experimental.turbo Node 18+
15.0–15.2 stable dev, beta build next dev --turbopack experimental.turbo (deprecated) → turbopack Node 18.18+
15.3+ stable dev, --turbopack build maturing next dev --turbopack / next build --turbopack turbopack (top-level) Node 18.18+ / 20+ recommended

Two renames bite here. The flag changed from --turbo (Next 13/14) to --turbopack (Next 15); scripts that still call next dev --turbo on Next 15 hit an unknown-flag error. The config key moved from experimental.turbo to a top-level turbopack in Next 15 — experimental.turbo still works with a deprecation warning during the 15 line but should be migrated.

// next.config.js — Next 13/14 (Turbopack alpha/beta). Old shape.
/** @type {import('next').NextConfig} */
module.exports = {
  experimental: {
    turbo: {
      rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js' } },
    },
  },
};
// next.config.js — Next 15+ (Turbopack stable dev). Promoted top-level key.
/** @type {import('next').NextConfig} */
module.exports = {
  turbopack: {
    rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js' } },
  },
};

For the incremental-compilation behavior these versions expose, see Turbopack Incremental Compilation.

Turborepo 1.x ↔ 2.x

Turborepo’s major jump renamed the central config key and tightened environment-variable handling, which directly affects cache hashing.

Turborepo Config key Env handling Node floor Migration
1.x pipeline Loose; env/globalEnv optional, more implicit inclusion Node 14+ n/a
2.x tasks Stricter; declare env/globalEnv/globalPassThroughEnv explicitly Node 18+ npx @turbo/codemod migrate
// turbo.json — Turborepo 1.x. The `pipeline` key.
{
  "pipeline": {
    "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }
  }
}
// turbo.json — Turborepo 2.x. `pipeline` renamed to `tasks`.
{
  "$schema": "https://turborepo.com/schema.json",
  "tasks": {
    "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }
  }
}

The 2.x env-handling change is the subtler hazard: a task that implicitly picked up an env var under 1.x may now miss it from its hash unless you declare it under env/globalEnv, producing stale hits. Run the codemod, then audit declarations — the mechanics are detailed in Remote Caching and Distributed Build Coordination.

Known Breaking Changes & Deprecations

  • esbuild 0.17 — context API replaces incremental/watch(). Remove build({ incremental, watch }); adopt esbuild.context() plus ctx.watch()/ctx.rebuild()/ctx.serve(). This is the hard cutover dividing pre- and post-0.17 build scripts.
  • esbuild 0.25 — tightened defaults. The 0.25 line carried default-behavior changes (stricter resolution and option defaults that previously had looser fallbacks). Re-run your build after upgrading and compare --metafile output; do not assume a silent passthrough.
  • esbuild 0.20 — published wrapper Node floor raised. The JS API wrapper expects Node 18+; older CI Node images can fail to load the package even though the Go binary would run.
  • Next.js 15 — --turbo--turbopack. The dev flag was renamed. Update every dev script; the old flag errors on Next 15.
  • Next.js 15 — experimental.turboturbopack. The config namespace was promoted to a top-level key. experimental.turbo warns during the 15 line and should be migrated before it is removed.
  • Turborepo 2.x — pipelinetasks. The turbo.json key was renamed and env handling became stricter. Apply @turbo/codemod migrate and declare env vars explicitly.

Upgrade & Migration Notes

Upgrade these tools one axis at a time and re-measure, because they interact through the cache. A safe order: bump Node first and confirm the existing build is green; upgrade esbuild and diff --metafile output for unexpected tree-shaking or resolution changes; upgrade Next.js and migrate the Turbopack flag/key in the same commit so dev scripts and next.config move together; upgrade Turborepo last and run the codemod so the tasks rename and env declarations land atomically. After any of these, invalidate caches once (rm -rf .next node_modules/.cache/turbo) so the first post-upgrade run rebuilds from clean inputs rather than replaying an artifact produced by the prior toolchain — recall that the Node version is not in the Turborepo hash by default, so a Node bump alone will not bust stale artifacts.