Frontend Development 14 min read

Understanding Tree Shaking in Webpack: Theory, Implementation, and Best Practices

This article explains the concept of Tree Shaking as a dead‑code elimination technique based on ES Modules, details how to enable it in Webpack, describes the underlying implementation steps, and provides practical tips for writing tree‑shakable code while avoiding common pitfalls.

ByteFE
ByteFE
ByteFE
Understanding Tree Shaking in Webpack: Theory, Implementation, and Best Practices

1. What is Tree Shaking

Tree Shaking is a dead‑code elimination technique built on the ES Module (ESM) specification that statically analyzes import/export relationships to remove unused exported values, thereby optimizing bundle size.

The technique was first implemented by Rich Harris in Rollup and has been supported by Webpack since version 2.0.

1.1 Enabling Tree Shaking in Webpack

To activate Tree Shaking in Webpack, three conditions must be met:

Write modules using the ESM syntax.

Set optimization.usedExports to true to enable export marking.

Enable code optimization, typically by setting mode = "production" , optimization.minimize = true , and providing an optimization.minimizer array.

Example configuration:

module.exports = {
  entry: "./src/index",
  mode: "production",
  devtool: false,
  optimization: {
    usedExports: true,
  },
};

1.2 Theoretical Basis

Older module systems like CommonJS, AMD, and CMD allow dynamic import/export statements that are hard to analyze statically. In contrast, ESM requires all import/export statements to appear at the top level with string literals, making the dependency graph static and enabling reliable Tree Shaking.

1.3 Example

Given the following modules:

// index.js
import { bar } from "./bar";
console.log(bar);

// bar.js
export const bar = "bar";
export const foo = "foo";

Only bar is used by another module, so after Tree Shaking the foo export is removed as dead code.

2. Implementation Details

Webpack implements Tree Shaking in two main phases: marking unused exports and then removing the dead code with a minifier such as Terser.

2.1 Collecting Module Exports

During the Make phase, Webpack converts each ESM export into a HarmonyExportSpecifierDependency (named exports) or HarmonyExportExpressionDependency (default export) and records them in the module’s dependencies list.

2.2 Marking Export Usage

In the Seal phase, the FlagDependencyUsagePlugin traverses the ModuleGraph starting from the entry point, checks each export’s exportInfo via compilation.getDependencyReferencedExports , and marks used exports with exportInfo.setUsedConditionally .

2.3 Generating Code

When generating the final bundle, the HarmonyExportXXXDependency.Template.apply method reads the recorded exportsInfo and emits export statements only for the used values. Unused exports are omitted from the generated __webpack_require__.d calls.

2.4 Removing Dead Code

After marking, the unused export variables become dead code inside __webpack_exports__ . A subsequent minification step (Terser, UglifyJS, etc.) removes this dead code, completing the Tree Shaking process.

2.5 Summary of Steps

Collect exports with FlagDependencyExportsPlugin and store them in exportsInfo .

Determine usage with FlagDependencyUsagePlugin and record results in exportInfo._usedInRuntime .

Generate export statements based on usage via HarmonyExportXXXDependency.Template.apply .

Run a dead‑code elimination (DCE) tool to drop unused code.

3. Best Practices

3.1 Avoid Meaningless Assignments

Unnecessary assignments can create side effects that prevent Tree Shaking from removing unused exports. Ensure that imported values are actually used and avoid assigning them to variables that are never read.

3.2 Mark Pure Function Calls

Prefix side‑effect‑free function calls with /*#__PURE__*/ so that Webpack knows they can be safely removed if unused.

3.3 Do Not Transpile ESM Imports/Exports with Babel

Transpiling ESM to CommonJS (e.g., modules: "commonjs" ) breaks static analysis. Set modules: false in babel-preset-env to keep ESM syntax intact.

3.4 Export Granularity

Export individual bindings rather than a large default object to allow fine‑grained Tree Shaking. For example, use export { bar, foo } instead of export default { bar, foo } .

3.5 Use Tree‑Shakable Packages

Prefer packages that provide ESM builds, such as lodash-es instead of lodash , or use plugins like babel-plugin-lodash to achieve similar results.

Note: The article concludes with a recruitment notice for the ByteDance front‑end team, which is unrelated to the technical content.
frontend developmentWebpackESMtree shakingDead Code Elimination
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.