In this post, we’ll examine some tools and techniques for reducing the size of your Webpack bundle. We’ll use Moment.js and Lodash as a case study, as you’re likely to see them in a legacy application that has grown too large. Even though Moment.js has been deprecated, it still gets almost 20 million npm downloads per week.
You’ll also see how to use Babel and Webpack’s production mode to reduce your bundle size further. And while we're at it, we’ll introduce some lightweight alternatives.
Let's get started
We’ll use Webpack Visualizer by Chris Bateman to determine which modules contribute the most to your bundle’s size. When you upload a bundle’s stats file, the visualizer displays the dependency graph in your bundle as a radial tree.
To follow along in this walkthrough at a higher level, upload the stats file provided for each step. You can find all the code files for this walkthrough on GitHub.
If you prefer to build the bundle in a more hands-on approach, start by setting the mode option to development. The mode option tells Webpack how to configure its optimizations for the bundle’s intended use. Although the default value is production, non-trivial projects typically benefit from the verbose comments and the useful component names that Webpack includes in development.
The only packages installed at this point are
webpack-cli. As you step through each optimization, import the modules into
src/index.js and trim them using the provided techniques.
To examine the size of the starting bundle, upload baseline-stats-dev.json to the Webpack Visualizer.
The output for your baseline bundle should have a
raw size of 0.3 KiB and an
actual size of 1.5 KiB. The relevant value in this demonstration is the
In the project’s root directory, create a Webpack configuration file named
webpack.config.js and enter the following code into it:
Moment.js used to be a popular library (again, it’s now deprecated) that provided a suite of functions to generate, parse, validate, and manipulate dates and times.
It was used often in applications requiring internationalization because it included a set of locales (like en_US).
Each locale provides information about time zones and the standardized date and time formats for a particular country, letting an application manage and display dates based on a user's language.
Upload moment-stats-raw-dev.json or your equivalent
stats.json file to examine your bundle:
The bundle now takes up an enormous 744.4 KiB. This is because Webpack imports every Moment locale, regardless of whether it’s used, by default.
Note that the core moment.js library takes up 188 KiB — a little over a quarter of the bundle’s volume:
The remaining volume consists of locales, each represented by a segment of the large outer ring and belonging to the
To reduce your bundle size, you can use Webpack’s built-in IgnorePlugin, which lets you use regular expressions or filter functions to specify which modules Webpack should skip during compilation.
Webpack automatically supplies the
checkResource filter function as part of the
IgnorePlugin. This filter is called for each resource that could be included in the bundle, along with its context, i.e., where that resource is located. You can specify
true if the resource should be excluded or
false if it should remain in the final bundle.
For example, suppose your project only applies to UK-based customers, and you only need the
en-gb locale. In that case, you can ignore all the other locales by using the following configuration in
The bundle size, visualized by uploading moment-stats-locale-dev.json, is now a more manageable 183.9 KiB:
The locale module only makes up 3.3% of the overall size. Only two locales remain: One is the
en-gb locale we specified, and the other is a non-country-code module.
To exclude all locales instead, set the
if statement to return
Bundlephobia shows Moment’s size to be 290.4 KiB — this is its Webpack production size, which you’ll see toward the end of this walkthrough. It also recommends alternatives, each of which is smaller in this case.
The smallest is Day.js, which is up to 96% smaller than Moment.
To see the difference using Day.js would make in your bundle, upload dayjs-stats-raw-dev.json to the visualizer.
The total package size is now 9.3 KiB — Day.js is only 9 KiB. This is a colossal reduction from the 744.4k size when using Moment before removing locales, and is still a large reduction from the 183.9k without the locales.
If you have an application that deals with dates and time, replacing Moment with a lighter library like Day.js is the best way to reduce the size of your Webpack bundle.
The table below shows an overview of the size differences for each step in this section.
Size in KiB
Moment.js, en-gb locale only
Babel also contains tools for reducing bundle sizes, especially when working with certain libraries. In the next section, you’ll see how to use Babel plugins with Lodash to reduce the bundle size.
Before continuing, make sure you have a clean copy of your baseline project.
To use Babel, first install the core Babel libraries by running the following three commands:
Then, add Babel to your Webpack configuration by modifying
webpack.config.js so it contains the following code:
This tells Webpack to use the
preset-env when generating your bundle. The @babel/preset-env preset lets you specify the target browsers without worrying about individual syntax features.
babel-plugin-lodash module reduces the size of the Lodash export by only importing the functions used in the code.
To demonstrate this, let’s use Lodash’s
_.clone function. Import it as a module with the following code:
clone() from anywhere inside the index.js file. This ensures that
clone() is picked up and included as part of your bundle.
To visualize the bundle without the Babel plugin, upload lodash-stats-raw-dev.json to the visualizer.
This gives you an initial size of 552.5 KiB — 99.9% of which is caused by Lodash.
Because you are only using the clone function, the rest of the Lodash library can be removed. You can do this by installing
babel-plugin-lodash using the following command:
This adds the
plugins property, which lets you specify an array of Babel plugins to be used. To use the
babel-plugin-lodash plugin, include
lodash in the plugins array in your config for the Babel loader.
In other words, add the following line to options in
Next, upload lodash-stats-babel-dev.json to the Webpack visualizer, which displays the components of Lodash required when calling the
You can see that the size has been reduced to 124.4 KiB.
You can take this one step further by using the
webpack-lodash-plugin. Whereas the Babel plugin simply ensures that only the required modules are included, the Wepback plugin replaces the actual features of certain modules with smaller alternatives, reducing the size even further.
Install it by running the following command:
Then, add it as a plugin to your Webpack configuration. The whole webpack.config.js file should now look like this:
The size has been further reduced to 49.6 KiB.
The table below illustrates the changes in bundle size between importing Lodash in its entirety and trimming it down with the Babel and Webpack plugins.
Size in KiB
Lodash and babel plugin-lodash
Lodash, babel plugin-lodash and lodash-webpack-plugin
Webpack production mode
Our final technique is to use Webpack’s production mode. In addition to other optimizations meant for a production environment, this mode reduces bundle sizes by minifying the code.
To use production mode, ensure your
NODE_ENV environment variable is set to
production or set the
--mode option flag to
production when initializing Webpack from the command line.
To see the difference this makes, compare your existing, reduced bundles for Moment, Day.js, and Lodash against the same bundles created in production mode:
Development Size in KiB
Production Size in KiB
Moment.js en-gb locale only
Lodash and babel-plugin-lodash
Lodash, babel-plugin-lodash and lodash-webpack-plugin
Production mode often makes the largest difference in Webpack bundle sizing.
It’s good practice to look at ways to reduce your bundle size when developing applications and then ensure your bundle is generated in production mode before being deployed. This will happen by default if you have the
NODE_ENV environment variable set up correctly across your environments.
It’s important to stay mindful of module size to avoid serving large bundles, which slow your app’s load times. You saw that by applying techniques to trim large modules, we can generate bundles almost 90% smaller in some scenarios.