However, as applications grow in size and complexity, bundling performance can become a bottleneck, slowing down the deployment process and negatively impacting the overall developer experience.
Luckily, we are now entering a new era of build tools written in high-performance languages like Go or Rust. In this article, we will compare the speed and bundle size of the most popular bundlers across various configurations.
Despite the variety of available bundlers, most of them use the same packages for transpilation and minification.
The story of this performance benchmark began with an attempt to understand why it takes more than half a minute to build a relatively small application with Material UI components and how to improve that.
For tests I created several React projects:
- Empty as the baseline
- Project containing five different libraries with a large codebase commonly used in web development
- All components from Material UI
- Synthetic test with 5000 small components as a way to measure the overhead of the bundler itself
Initially, the project with Material UI components also included an icons package, but after the first measurements I found a performance degradation in Rollup, so as not to distort the final results I removed it.
The benchmark includes four bundlers in various configurations, all of them are configured as closely as possible (production build, no cache):
- Esbuild as bundler without any extra plugins
- Parcel in its default configuration with Babel and Terser
- Rollup and Webpack in three different presets: Babel and Terser, Esbuild and SWC
For the testing environment I used Mac OS
11.5.2 and Node
16.20.0, with the latest package versions at the time of writing this article.
Tests were done on a 6-core 2019 MacBook Pro with 16gb of RAM. To ensure the accuracy of my measurements each test was run three times consecutively, the final results represent the average value.
In addition, I have measured the size of the resulting bundle and its size after gzip compression with standard settings.
All code is available in the GitHub repository
Time in sec (average time for 3 runs)
|Parcel: babel + terser||3.737||11.529||8.892||57.232|
|Rollup: babel + terser||3.121||13.056||9.495||37.689|
|Webpack: babel + terser||2.471||11.529||6.406||23.889|
Bundle size in KiB
|Parcel: babel + terser||348.28||1546.24||950.09||1351.68|
|Rollup: babel + terser||157.85||1454.08||592.64||786.78|
|Webpack: babel + terser||158.33||1464.32||593.95||868.48|
Bundle size after gzip in KiB
|Parcel: babel + terser||106.91||424.13||264.39||139.05|
|Rollup: babel + terser||52.81||353.12||173.57||67.34|
|Webpack: babel + terser||52.90||407.58||175.36||84.42|
Esbuild as a bundler currently is significantly faster than others, it will be fascinating to see how it compares to swcpack in the future, but at the moment it's the ultimate winner.
When we consider Esbuild and SWC as plugins with other bundlers, there is really not much difference between them, it completely depends on the specific configuration, detailed comparison between them you can find here.
A couple of words about Parcel, when I tried to build 10 copies of three.js my results were completely identical to Esbuild benchmark, it really was the fastest, but for React application the metrics are different.
The unpleasant discovery for me was the build time of Rollup, on a large number of files/imports it shows worse results even than Webpack.
So, what to choose for React app:
- To achieve maximum performance and if you don't care about HMR support consider using Esbuild as bundler
- If you are already using Webpack, you can simply improve the build speed by using Esbuild or SWC as a loader and minimizer, in this case you can keep using CRA configuration
- If you really need HMR you can use combination of the previous options, Esbuild for production, Webpack + Esbuild loader for development (don’t use different tools for dev and production builds, there is a chance that the generated code may be different)
Top comments (2)
You are missing rspack.dev/ which is a rust based webpack implementation.
Good job! 👍