Tree Shaking in JavaScript
January 4, 2026
Tree Shaking in JavaScript
Overview
Tree shaking is a process used in modern JavaScript bundlers to eliminate dead code before adding it to the final bundle. It analyzes the dependency graph of your code, identifies unused exports, and removes them to reduce the final bundle size.
How It Works
- Analyzes the dependency graph: Traces all imports and exports
- Identifies unused exports: Finds code that's imported but never used
- Removes dead code: Eliminates unused code from the final bundle
- Reduces bundle size: Results in smaller, more efficient bundles
Basic Example
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;
// main.js
import { add } from "./math.js";
console.log(add(2, 3));
In this example, the subtract, multiply, and divide functions would be removed during tree shaking as they're not used.
Requirements for Tree Shaking
1. ES6 Module Syntax
Tree shaking requires ES6 module syntax (import/export):
// ✅ Good: ES6 modules (tree-shakeable)
export const add = (a, b) => a + b;
import { add } from "./math.js";
// ❌ Bad: CommonJS (not tree-shakeable)
module.exports = { add };
const { add } = require("./math.js");
2. Static Analysis
Bundlers need to statically analyze your code:
// ✅ Good: Static import
import { add } from "./math.js";
// ❌ Bad: Dynamic import (harder to tree-shake)
const module = await import("./math.js");
3. Side-Effect Free Modules
Modules should be free of side effects:
// ✅ Good: Pure module
export const add = (a, b) => a + b;
// ❌ Bad: Side effects prevent tree shaking
export const add = (a, b) => a + b;
window.myGlobal = "value"; // Side effect!
Benefits
1. Smaller Bundle Size
// Before tree shaking: 50KB
// After tree shaking: 15KB
// Savings: 70% reduction
2. Improved Load Times
Smaller bundles mean faster downloads and parsing:
Bundle Size: 200KB → 80KB
Load Time: 2.5s → 1.0s
3. Better Performance
Less code to parse and execute:
- Faster initial load
- Reduced memory usage
- Better runtime performance
Common Bundlers
Webpack
// webpack.config.js
module.exports = {
mode: "production", // Enables tree shaking
optimization: {
usedExports: true,
sideEffects: false
}
};
Rollup
// rollup.config.js
export default {
input: "src/main.js",
output: {
file: "bundle.js",
format: "es"
},
treeshake: {
moduleSideEffects: false
}
};
Parcel
Parcel automatically performs tree shaking in production mode.
Vite
Vite uses Rollup for production builds, which includes tree shaking by default.
Best Practices
1. Use ES6 Module Syntax
// ✅ Good
export const utility = () => {};
import { utility } from "./utils.js";
// ❌ Bad
module.exports = { utility };
const { utility } = require("./utils.js");
2. Avoid Side Effects
// ✅ Good: Pure module
export const add = (a, b) => a + b;
// ❌ Bad: Side effects
export const add = (a, b) => a + b;
console.log("Module loaded"); // Side effect
3. Mark Side-Effect Modules
If a module has side effects, mark it in package.json:
{
"name": "my-package",
"sideEffects": ["./src/polyfills.js", "*.css"]
}
4. Use Named Exports
// ✅ Good: Named exports (better for tree shaking)
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// ⚠️ Less optimal: Default export
export default { add, subtract };
5. Avoid Barrel Exports When Possible
// ⚠️ Can prevent tree shaking
// utils/index.js
export * from "./math.js";
export * from "./string.js";
// ✅ Better: Direct imports
import { add } from "./utils/math.js";
Real-World Example
Before Tree Shaking
// utils.js
export const formatDate = (date) => { /* ... */ };
export const formatCurrency = (amount) => { /* ... */ };
export const formatPhone = (phone) => { /* ... */ };
export const validateEmail = (email) => { /* ... */ };
// main.js
import { formatDate } from "./utils.js";
// All functions included in bundle: 15KB
After Tree Shaking
// Only formatDate included: 3KB
// Savings: 12KB (80% reduction)
Library Development
1. Package.json Configuration
{
"name": "my-library",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"sideEffects": false
}
2. ESM Build
// Provide ES module build for better tree shaking
// dist/index.esm.js
export { add } from "./math.js";
export { format } from "./string.js";
3. Avoid Default Exports for Utilities
// ✅ Good: Named exports
export const add = (a, b) => a + b;
// ❌ Bad: Default export object
export default {
add: (a, b) => a + b
};
Debugging Tree Shaking
1. Webpack Bundle Analyzer
npm install --save-dev webpack-bundle-analyzer
// webpack.config.js
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
module.exports = {
plugins: [new BundleAnalyzerPlugin()]
};
2. Check Bundle Contents
# Analyze bundle
npm run build -- --analyze
# Check what's included
grep -r "unusedFunction" dist/
3. Verify Tree Shaking
// Add unused import
import { unusedFunction } from "./utils.js";
// Build and check if it's removed
// If it's still in bundle, tree shaking isn't working
Common Issues
1. Babel Transpilation
Babel might convert ES6 modules to CommonJS:
// .babelrc
{
"presets": [
["@babel/preset-env", {
"modules": false // Keep ES6 modules
}]
]
}
2. TypeScript Configuration
// tsconfig.json
{
"compilerOptions": {
"module": "ESNext", // Use ES modules
"moduleResolution": "node"
}
}
3. Third-Party Libraries
Some libraries don't support tree shaking:
// ❌ Bad: Imports entire library
import _ from "lodash";
_.add(1, 2);
// ✅ Good: Import specific functions
import add from "lodash/add";
add(1, 2);
Performance Metrics
Example Bundle Sizes
Without Tree Shaking:
- main.js: 250KB
- vendor.js: 180KB
- Total: 430KB
With Tree Shaking:
- main.js: 85KB
- vendor.js: 120KB
- Total: 205KB
Reduction: 52% smaller
Summary
Tree shaking is an essential optimization technique that eliminates unused code from bundles. It requires ES6 module syntax, static analysis capabilities, and side-effect-free modules. Proper use of tree shaking can significantly reduce bundle sizes, improving load times and performance. Modern bundlers like Webpack, Rollup, and Vite support tree shaking out of the box when configured correctly.