Ship React Bundles Without a Separate Build Tool
Bun's integrated bundler handles TypeScript, JSX, and npm resolution in one command. Here is when it fits and when it does not.
30 May 2026, 11:50 UTC

The problem: your frontend build is three tools in a trench coat
You push a one-line React fix and wait for a transpiler, a bundler, and a dev server to finish before you can test it. You also maintain a lockfile and configure loaders for TypeScript and JSX. On small and medium projects, that toolchain overhead often exceeds the time spent writing code.
Bun's bundler collapses those steps. It parses TypeScript, JSX, and ES modules in a single pass, resolves npm packages through Bun's built-in installer, and emits minified output with source maps using one command.
What the command actually does
Run this from your project root:
bun bun src/main.tsx --outdir=dist
bun bun invokes the bundler. src/main.tsx is the entry point. --outdir=dist writes the bundle and source maps to dist/. No separate build step is required, though Bun respects an existing tsconfig.json for path aliases and JSX settings.
No administrator permissions are required, but the current user must have write access to dist/. The command overwrites existing files in that directory, so do not point it at a folder you need to preserve.
Worked example: a minimal React entry point
Create src/main.tsx:
import { createRoot } from 'react-dom/client';
import App from './App';
function unusedHelper() {
return 'this should not appear in the bundle';
}
const root = createRoot(document.getElementById('root')!);
root.render(<App />);
Run the bundler:
bun bun src/main.tsx --outdir=dist
To verify tree-shaking, inspect dist/main.js:
grep -n "unusedHelper" dist/main.js || echo "unusedHelper not found"
If the helper is truly unused, the command should print unusedHelper not found. If the helper appears, the bundler did not eliminate it, which may indicate a side-effect import or a configuration issue.
Run the bundle directly in Bun:
bun run dist/main.js
This executes the bundled output without a separate server. For browser testing, serve dist/ with any static file server, including Bun's built-in server.
Trade-offs and limits
Bun's bundler is not a drop-in replacement for Webpack or Vite in every project. Know the boundaries before adopting it.
- No automatic code-splitting. The single-pass architecture emits one bundle by default. It does not split routes or lazy-loaded chunks the way Webpack does. Dynamic imports work at the top level, but lazy-loading patterns need manual review.
- No built-in Sass or Less loaders. If your stylesheet pipeline relies on legacy CSS preprocessors, you must pre-process those files or run a hybrid setup.
- ES2024 target. The bundler assumes modern JavaScript. Older browsers may need a separate transpilation step.
- Runtime maturity. Bun's runtime does not yet match Node.js feature parity for niche APIs, native bindings, or C++ addons. Some npm modules may fail at runtime even if they bundle successfully.
Check before you commit
- Run
bun installand confirm dependencies resolve without a separatepackage-lock.jsonorpnpm-lock.yaml. This requires network access to fetch packages. - Run
bun bun src/main.tsx --outdir=distand compare build time against your current toolchain on the same machine. - Open
dist/main.jsand confirm unused imports are absent. - Run
bun run dist/main.jsto confirm the bundle executes. - If you use dynamic imports, test those paths explicitly; they are not guaranteed to split automatically.
Rollback
Because the bundler writes files to dist/, rollback is removing that directory:
rm -rf dist
Then return to your previous build configuration.
When to use it
Use Bun's bundler for internal tools, prototypes, and small-to-medium React applications where a single bundle is acceptable. Keep Webpack, Vite, or esbuild if you need route-based code-splitting, Sass pipelines, or broad Node.js API compatibility.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.