esbuild vs. Vite: What This Repo's Own Lockfile Says
Neither is a dependency here — but the Vite version vitest pulls in has already dropped esbuild for Rolldown, proven straight from the lockfile.
esbuild is a Go-based JavaScript/TypeScript bundler and minifier built for raw transform speed; Vite is a dev server plus build tool that historically used esbuild internally for dependency pre-bundling while handing the production bundle to Rollup. Use esbuild directly for a library, a CLI, or a step inside a larger pipeline; use Vite for an application that needs a dev server, HMR, and a plugin ecosystem. This repository depends on neither directly — but the version of Vite it does pull in, transitively, has already stopped using esbuild at all.
Neither is a direct dependency
$ cat package.json | grep -E '"(vite|esbuild)"'
$ echo $?
1
No match, in either dependencies or devDependencies. This storefront runs on Next.js with Turbopack as its build engine — the same "the framework owns the build" situation covered for Webpack in Vite vs. Webpack, and the reason Vite vs. Vue is a layer confusion rather than a comparison: the build tool and the component model are separate choices, and a framework usually makes one of them for you. But vitest (the test runner behind npm test, listed as a direct devDependency) pulls in Vite as one of its own dependencies, which means Vite's actual dependency tree — not a hypothetical one — is sitting in this repository's lockfile right now.
What that lockfile entry says
$ node -e "console.log(require('./package-lock.json').packages['node_modules/vite'].version)"
8.1.3
Vite 8.1.3, pulled in by vitest@4.1.9. And its own dependency list, straight from the same lockfile:
"node_modules/vite": {
"version": "8.1.3",
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
"postcss": "^8.5.16",
"rolldown": "~1.1.3",
"tinyglobby": "^0.2.17"
},
"peerDependencies": {
"esbuild": "^0.27.0 || ^0.28.0"
},
"peerDependenciesMeta": {
"esbuild": { "optional": true }
}
}
rolldown is a hard dependency. esbuild is listed only as an optional peer dependency — Vite declares it can use one if the host project provides it, but doesn't install it, doesn't require it, and doesn't fall back to it for anything mandatory. Checking what's actually on disk confirms it:
$ grep -c '"@esbuild/' package-lock.json
0
$ grep -c '"node_modules/@rolldown/' package-lock.json
18
Zero esbuild platform binaries anywhere in this tree. Sixteen distinct @rolldown/binding-* native packages listed in the lockfile instead — one per target platform (Linux, macOS, Windows, even wasm32-wasi as a portable fallback) — the same shape npm uses to ship any Rust/Go-native tool across operating systems. Most "esbuild vs. Vite" articles describe Vite as esbuild-plus-Rollup, because that was true for years and is still true of older Vite majors. It stopped being true here: this repository's own lockfile shows Vite 8 has already completed the move to Rolldown — a Rust rewrite of Rollup with a compatible plugin API — as its actual bundler, with esbuild demoted to an integration point a consuming project can opt into, not a piece Vite ships itself.
What each one still does, on its own terms
| esbuild | Vite (pre-8, historical) | Vite 8 (measured here) | |
|---|---|---|---|
| Primary job | Bundle/transform/minify, directly | Dev server + build orchestrator | Dev server + build orchestrator |
| Dependency pre-bundling | — | esbuild | Rolldown |
| Production bundle | esbuild itself, if you call it as a bundler | Rollup | Rolldown |
| Written in | Go | JS (orchestration) | JS (orchestration) |
| Bundler engine language | Go | Go (esbuild) + JS (Rollup) | Rust (Rolldown) |
| Typical use | A library, a CLI, one step of a custom pipeline | A standalone app (React, Vue, Svelte) with dev server + HMR | Same, engine swapped underneath |
| Config file | None required — usable via API or CLI flags | vite.config.ts | vite.config.ts |
| Appears in this repo | Not at all | Not at all, directly | Transitively, via vitest |
The row that answers "which do I pick" for most readers is "typical use": esbuild is a tool you reach for directly when you're building something small and want raw speed with minimal ceremony; Vite is what you reach for when you want a full application dev experience and don't want to hand-assemble one from esbuild plus a dev server plus HMR plus a plugin system. They aren't really competitors at the layer most comparison posts frame them at — Vite used esbuild, the way this repo's own lockfile makes visible for one specific, real dependency.
Why this matters beyond trivia
Two migrations that used to be routine — "upgrade Vite" and "add an esbuild-based tool as a peer" — now interact differently than the always-esbuild-inside story implies:
- Upgrading
vitestto a version pulling Vite 8+ no longer guarantees anesbuildbinary lands innode_modules. A tool in your own pipeline that assumed esbuild would be present transitively (because Vite always pulled it in) can silently stop finding it. This repo'sscripts/seo/andscripts/release/tooling never made that assumption — they're described in their own documentation as "plain node, zero dependencies," which is exactly the posture that survives a change like this without noticing it happened. - The peer-dependency line is the actual opt-in point now. If a project wants esbuild-specific behavior alongside Vite 8 — a plugin that shells out to esbuild's API directly, say — it has to add
esbuildas its own dependency; Vite will use it if present (the peer dependency is satisfied) but will not go fetch it on the project's behalf.
Neither point changes anything this repository has to do today — nothing here calls esbuild's API directly, and vitest doesn't need it satisfied for the test suite to run. It's the kind of fact that matters the next time a npm test starts throwing an unfamiliar peer-dependency warning after a routine npm update, and the instinct is to assume the lockfile is broken rather than that a transitive tool's own architecture moved.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
A plugin or tool expects an esbuild binary that isn't in node_modules | Vite 8+ made esbuild an optional peer dependency instead of a hard one | Add esbuild as a direct dependency of your own project if something in your pipeline calls its API |
| An old blog post or doc says "Vite uses esbuild for pre-bundling" and it doesn't match what you see | True for Vite's earlier major versions; Vite 8 moved dependency pre-bundling to Rolldown | Check your actual installed Vite version's own package.json/lockfile entry rather than trusting a general description |
npm ls esbuild returns nothing even though vite is installed | Expected on Vite 8+ — esbuild is no longer a transitive dependency | Not a bug; install esbuild explicitly if you need it |
| Confusing "esbuild vs. Vite" with "esbuild vs. Webpack" | The former is a library/tool vs. an app dev-server-plus-bundler; the latter is two full bundlers | Frame the choice by what you're building (a library → esbuild candidates; an app → Vite/Webpack/framework-owned) |
| Assuming your framework's dev server behaves like standalone Vite | A framework (Next.js, SvelteKit) that bundles Vite internally can pin its own version and configuration, diverging from the standalone CLI defaults | Check the framework's own docs for which bundler version and config it actually wires up |
Frequently asked questions
If this codebase doesn't use Vite directly, why does it show up in the lockfile at all?
vitest, this repo's test runner (npm test), depends on Vite internally to power its module transforms and watch mode — the same way next depends on its own bundling internals without a project needing to configure them separately. Vite ends up in node_modules as a consequence of choosing vitest, not as a project-level decision.
Does Vite still use Rollup at all in version 8?
Not according to this repository's own lockfile entry — Rolldown, a Rust-based Rollup-compatible rewrite, is the dependency listed, and Rollup itself doesn't appear as a node_modules/vite dependency. Rolldown is designed to be close enough to Rollup's plugin API that most Rollup plugins are expected to keep working, but the underlying engine has changed.
Should I install esbuild myself if I'm using Vite 8?
Only if something in your own toolchain calls esbuild's API directly — a custom script, a plugin that isn't Vite-native. If your usage is "write Vite config, run vite build," Vite's own bundling no longer touches esbuild at all in this version, so there's nothing to add.
Is Rolldown the same as Turbopack, the bundler this repository's own build actually uses?
No — different projects, different languages sharing only "written in Rust, aimed at Rollup/webpack-class problems." Turbopack is Vercel's bundler, built into Next.js and used by this repo's own next build; Rolldown is the Vite team's Rollup-compatible rewrite, arriving here only as a transitive dependency of vitest, never invoked by this repo's actual production build.
Templates in this post
ASoc Till (a POS-system landing page), ASoc Timbre (an AI voice-generator marketing site) and ASoc Uptime (a web-hosting landing page) all build the same way this storefront does — no vite.config.ts, no esbuild in dependencies, the framework's own bundler doing the work.
Browse the full sets: Next.js landing page templates, Tailwind landing page templates. For the framework-owns-the-build case worked through in full, see Vite vs. Webpack; for what this repo's own build actually ships, Next.js Bundle Analyzer.
