Modern JavaScript applications often rely on dozens of libraries and utilities.
As projects grow, the bundle size can quickly increase - sometimes including code your application never even uses.
This unnecessary code slows down loading times and hurts performance.
Large bundle size is one of the biggest performance problems in modern web applications.
That’s where Tree Shaking comes in.
Tree Shaking is a powerful JavaScript optimization technique used by modern JavaScript bundlers like Webpack, Rollup, Vite and esbuild to automatically remove unused code from your application.
In this article we will explore:
- What Tree Shaking is
- Why it matters for performance
- How it works internally
- Practical examples
- Best practices for using it effectively
What is Tree Shaking?
Definition
Tree Shaking is a build optimization technique that removes unused JavaScript code from the final bundle.
It works by analyzing ES module imports and exports and eliminating functions, variables, or modules that are not actually used.
Simple Explanation
Imagine a file exports 10 functions, but your app only uses 2 of them.
Tree Shaking ensures that only those 2 functions remain in the final production bundle.
The other 8 functions are removed automatically during the build process.
Why Is It Called “Tree Shaking”?
The name comes from a visual analogy.
Imagine your codebase as a tree.
Files → branches
Functions/exports → leaves
Imports → connections
When the bundler analyzes your application, it “shakes the tree” and unused leaves fall off.
Conceptual Diagram
math.js
---------------------------------
add | subtract | multiply | divide
---------------------------------
|
| imported
v
app.js
|
v
Final Production Bundle
|
v
add()
Unused functions:
subtract()
multiply()
divide()
These are removed during the build.
Why Tree Shaking Matters
Without Tree Shaking, your application might ship a lot of unnecessary code to users.
That leads to slower loading times and worse performance.
Benefits
| Benefit | Explanation |
|---|---|
| Smaller bundle size | Less JavaScript downloaded |
| Faster page load | Reduced network transfer |
| Better performance | Browser parses less code |
| Improved user experience | Faster application startup |
| Lower bandwidth usage | Especially helpful for mobile users |
Real-Life Example
Imagine you're moving to a new house.
You open your wardrobe and find:
clothes you wear every day
clothes you haven’t worn for years
broken accessories
old shoes
Instead of packing everything, you only pack what you actually use.
Tree Shaking works the same way.
Your project may contain many functions, but the final bundle only includes the ones your application needs.
Basic Example
Let’s look at a simple example.
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;
}
export function divide(a, b) {
return a / b;
}
app.js
import { add } from "./math.js";
console.log(add(2, 3));
Here the application only uses add().
A tree-shaking-enabled bundler removes:
subtract
multiply
divide
from the final bundle.
How Tree Shaking Works
Tree Shaking typically happens during the build process.
Flow Diagram
Source Code
|
v
Bundler analyzes imports/exports
|
v
Builds dependency graph
|
v
Detects unused exports
|
v
Marks removable code
|
v
Optimizer removes unused code
|
v
Final optimized bundle
Build Optimization Pipeline
Developer Code
|
v
Bundler builds dependency graph
|
v
Unused exports detected
|
v
Tree Shaking removes unused code
|
v
Minifier compresses remaining code
|
v
Optimized production bundle
Dependency Graph Visualization
When bundlers analyze code, they create something called a dependency graph.
Example project:
src/
├── main.js
├── utils.js
└── api.js
Dependency graph:
main.js
/ \
v v
utils.js api.js
If utils.js exports multiple functions but main.js uses only one, Tree Shaking removes the rest.
Tree Shaking Works Best With ES Modules
Tree Shaking depends on static analysis, which is easier with ES modules.
ES Module Syntax
import { add } from "./math.js";
export function add() {}
Because imports and exports are static, bundlers can analyze them before execution.
Why CommonJS Is Harder to Tree Shake
CommonJS uses dynamic imports.
Example:
const math = require("./math");
console.log(math.add(2, 3));
Because require() can be dynamic, bundlers cannot always determine exactly which functions are used.
Comparison
| Module System | Syntax | Tree Shaking |
|---|---|---|
| ES Modules | import/export | Excellent |
| CommonJS | require/module.exports | Limited |
Real-World Library Example
Let’s look at a simplified example of how Tree Shaking works with a utility library.
Suppose a utility library exports multiple functions:
export function debounce() {}
export function throttle() {}
export function memoize() {}
export function deepClone() {}
export function isEqual() {}
Your app only imports:
import { debounce } from "./library.js";
Without Tree Shaking:
All 5 functions included
With Tree Shaking:
Only debounce() included
Example: Tree Shaking With Lodash
A common real-world example of Tree Shaking involves the Lodash utility library.
Lodash contains many helper functions such as:
- debounce
- throttle
- cloneDeep
- isEqual
- memoize
If you import Lodash like this:
import _ from "lodash";
Your bundle may include the entire library, even if you only use one function.
A better approach is to import only the function you need:
import debounce from "lodash/debounce";
Or using lodash-es, which supports ES modules:
import { debounce } from "lodash-es";
Tree Shaking vs Dead Code Elimination
These terms are related but slightly different.
| Technique | Purpose |
|---|---|
| Tree Shaking | Removes unused exports |
| Dead code elimination | Removes unreachable code |
| Minification | Compresses code |
Tree Shaking focuses on removing unused module exports, while dead code elimination removes code that can never run.
Example of dead code:
if (false) {
console.log("never runs");
}
This code can be removed by optimizers.
Side Effects and Tree Shaking
A side effect happens when code runs immediately when a module is imported.
Example:
console.log("Module loaded");
export function log(message) {
console.log(message);
}
Even if log() isn't used, the console.log runs.
Because of this, bundlers must be careful when removing modules.
Another common example of side effects is importing CSS:
import "./styles.css";
Even though nothing is exported from this file, the import has a side effect - it loads styles into the page.
Because of this, bundlers should not remove this import, even if it appears unused.
Pure Module vs Side Effect Module
Pure module
export function add(a,b){ return a+b }
Safe to remove if unused.
Side-effect module
console.log("loaded")
export function add(){}
May need to remain.
Code Patterns That Prevent Tree Shaking
Bad pattern:
const utils = {
add(a,b){ return a+b },
subtract(a,b){ return a-b }
}
export default utils
Better pattern:
export function add(a,b){ return a+b }
export function subtract(a,b){ return a-b }
Named exports make Tree Shaking easier.
Tree Shaking with Webpack
Webpack supports Tree Shaking in production mode.
Example package.json:
{
"scripts": {
"build": "webpack --mode production"
}
}
Production builds activate optimizations like:
Tree Shaking
minification
dead code removal
Using sideEffects in package.json
Libraries can help bundlers using:
{
"sideEffects": false
}
This tells bundlers that modules do not run code during import, allowing unused files to be safely removed.
Real-Life Application Example
Imagine an e-commerce platform.
Utility functions include:
calculateTax
calculateShipping
generateInvoicePDF
exportSalesReport
formatPrice
The product page only needs:
calculateTax
formatPrice
Tree Shaking ensures the browser does not download the reporting or PDF generation code.
This improves performance significantly.
Tree Shaking in Modern Tools
| Tool | Tree Shaking Support |
|---|---|
| Webpack | Yes |
| Rollup | Excellent |
| Vite | Yes |
| esbuild | Yes |
Most modern build tools support Tree Shaking by default.
How To Enable Tree Shaking
Tree Shaking usually works automatically when using modern bundlers with ES module syntax in production builds.
Using Webpack
Run the production build:
webpack --mode production
Webpack automatically enables:
- Tree Shaking
- Dead code elimination
- Minification
Using Vite
npm run build
Vite uses Rollup under the hood, which performs Tree Shaking automatically.
Using Rollup
rollup -c
Rollup was originally designed around ES modules, making its Tree Shaking very powerful.
Best Practices for Tree Shaking
| Practice | Why |
|---|---|
| Use ES modules | Easier static analysis |
| Prefer named exports | More precise imports |
| Avoid unnecessary side effects | Enables safe removal |
| Import only what you need | Prevents large bundles |
| Use production builds | Activates optimizations |
Example Project
utils.js
export function sum(a,b){ return a+b }
export function multiply(a,b){ return a*b }
export function divide(a,b){ return a/b }
main.js
import { sum } from "./utils"
console.log(sum(4,6))
Production bundle should contain only the sum function.
Final Mental Model
Think of Tree Shaking as:
Tree Shaking keeps only the code your application actually uses and removes everything else during the build process.
Your application becomes:
faster
lighter
more efficient
without changing how you write features.
Common Mistakes That Break Tree Shaking
- Using CommonJS (
require) instead of ES modules - Importing entire libraries instead of specific functions
- Adding side effects in utility modules
- Converting ES modules to CommonJS with Babel before bundling
- Testing only in development mode instead of production builds
Final Thoughts
Tree Shaking is a fundamental optimization technique in modern JavaScript development. As applications grow and dependencies increase, removing unused code becomes critical for performance.
By structuring your code properly and using ES modules, you allow bundlers to eliminate unnecessary code automatically.
This leads to:
smaller bundles
faster applications
better user experiences
And ultimately, cleaner production builds.
Top comments (0)