Skip to content
⚠️ This article was written in 2018. Some content may be outdated.

Webpack Tree Shaking: Principles and Practice

"Tree shaking" comes from Rollup — the idea of shaking unused code off like dead leaves. Webpack has supported it since version 2, but it requires correct configuration to actually work.

How It Works ​

Tree shaking relies on the static structure of ES modules (imports and exports are determined at compile time and cannot change dynamically):

javascript
// ES module: static — analyzable at compile time
import { add } from "./math"; // only `add` is used
export function add(a, b) {
  return a + b;
}
export function multiply(a, b) {
  return a * b;
} // never imported — can be removed

// CommonJS: dynamic — cannot be analyzed
const math = require("./math"); // the entire object is imported; which parts are used is unknown
const method = "add";
math[method](); // dynamic access — tree shaking can't determine which methods will be called

Prerequisites ​

1. Source code uses ES modules (import/export)
2. webpack mode is "production"
3. Babel must not convert ES modules to CommonJS
4. package.json sideEffects field is configured correctly

Configure Babel Not to Convert to CommonJS ​

javascript
// .babelrc or babel.config.js
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false  // Critical! Do not convert to CommonJS
    }]
  ]
}

The sideEffects Field ​

Webpack needs to know which files have side effects (e.g., CSS, polyfills) and must not be tree-shaken away:

json
// package.json
{
  // All files have no side effects — safe to tree-shake
  "sideEffects": false,

  // Or list files that do have side effects
  "sideEffects": ["*.css", "*.scss", "./src/polyfills.js"]
}

Verifying Tree Shaking Works ​

javascript
// math.js
export function add(a, b) {
  return a + b;
}
export function subtract(a, b) {
  return a - b;
}
export function multiply(a, b) {
  return a * b;
} // not used

// main.js
import { add } from "./math";
console.log(add(1, 2));

After a production build, multiply should not be in the bundle. Verify with webpack-bundle-analyzer.

Common Issues ​

Issue 1: Third-party library has no ES module version

javascript
// lodash is CommonJS — tree shaking doesn't work
import { debounce } from "lodash"; // bundles the entire lodash!

// Fix: use lodash-es (ES module version)
import { debounce } from "lodash-es"; // ✅ only bundles debounce

// Or import by path
import debounce from "lodash/debounce"; // ✅ also works

Issue 2: Side effects in classes

javascript
// Class methods generally can't be tree-shaken (they may have side effects)
class Utils {
  static add(a, b) {
    return a + b;
  }
  static multiply(a, b) {
    return a * b;
  } // may still be included even if unused
}

// Export as functions instead — better tree-shaking
export function add(a, b) {
  return a + b;
}

Summary ​

  • Tree shaking requires ES modules — Babel must not transform them to CommonJS
  • Set sideEffects: false in package.json (or list files that have side effects)
  • Use webpack-bundle-analyzer to verify the result
  • Switch lodash to lodash-es for better tree shaking of utility libraries

MIT Licensed