Matthew Tyson
Contributing Writer

Bundling with the Bun.js bundler

Bun.js is the drop-in replacement for Node that now comes with a blazingly-fast bundler. Here's a first look at bundling with Bun.js.

Blazing fast - runner on fire
Credit: d1sk/Shutterstock

Bun.js is an exciting new project in the JavaScript ecosystem that offers a comprehensive runtime and toolchain.ย Iโ€™ve previously written a general introduction to the project as a drop-in replacement for Node.js.ย This article focuses on Bunโ€™s new bundling feature. While itโ€™s still in early releases, the Bun bundler is quite capable and flaunts Bunโ€™s speed. It could easily replace tools like WebPack, Vite, and Snowpack.

Letโ€™s check out the new bundling support in Bun.

Why another bundler?

The first question for many developers is why we need another bundler.ย We already have a few of them, after all.

So whatโ€™s special about the Bun bundler? One advantage is that having a bundler in the Bun toolchain brings everything under one roof. Rather than building a toolkit from parts, youโ€™ve got everything you need in one toolchain. The other advantage is that Bun is built for speed, which matters for bundling. Bun is built on Zig and JavaScript to support faster built times. When magnified across development and devops (CI/CD) workflows, Bunโ€™s performance advantage can have a significant impact on productivity.

State of the Bun bundler

The Bun.js bundler is still in beta, so we can expect a few rough edges.ย But one thing you canโ€™t miss is the speed of the engine.ย Shane Oโ€™Sullivan in the SOS blog wrote that building with Bun seemed to โ€œfinish as the Enter key was still traveling back upwards from executing the command.โ€ย  In short, itโ€™s quick. So are the other parts of Bun, like the server and package manager.ย Again, having all the tools in one place and working fast yields an evolved developer experience, which many developers will welcome.

Take the bundler for a test drive

Letโ€™s take the Bun.js bundler for a test drive with a create-react-app.ย For this test, youโ€™ll need Node and/or NPM installed.ย You can install the create-react-app with the command: npm i -g create-react-app, or just use npx, as weโ€™ll do below.

We of course also need Bun.js, and weโ€™ll need the latest version that includes the bundler (0.6.0+).ย ย  There are several ways to install bun.ย You can use: npm: $ npm i -g bun. Or, if you have it already installed with an older version, make sure to upgrade with $ bun upgrade.

Letโ€™s create a new application by typing: $ npx create-react-app bun-react.ย Then, switch to the new directory with $ cd bun-react.ย Note that create-react-app will run npm install for you automatically.

If we were just using NPM, we could run the app in dev mode with $ npm run start. Instead, letโ€™s bundle it together and serve it with Bun.ย 

To start, bundle the application with: $ bun build ./src/index.js --outdir ./build.ย That will create a bundle of the application (with a default target of the browser) in the /build directory.ย 

Now, we need to create an index.html page like the one shown here.

Listing 1. Index.html with JavaScript and CSS included


<html>
  <body>
    <link rel="stylesheet" href="App-f6cf171344c59401.css">
    <div id="root"></div>
    <script type="module" src="/index.js"></script>
  </body>
</html>

Note that your CSS file will be different from mine depending on the build output.ย Once you have the index in place, you can serve the application with: $ bunx serve build.

Visiting localhost:3000 will now give you the familiar create-react-app landing page.

Execute the build with a script

You can also use a script file to execute build commands with the Bun API. The one shown in Listing 2 is the equivalent of the command-line call we just executed.

Listing 2. Build script


await Bun.build({
  entrypoints: ['./src/index.js'],
  outdir: './build',
})

Bundling a server

As the Bun documentation notes, you can often execute server-side code directly; however, bundling such code can reduce startup times. Moreover, the benefits of doing so will compound in CI/CD scenarios.

To bundle a server, you change the target type to โ€œbunโ€ or โ€œnode,โ€ depending on the runtime you are using.ย Letโ€™s create a simple Express server and run it with Bun.js.

Weโ€™ll use theย Hello World serverย shown in Listing 3.

Listing 3. The Express server


const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Next, we use the following script to bundle the server.

Listing 4. Bundling the server


await Bun.build({
  entrypoints: ['./index.js'],
  outdir: './out',
  target: 'bun'
})

This code outputs a complete server to ./out/index.js.ย You can run it with bun out/index.js and get the anticipated behavior (โ€œHello World!โ€ printed to the browser at localhost:3000).

Build a standalone executable

The bundler includes a very nice power, to โ€œcompileโ€ an application down to an executable target.

We can take our Express server and make an executable with it by using the --compile switch, as shown in Listing 5.

Listing 5. Compile a standalone server


await Bun.build({
  entrypoints: ['./index.js'],
  outdir: './out',
  target: 'bun'
})

This will drop a server file into the current directory which we can run with: ./server.ย When you do this, youโ€™ll get the same server behavior.ย Pretty slick!

Do more with Bun

Letโ€™s look at a few more of Bunโ€™s bundling features.ย To start, we can have our bundled server (from Listing 3) output a text file to the browser.ย Create a file in the working directory, named โ€œquote.txt,โ€ and add some text to it. (I used โ€œAfter silence that which comes nearest to expressing the inexpressible is music.โ€)ย 

We can reference the file in the server as shown in Listing 6.

Listing 6. Outputting a text file to the browser


bun build ./index.js --compile --outfile server

Now we can bundle the text file into our application with the command: $ bun build ./index.js --compile --outfile server.ย 

When you run the server now, youโ€™ll see the contents of the quote.txt file output to the browser.

Splitting

Another important feature the Bun.js bundler supports is splitting.ย In essence, whenever you have two entry points in the application that both rely on the same third bundle, you can instruct the bundler to break this out into a separate chunk.ย You do this by setting the splitting property to true.

We can see this in action by duplicating the index.js file: $ cp index.js index2.js.ย Now, we have two files that both reference the quote.txt file.

Next, create a build.js file like the one in Listing 7.

Listing 7. A build file with splitting


const express = require('express')
const app = express()
const port = 3000
import contents from "./quote.txt";

app.get('/', (req, res) => {
  res.send(contents)
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

This build file sets splitting to true and adds both index.js and index2.js to the entrypoints.ย Now, we can run the build with: $ bun build.js.ย 

The application will be output to /build and youโ€™ll see three files: index.js, index2.js, and chunk-8e53e696087e358a.js.

This is a contrived example but in many situations, especially on the front end, splitting is an important feature for reducing code bundle sizes.ย The browser can cache one version of the bundle and all other code that depends on it will avoid having it included, and also avoid that trip to the server. When conscientiously used, code splitting is key to performance. The Bun bundler makes it dead simple to use.

Plugins

Bun uses a generic plugin format for both the runtime engine and the bundler. You can learn about plugins here.ย 

For the bundler, you can add plugins as shown in Listing 8, where we include a hypothetical YAML plugin.ย 

Listing 8. Plugin syntax


await Bun.build({
  entrypoints: ['./index.js','index2.js'],
  outdir: './build',
  target: 'bun',
  splitting: true
})

The bundler also has controls for sourcemap generation, minification, and โ€œexternalโ€ paths (resources not included in final bundle).ย You can also customize naming conventions with the naming property and fine-tune the root directory with the root property.ย The publicPath property allows you to define a root path for URLs, to handle things like images being hosted at a CDN.

You can also define global variables that the bundler will replace with the value given by the define property, which works something like a macro.

Conclusion

Using the Bun.js bundler just feels easy. It takes an overall cleaner approach to bundling than earlier tools, and it applies the lessons learned from them. No doubt, there are edge cases, gotchas, and wrinkles to be ironed outโ€”both in the tool and for complex or very custom build chains.ย But the Bun.js team is working furiously on the codebase, and we can expect improvements over time. The toolโ€™s high points are its simplicity and the fact that itโ€™s part of an all-in-one toolset (bundler, packager, runtime), and the palpable speed of the engine. Altogether, you get a distinct feeling that you are using a next-generation tool. Itโ€™s probably a good indicator ofย what we can expect to see from server-side JavaScript going forward: comprehensive, seamless, and fast.

Matthew Tyson
Contributing Writer

Matthew Tyson is a contributing writer at InfoWorld. A seasoned technology journalist and expert in enterprise software development, Matthew has written about programming, programming languages, language frameworks, application platforms, development tools, databases, cryptography, information security, cloud computing, and emerging technologies such as blockchain and machine learning for more than 15 years. His work has appeared in leading publications including InfoWorld, CIO, CSO Online, and IBM developerWorks. Matthew also has had the privilege of interviewing many tech luminaries including Brendan Eich, Grady Booch, Guillermo Rauch, and Martin Hellman.

Matthewโ€™s diverse background encompasses full-stack development (Java, JVM languages such as Kotlin, JavaScript, Python, .NET), front-end development (Angular, React, Vue, Svelte) and back-end development (Spring Boot, Node.js, Django), software architecture, and IT infrastructure at companies ranging from startups to Fortune 500 enterprises. He is a trusted authority in critical technology areas such as database design (SQL and NoSQL), AI-assisted coding, agentic AI, open-source initiatives, enterprise integration, and cloud platforms, providing insightful analysis and practical guidance rooted in real-world experience.

More from this author