4 Ways to Shrink Your Javascript Bundle by 50%

Riley Shenk
5 min readMay 17, 2021

The best way to get started is having a visual model to show you what packages are contributing to your bundle size. Here is a great guide for getting started with webpack-bundle-analyzer.

When I ran the Webpack Bundle Analyze I saw 5 libraries that were taking a disproportionate amount of space given that we only needed the libraries in a few pages of the app.

visual representation of javascript bundles

The total bundle size is 8.31MB.

4 Strategies to shrink the JS bundle:

  1. Only include languages you require from Moment.js and not the 100+ language it ships with.
  2. Directly import the functions you need from Lodash and not the entire library.
  3. Use Webpack dynamic imports to pull big libraries out of the main bundle so they only get sent on the pages that need them.
  4. Dynamically import large 3rd party React components.

1. Ignore moment.js locales

It is likely that you only need 1 or 2 languages from the library and not the 100+ that it ships with. In my case I only needed English which made things simple because I can ignore all moment plugins in my webpack config.

I have drawn a red box around the entire moment library shown by Webpack Bundle Analyzer. The languages boxed in blue are what takes up most of the space.

Before

After

This IgnorePlugin will allow us to avoid all moment local plugins.

Here is the output from Webpack Bundle Analyze after this change with all the languages taken out:

Read more about this process in the webpack documentation https://webpack.js.org/plugins/ignore-plugin/#example-of-ignoring-moment-locales.

2. Directly import lodash functions you need.

Before es6, lodash became a staple in every repo because even basic functions like forEach and map weren’t native. After es6 most if not the entire library is not needed anymore. If you do want to continue using the library, be intentional about only importing the functions you need. Here is one way to only import the functions you need:

Importing all of lodash

Instead, do the following to import just the functions you need:

Importing specific lodash function

Before lodash optomization:

Entire lodash library outlined in red

After importing only the function the repository uses:

Lodash size after individual function imports

The before and after on this isn’t as dramatic here because we are using so many lodash functions. Over time this will shrink as we convert more of the logic to vanilla es6.

Here is more information on other options for reducing the lodash library https://www.blazemeter.com/blog/the-correct-way-to-import-lodash-libraries-a-benchmark.

3. Dynamically import large libraries

before braintree-web-drop-in dynamic import

To do a dynamic import with webpack you just need to pass the library name into import() and then use the promise it returns.

After braintree-web-drop-in dynamic import

Here is another example with a password strength estimator zxcvbn:

Before

Before zxcvbn dynamic import

After

After zxcvbn dynamic import

That import will now only happen when evaluatePassword() gets called. That function only gets called on the signup and forgot password pages.

4. Dynamically import large components

Using a dynamic import will split the library off into its own bundle that will only be downloaded when the function is actually used. Chart.js is a very large library, but we only have charts on 3 or 4 pages.

Component before chart.js dynamic import

There are two different types of imports here that need to be handled differently.

For a React component import, you can put that in a useEffect and then put the code that gets imported into a React.createElement so that you can return a component.

Chart.js component dynamically imported

For a default library that needs to get imported and used in a React component it is the same idea where you put it in a useEffect and show loading text while you wait for it. This is an example of the two dynamic imports in one file:

Two types of dynamic imports in a React component

The following is the output from Webpack Bundle Analyze after all the dynamic imports have been done. Notice that all the large libraries are off to the right in their own bundles. Those bundles will only be downloaded on the pages that they are needed on.

Webpack bundle analyze after all optimizations

Please reach out to me on twitter @lawn_champ if this helped you out or if you have any questions!

--

--

Riley Shenk

Software Engineer building web apps with Rails and React