4 Ways to Shrink Your Javascript Bundle by 50%
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.
- lodash 715KB
- moment.js 672KB
- zxcvbn 827KB
- braintree-web-drop-in 917 KB
- chart.js 432 KB
The total bundle size is 8.31MB.
4 Strategies to shrink the JS bundle:
- Only include languages you require from Moment.js and not the 100+ language it ships with.
- Directly import the functions you need from Lodash and not the entire library.
- Use Webpack dynamic imports to pull big libraries out of the main bundle so they only get sent on the pages that need them.
- 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:
Instead, do the following to import just the functions you need:
Before lodash optomization:
After importing only the function the repository uses:
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
To do a dynamic import with webpack you just need to pass the library name into import() and then use the promise it returns.
Here is another example with a password strength estimator zxcvbn:
Before
After
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.
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.
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:
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.
Please reach out to me on twitter @lawn_champ if this helped you out or if you have any questions!