How to Make Your React Component Library Tree Shakeable
These days, it is pretty common for companies to build their own reusable components that can be used across several apps. In this post, we’ll learn how to configure a reusable component library so that it can be tree-shaken by the apps that use it.
What is tree shaking and why?
Tree shaking is a process that bundlers like webpack and rollup go through to remove unused code.
Tree shaking means that only components from our library used in the app are included in the app bundle. If not tree shaken, all the components will be included even if they are not used. So, tree shaking reduces the size of the app bundle and increases the app’s performance because less time is spent downloading, decompressing, parsing, and executing it.
It is important to note that it is the consuming app doing the tree shaking and not the component library. Therefore, we need to ensure our component library’s structure, format, and settings will allow tree shaking.
Include native module format
We are using rollup to bundle the component library. If we include a bundle for ESM format, the library will have a much better chance of being tree shaken than the CommonJS (CJS) format or other formats. This is because the unused exports can be detected easier because they are static.
Here’s how to configure rollup to include an ESM bundle:
export default {
...,
output: [
...,
{
...,
format: 'esm', ...,
},
],
...
Inform app bundlers that there are no side effects
There are cases when bundlers are unsure whether code is unused. For example, importing a component’s styles could mean that the button styles impact other parts of the app.
Import "./button.css"
The sideEffects
field in package.json
informs the bunder that all files in the component library are pure and free from side effects:
{
"name": "my-react-library",
"version": "0.0.1",
"main": "dist/index.cjs.js",
"sideEffects": false, ...
}
Preserve module structure
Rollup will bundle into a single file by default. A single file means that the bunder can’t use the sideEffects
flag to skip modules.
The module structure can be preserved using the preserveModules
field in the rollup config:
export default {
...,
output: [
...,
{
format: 'esm',
sourcemap: true,
dir: 'dist', preserveModules: true, },
],
...
Note that the dir
field needs to be supplied rather than file
because many files will now be bundled rather than one. The value of the dir
field is the folder where the bundled files are placed.
Inform app bundlers that a native module bundle is available
Specify the entry point to the module bundle using the module
field in package.json
:
{
"name": "my-react-library",
"version": "0.0.1",
"main": "dist/index.cjs.js",
"module": "dist/src/index.js", "sideEffects": false,
}
The app bundler will then use the ESM bundle rather than the bundle specified by the main
field.
Transpile the components with the latest babel preset
Babel can mark parts of React code as pure with a /*#__PURE__*/
marker as shown below:
function Button(_a) {
var children = _a.children;
return /*#__PURE__*/React__default.createElement("button", null, children);
}
These markers help bundlers determine that the component is pure and hence tree shakeable.
So, make sure the component library build includes babel with the latest preset. For example, here is babel in the rollup config:
...
import { babel } from "@rollup/plugin-babel";
export default {
...,
plugins: [
...,
babel({ babelHelpers: "runtime", exclude: /node_modules/, extensions: [".js", ".ts", ".tsx"], }), ...
]
};
Here’s the babel config:
{
"presets": [
["@babel/env", { "modules": false }],
"@babel/typescript",
[
"@babel/preset-react",
{
"runtime": "automatic"
}
]
],
"plugins": [
["@babel/plugin-transform-runtime", { "useESModules": true }]
]
}
The { "modules": false }
setting for "@babel/env"
is important to preserve the ESM format.
Ensure third party libraries are tree shakeable
When building a component library, if leveraging 3rd party libraries, ensure they are tree shakeable. For example, you can carry out the following steps to quickly check whether a library is tree shakeable:
- Create an app using Create React App.
- Build the app and note down the file sizes.
- Install the 3rd party library.
- Reference a small part of the 3rd party library in the app.
- Build the app and compare the bundle file sizes. Hopefully, the bundle size hasn’t increased much. If the bundle size has increased significantly, the 3rd party library may not be tree shaking.
- Reference a little more of the 3rd party library in the app.
- Build the app and compare the bundle file sizes. If the bundle size hasn’t changed, the 3rd party library may not be tree shaking.
Wrap up
In summary, to ensure a React component library is tree shakeable:
- Include ESM format and preserve that format.
- Use the
module
package.json
field to let bundlers know the components are in ESM format. - Use the
sideEffects
package.json
flag to let bundlers know the components are pure. - Use the latest babel preset to ensure the latest tree shaking markers are set.
- Test that third-party libraries are tree shakeable before you leverage them.
Did you find this post useful?
Let me know by sharing it on Twitter.If you to learn about using TypeScript with React, you may find my course useful: