JavaScript Tree Shaking: A Complete Tutorial
Tree shaking eliminates unused code from your JavaScript bundles. Learn how bundlers detect dead code, why ES modules are required, and how to configure sideEffects for smaller production builds.
JavaScript tree shaking is the process of removing unused code from your final production bundle. The name comes from visualizing your dependency graph as a tree: you shake it, and the dead leaves (unused exports) fall off, leaving only the live branches in the output.
Here is the core idea in one small example. You have a utility module with three functions, but your app only imports one of them. The source module exports add, subtract, and multiply:
// 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; }The application code only imports the add function from the module. The other two exports are never referenced anywhere in the project:
// app.js
import { add } from "./math.js";
console.log(add(2, 3));Without tree shaking, the entire math.js module ends up in the production bundle -- all three functions, even though only add is used. With tree shaking, the bundler analyzes the imports, determines that subtract and multiply are never referenced, and removes them from the output. The production bundle contains only the add function.
The process is entirely static. The bundler never executes your code. It reads the source, traces every import to its source, and determines which exports are actually consumed.
Why ES Modules Are Required
Tree shaking only works with ES module syntax. The reason is fundamental: ES module imports are static declarations that can be analyzed at build time without running the code.
// Static import - analyzable at build time
import { add } from "./math.js";CommonJS uses dynamic require() calls that depend on runtime conditions, making static analysis impossible for the bundler at build time:
// Dynamic require - cannot be analyzed at build time
const moduleName = condition ? "./math.js" : "./other.js";
const { add } = require(moduleName);The bundler cannot know at build time which module will load, so it cannot safely remove any exports. This is why migrating from CommonJS to ES modules is essential for effective tree shaking.
Configuring sideEffects in package.json
The most important configuration for tree shaking is the sideEffects field in package.json. This flag tells bundlers whether your modules have side effects -- code that runs when the module is imported, even if no exports are used.
Set it to false to declare that all your modules are pure and safe to remove if their exports are unused:
{
"name": "my-library",
"sideEffects": false
}With this setting, if your app imports a library but never calls any of its exports, the entire library is removed from the production bundle.
Some files do have side effects and must be excluded. Global CSS imports and polyfills are the most common examples:
{
"sideEffects": [
"*.css",
"./src/polyfills.js"
]
}Without this exclusion, the bundler might remove your CSS import because no JavaScript references it, breaking your page styles. Be explicit about which files have side effects.
Verifying Tree Shaking Works
The best way to confirm tree shaking is to inspect the production output. Build your app in production mode and check the bundle contents using a bundle analysis tool. For Webpack, generate a stats file and visualize it:
npx webpack --mode production --profile --json > stats.json
npx webpack-bundle-analyzer stats.jsonFor Vite and Rollup, the visualizer plugin shows the same information. Look for any unused exports appearing in the final bundle and trace them back to their source to understand why they were not shaken.
A quick manual verification: temporarily remove an import from your entry file and rebuild. The production bundle should shrink by roughly the size of the removed module and its dependencies.
Common Pitfall: Side Effects in Module Code
A module has side effects if it does anything beyond exporting values -- like modifying globals, patching prototypes, or running initialization logic:
// This module has side effects - it modifies window on import
window.__VERSION__ = "2.0.0";
export function init() { /* ... */ }Even if no file imports init, the window assignment runs the moment this module is imported anywhere. Marking this module as side-effect-free would cause the bundler to remove it, silently breaking the version check. Always exclude modules with genuine side effects from tree shaking.
Common Pitfall: Re-Exports and Barrel Files
Barrel files that re-export everything can prevent tree shaking if not written carefully. The export * syntax forces the bundler to include every export, because it cannot determine which ones are used:
// Avoid: forces all exports into the bundle
export * from "./module-a.js";
export * from "./module-b.js";Prefer explicit re-exports so the bundler can trace usage precisely. For more on this pattern, see named exports and barrel files.
For the practical step-by-step on removing dead code from an existing project, continue to the dead code removal guide.
Rune AI
Key Insights
- Tree shaking removes unused exports from your final bundle by analyzing the import graph.
- ES module syntax is required because static imports can be analyzed at build time.
- Set sideEffects: false in package.json to tell bundlers all your modules are safe to shake.
- Mark files with side effects (CSS imports, polyfills) explicitly to prevent breakage.
- Verify tree shaking is working by checking production bundle contents with analysis tools.
Frequently Asked Questions
Why is it called tree shaking?
Does tree shaking work with CommonJS modules?
Do I need to configure anything to enable tree shaking?
Conclusion
Tree shaking is one of the most effective code optimization techniques available to JavaScript developers. By using ES module syntax and properly configuring sideEffects, you let your bundler automatically strip unused code. The result is smaller bundles, faster page loads, and happier users. Pair tree shaking with code splitting for maximum impact on production performance.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.