Tree shaking
Tree shaking is the process of removing unused code from the final JavaScript bundle. The name literally means shaking a tree: picture your project's code as a tree whose dead branches (functions, modules, imports) simply fall off during the build. Less code means a faster download and better performance.
Theory
TL;DR
- If a function, variable or import is used nowhere, the bundler (Webpack, Rollup, Vite) deletes that code from the output file.
- It rests on ES Modules:
import/exportare static, so they can be analysed at build time. - CommonJS (
require) does not support tree shaking. - Side effects block the removal of a module until
package.jsondeclares"sideEffects": false. - Rollup, Vite and esbuild do it out of the box; Webpack turns it on in production mode.
Quick example
// utils.js
export function add(a, b) { return a + b; }
export function sub(a, b) { return a - b; }
export function mul(a, b) { return a * b; }
export function div(a, b) { return a / b; }
// main.js
import { add } from './utils.js';
console.log(add(2, 3));Without tree shaking the bundler puts every function (add, sub, mul, div) into bundle.js, even though only add() is actually used. With tree shaking enabled (Webpack production mode, Rollup or Vite) only add() survives in the final bundle. The result is a smaller bundle, a faster download, and faster parsing and execution too.
How it works technically
Tree shaking rests on three things:
- ES Modules (ESM), that is
import/export: unlikerequire, ESM imports are static and can be analysed at build time; - static dependency analysis: the bundler knows exactly which exports are really used;
- dead code elimination, the optimization that strips what is left unreferenced.
// the import is static, so the bundler sees that only add is used
import { add } from './utils.js';CommonJS, on the other hand, does not support tree shaking:
const utils = require('./utils'); // the bundler cannot know for sure what is usedWhen tree shaking will not work
Tree shaking does not work if:
- you use CommonJS (
require) instead of ES Modules (import/export); - the code has side effects, for example it runs something on import;
- the library did not declare
"sideEffects": falsein itspackage.json; - you write dynamic imports or reach properties through variables.
An example with a side effect:
// side.js
console.log("This code runs on import!");
export const x = 42;Even if x is never used, the whole file stays, because importing it executes console.log(), which is a side effect.
Configuration in Webpack, Rollup and Vite
Webpack. Tree shaking is enabled automatically in production mode when package.json contains:
{
"sideEffects": false
}Instead of false you can give a list of the files that genuinely do have side effects.
Rollup. Tree shaking is on by default. Historically Rollup was the first bundler to ship it.
Vite and esbuild. It works out of the box, because both are built around ESM, and Vite additionally uses Rollup for the production build.
The size of the win
Before the optimization the bundle weighed 120 KB; after tree shaking, 42 KB.
| Item | Description |
|---|---|
| What it does | Removes unused code from the final bundle |
| Based on | ESM (import / export) and static analysis |
| Works in | Webpack, Rollup, Vite, Parcel |
| Does not work with | require(), side effects |
| Goal | Reduce size and speed up loading |
Common mistakes
- Expecting tree shaking from a CommonJS dependency. If a library ships only a CJS build, the bundler cannot cut anything; look for a package with a
moduleor an ESMexportsfield. - Importing a whole package for one function.
import _ from 'lodash'pulls in everything;import debounce from 'lodash-es/debounce'gives the bundler a chance. - Setting
"sideEffects": falseblindly. If the package has files with real side effects, such as global polyfills or CSS imports, they silently vanish from the bundle. - Checking the result in dev mode. In Webpack tree shaking only runs in the production build, so measure the size there.
- Reaching exports through a variable.
utils[name]()makes static analysis impossible, and the bundler keeps everything. - Confusing tree shaking with minification. A minifier shortens the code that is there; tree shaking stops unused code from entering the bundle at all.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.