Suggest an editImprove this articleRefine the answer for “How does "tree shaking" work?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Tree shaking** is the process of analyzing module imports/exports and removing the ones that are not used in the final code. **Key point:** if a function, class, or variable is never called or imported, the bundler simply drops it from the resulting bundle.Shown above the full answer for quick recall.Answer (EN)Image## What is Tree Shaking **Tree shaking** is the process of analyzing module imports/exports and **removing the ones that are not used** in the final code. Put simply: > If a function, class, or variable is **never called / never imported**, the bundler just drops it from the resulting bundle. The name is a metaphor: the tree = the project's dependency graph, shaking = "shaking off" the unused branches. --- ## Example without tree shaking ```javascript // math.js export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } // app.js import { add } from './math.js'; console.log(add(2, 3)); ``` Without tree shaking, the bundle would include **both** `add` **and** `multiply`, even though `multiply` is never used. --- ## Example with tree shaking A modern bundler (Rollup, Webpack 5+, ESBuild, Vite) analyzes: - `multiply` is never imported anywhere, - so it can be safely removed. Only this remains in the resulting bundle: ```javascript function add(a,b){return a+b} console.log(add(2,3)) ``` --- ## Conditions for tree shaking to work Tree shaking works **only with ES Modules (ESM)**, because they are **static**: ```javascript // ESM import { add } from './math.js'; // CommonJS (analysis is impossible at build time) const math = require('./math'); ``` The bundler must see all imports/exports **at compile time**, not at runtime (dynamic require() calls break the optimization). --- ## How the bundler figures out what can be removed Tree shaking is **an analysis of the dependency graph**: 1. It identifies the "roots": the modules actually used in the app (entry points); 2. It builds the import graph; 3. It removes everything that is unreachable from those roots. --- ## "Dead Code Elimination" vs "Tree Shaking" Tree shaking is a logical analysis of **imports/exports** at the module level. Dead code elimination (DCE) is the follow-up **analysis at the expression level** (usually done by Terser/UglifyJS). The typical chain looks like this: ```javascript Webpack / Rollup → mark unused exports ↓ Terser → physically strip the code ``` --- ## Example: side effects Tree shaking **does not remove code with side effects**: anything that can change global state. ```javascript // utils.js console.log('module loaded'); // a side effect! export const a = 1; export const b = 2; ``` Even if you don't import `a` or `b`, the bundler **will not remove** the module if it sees a side effect (a `console.log` call, changing a global variable, and so on). --- ## A hint for the bundler: `package.json → sideEffects` In a package you can explicitly state that the code **has no side effects**, and tree shaking then works more aggressively: ```javascript { "name": "my-lib", "sideEffects": false } ``` or selectively: ```javascript { "sideEffects": [ "*.css", "./polyfills.js" ] } ``` --- ## Common reasons tree shaking doesn't work | Reason | Why | |---|---| | CommonJS is used (`require`) | there's no static dependency analysis | | Dynamic imports (`import * as m`) or `require(variable)` | the bundler can't predict what exactly is imported | | Side effects in modules | they cannot be safely removed | | Plugins or transpilation break ESM | for example, Babel turns `import/export` into `require` | | `mode: 'production'` is not enabled in Webpack | optimizations are off in dev mode | --- ## Example: Webpack ```javascript // webpack.config.js module.exports = { mode: 'production', // enables tree-shaking + minify optimization: { usedExports: true, // marks used exports } }; ``` If you look in a bundle analyzer, you'll see comments like `/* unused harmony export */`: that's tree shaking in action. --- ## Example: Rollup (the ideal case) Rollup was originally built around tree shaking, so it does this better than anyone: ```javascript // rollup.config.js export default { input: 'src/index.js', output: { file: 'dist/bundle.js', format: 'esm' } }; ``` Rollup removes unused exports and even "chains" of functions if they are never called. --- ## Integration with Vite / ESBuild Vite and ESBuild **always** work with ESM and support tree shaking **out of the box**: there's nothing to configure. The main thing: don't use CommonJS and don't have hidden side effects. --- ## Summary | What | Description | |---|---| | **Tree shaking** | Removes unused imports/exports | | **Works only with** | ES Modules (`import` / `export`) | | **Does not remove** | Code with side effects | | **Configuration** | `sideEffects: false` in `package.json`, `mode: production` | | **Tools** | Webpack >= 2, Rollup, ESBuild, Vite | | **Goal** | Reduce bundle size without manually removing code | --- **The main idea:** > A bundler builds a "dependency tree" and "shakes off" > everything that isn't used, hence "tree shaking".For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.