Making lodash tree-shakable
Pedro Figueiredo
Posted on November 19, 2023
TL;DR
1- Replace `lodash` with `lodash-es`
2- Ditch default imports
3- Prefer namespace imports - import * as _ from "lodash-es"
4- Setup a linter rule to not allow this happen again
5- See great bundle size improvements π
Lodash
Lodash is a JS utility library that delivers small functions capable of easing the way we work with arrays, numbers, objects, strings, etc.
isEmpty
lowerCase
concat
defer
- ...
And although it's about 11 years old, lodash
is still today, the most "depended-upon" package, according to the npm registry. So it's very likely that you are using it directly or indirectly through one of your dependencies.
CommonJS
When lodash
was first introduced in 2011, CommonJS
was the common standard to write JS modules, however, that standard was mainly intended to be used in server-side applications and never to reach the browser.
But in 2023, the lodash
library is still being widely used in web applications, and of course, it's still written in the CommonJS
format.
Tree Shaking in CommonJS
Tree shaking is a process in modern JavaScript bundlers that eliminates unused code during the build process, resulting in smaller and more optimized bundles.
Because CommonJS
was originally designed with server-side applications in mind, it didn't prioritize a crucial aspect for browser-based environments: the ability to be tree shaken.
This happens because CommonJS allows for highly dynamic imports/exports, that bundlers can't simply pick up and do a fully static analysis on, to understand if certain code should be kept or eliminated in the final bundle.
For more in-depth information on how CommonJS
works when it comes to tree shaking, make sure to take a look at this great article.
A Tree-Shakable Alternative
In today's web development, ECMAScript
is the go-to standard for writing JS modules and it's being widely adapted not only on the browser but also on node
environments.
This standard is much more mature and offers much better support for static analysis and tree shaking due to its more declarative and static nature.
Tree-shaking lodash
Now that we have gone through why tree shaking doesn't play well with
lodash
, let's dive into what we can do we make it tree-shakable.
1- Replacing lodash
with lodash-es
Due to the huge amount of benefits of using ESM (ECMAScript Modules), the lodash-es
lib was created and it's now the recommended package to be installed if you want to continue to use lodash
, it is still the same library, but exported as ES modules.
2- Stop using the default import
Another very common practice that might completely ruin tree shaking, is doing a default import when using lodash
or any other lib that exports everything from the default exported variable.
import _ from "lodash-es";
// `_` contains every single lodash's function
_.add(1,2);
_.isEmpy([]);
_.lowerCase("HEY");
In the example above, although we are only using 3 functions, we are actually importing the whole lib, because the default
export is an object that contains all the lib's utilities.
3- Use namespace imports
Namespace imports are a way to import every single exported member from a module individually.
import * as _ from "lodash-es";
// `_` contains every single lodash's function
_.add(1,2);
_.isEmpy([]);
_.lowerCase("HEY");
The difference between this and the default
import, is that we are importing add
, isEmpty
and lowerCase
individually and as such, every other import that isn't used will be removed at build time.
Of course, you could also import each utility individually, but because of how similar these function names are to the ones provided by native JS, I highly recommend that you keep the _
prefix when using lodash
.
import { some } from "lodash-es";
// makes it harder to understand which one is each β
some([null, 0, 'yes', false], Boolean);
[null, 0, 'yes', false].some(Boolean);
// ------------------------------------------------
import * as _ from "lodash-es";
// makes it very easy to understand which one is each β
_.some([null, 0, 'yes', false], Boolean);
[null, 0, 'yes', false].some(Boolean);
Bonus tip
To safeguard against unintentional slip-ups, such as default imports that can bloat your app's bundle size, I strongly recommend incorporating an ESLint rule to NOT allow using the default
import of lodash-es
.
// eslintrc.js
module.exports = {
rules: {
"no-restricted-syntax": [
"error",
{
message:
"Do not import default from lodash-es. Use a namespace import (* as) instead.",
selector:
'ImportDeclaration[source.value="lodash-es"] ImportDefaultSpecifier',
},
],
},
};
Summary
1- Avoid using CommonJS libraries in browser-based applications
2- Be mindful when using default imports and prefer namespace imports
3- Avoid these common pitfalls with linter rules
Conclusion
This approach is not exclusive to lodash
, and it can actually be applied to a variety of packages, particularly those that are still being exported as CommonJS
.
Make sure to follow me on twitter if you want to read about TypeScript best practices or just web development in general!
Posted on November 19, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024
October 16, 2024