Replacing a 200-Line Webpack Config with Two Bun API Calls
Bun's built-in bundler can replace a sprawling webpack config with a single Bun.build() call. Here's a worked migration, how to verify the output, and where webpack still wins.
12 Mar 2026, 12:50 UTC

Most small-to-medium JavaScript projects carry a webpack config that nobody on the team fully understands anymore. It grew plugin by plugin, loader by loader, and now a simple "bundle my entry file into one file" task requires 200 lines of configuration and four dev dependencies. Bun's built-in bundler collapses that into a single function call — and for many projects, that's a genuine simplification rather than a downgrade.
This post walks through what Bun's bundler actually does, a worked example of replacing a webpack setup, and the honest limits where webpack still earns its complexity.
What "zero-config" actually means in Bun
Bun's bundler doesn't guess at your intent through a config file; it takes explicit inputs and applies sensible defaults. You tell it the entry points and where to write output, and it handles module resolution (both ESM and CommonJS), JSX/TSX transpilation, and modern syntax without a separate Babel step. Because Bun is written in Zig and uses its own fast parser, cold builds are typically much quicker than a webpack cold start — though you should measure this on your own codebase rather than trusting anyone's benchmark, including this one.
The key mental shift: webpack is a plugin system that happens to bundle; Bun's bundler is a bundler with a small, fixed set of options. That trade is the whole story.
A worked example: migrating a library build
Say your current setup bundles src/index.ts into dist/bundle.js for Node consumers. With Bun, the entire build script becomes a file like build.ts in your project root:
// build.ts — run with: bun run build.ts
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "node", // or "browser" / "bun"
format: "esm", // "cjs" also supported
minify: true,
sourcemap: "external",
});
if (!result.success) {
for (const log of result.logs) console.error(log);
process.exit(1);
}
console.log("Wrote", result.outputs.map(o => o.path));Run it from the project root with bun run build.ts. No extra packages are needed — Bun.build is part of the runtime. The target option matters: "node" keeps Node built-ins external, while "browser" will error on unresolved Node APIs, which is a useful early warning if server-only code leaks into a client bundle.
Verification is straightforward: check that dist/index.js exists, then smoke-test it with node -e "import('./dist/index.js').then(m => console.log(Object.keys(m)))" to confirm your exports survived bundling. For a size comparison, run your old webpack build once more and diff the output sizes — that's the only comparison that counts for your code.
Where the trade-off bites
The limitation is the plugin ecosystem. Webpack's loader pipeline handles things Bun's bundler does not: certain CSS module conventions, custom asset pipelines, Module Federation, and framework-specific transforms that teams have built over years. If your webpack config exists mostly to run TypeScript and resolve imports, Bun replaces it cleanly. If it encodes a bespoke asset workflow, you'll end up reimplementing that workflow in a Bun plugin or a separate script — and at that point the "simplification" may be fictional.
Also note that Bun's bundler API changed across its 0.x releases and continued to be refined after 1.0. Pin your Bun version in CI (bun --version in your pipeline logs is cheap insurance) and re-run your verification steps after any upgrade.
A practical decision rule
Try the migration in a branch: write the 15-line build script, run both builds, compare output size, and run your existing test suite against the Bun-built artifact. If tests pass and the output is equivalent, delete the webpack config and its dev dependencies — that deletion is the real win, not the build speed. If you hit a missing loader capability, you've learned that in an afternoon rather than after committing to the switch.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.