Decreasing Webpack’s bundle size with conditional imports

Tugay İlik
4 min readJul 14, 2019
Image from https://webpack.js.org/

There are several tools used for bundling JavaScript modules or assets. Most bundlers have tree shaking system which clears up unused modules before compiling the whole code and decreases bundle size accordingly. That is, it removes any code that isn’t actually used by your app.

But sometimes, even if you don’t use the functionality, you see that a library gets included in your output bundle anyway. You may need to load that item only for specific situations and not need it in your main bundle.

In those situations, you may look for dynamic module loading by setting a dynamic module import. But let's check what happens when we try to do that like below:

const DynamicModule = import(`path/to/${dynamicModuleName}.js`)

When you do that, Webpack imports each module inside the path/to/* path into your bundle and when your script evaluated on client side, it picks the required one and executes. Why does Webpack do that? Because it can’t know the value of the dynamicModuleName variable at build time.

Well, this can hurt our overall build. Let’s say you have a feature toggle set. Some of the features can be active or passive according to their status. You do not want that feature’s code when it’s passive.

On that point, webpack’s NormalModuleReplacementPlugin comes in handy. What it does is simply allow you to replace resources which match.

According to our scenario, the basic implementation will take those steps at below:

There should be a .empty.js (you can name it as whatever you want) file for your toggleable modules. That means if you have Module1, there should be also Module1.empty.js file too near the normal module.

Empty modules should be literally empty with only class exports, no methods, no variables, not dependencies. It may only contain some empty functions which is dependently used in another module.

For example, let’s say in ModuleX you import Module1 and use Module1’s C method. In this case, when you load empty form of Module1, the application will throw an error on the client side since it does not have C method right? To fix that, you can simply create an empty method with exact name — which is C in our case — in Module1.

There should be a resolver file for the modules that can be marked as empty.

We should tell the plugin, replace normal modules as empty modules according to their statuses

In the last step in the webpack config, we have to activate our plugin like below

Let’s import modules as normal ones and see the output according to their statuses.

While both modules are marked as active

While both modules are marked as passive;

This technique dramatically decreases the bundle size no matter what. Not only those files, but also the unused modules inside the normal modules will be treeshaked and it may literally surprise you sometimes.

See the example repository for more clear understanding;

I hope you had a chance to give a try to this approach! ✊

--

--