Unused library code is often the largest part of a bundle.
Tree shaking removes exports nobody imports. In webpack it is a cooperation of several steps, all enabled in production mode:
optimization.usedExports: webpack marks which exports of each module are used.- The minifier (Terser) then deletes the unused, now-unreferenced code.
optimization.sideEffects: if a package declares"sideEffects": falsein itspackage.json, webpack can skip entire modules whose exports are unused, even if they were imported through a re-exporting index file.optimization.concatenateModules(scope hoisting) merges modules into one scope, making the output smaller and faster.
What breaks it:
- CommonJS (
require,module.exports): webpack 5 can analyze some patterns, but ESM is far more reliable. Make sure Babel does not convert ESM to CommonJS (modules: falsein preset-env; the default with babel-loader is to keep ESM). - Side effects at module top level (polyfills, global CSS imports, registering things). If you mark a package
"sideEffects": false, list files that do have side effects:"sideEffects": ["*.css", "./src/polyfills.js"], or your CSS disappears. - Importing a whole namespace and using it dynamically (
lib[name]).
Mark pure function calls with /*#__PURE__*/ so minifiers can drop them when unused. Verify with the bundle analyzer, or by setting usedExports with mode: "development" and looking for /* unused harmony export */ comments.
Example
// src/math.js
export const add = (a, b) => a + b;
export const heavyStats = (xs) => xs.reduce((s, x) => s + x * x, 0); // never imported
// src/index.js
import { add } from "./math.js";
console.log(add(2, 3));
// package.json of the app or a library
// "sideEffects": ["*.css", "./src/polyfills.js"]
// $ npx webpack --mode development --optimization-used-exports
// dist/main.js contains: /* unused harmony export heavyStats */
// $ npx webpack --mode production
// heavyStats is gone from dist/main.jsRemember
- Tree shaking needs ES modules
- usedExports marks, the minifier removes
sideEffects: falselets webpack skip whole modules- List CSS and polyfills in sideEffects so they survive
Watch out for
- Setting
"sideEffects": falsein an app that imports global CSS, silently dropping the styles.