Contact

What is Tree Shaking?

Definition

Tree shaking is a bundler optimisation that detects code exported from JavaScript modules but never used anywhere, and leaves it out of the final bundle; it is a cross-module form of dead code elimination. It relies on the static import and export structure of ES modules. The term was popularised by Rollup, and tools such as webpack, esbuild and Turbopack now apply it in production builds.

Also known as: dead code elimination, dead-code elimination, unused export removal

Tree showing that only the functions an app actually imports are kept in the bundle, while unused exports are shaken out

Why it needs ES modules

For tree shaking to work, the bundler has to know before the code runs which parts of which module are used. In ES modules, import and export statements sit at the top level of a file and are static, so they can be analysed at build time. CommonJS require() is an ordinary function call: it can be conditional, and its argument can be computed at runtime. That uncertainty means CommonJS modules can't be pruned safely.

// format.js
export function date(d) { /* ... */ }
export function currency(n) { /* ... */ }
export function percent(n) { /* ... */ }

// app.js
import { currency } from "./format.js";
console.log(currency(1250));

// The production bundle contains only currency()

Side effects and the sideEffects field

Even when none of a module's exports are used, importing it may still do something: load a polyfill, patch a global object or inject a CSS file. A bundler can't be sure, so by default it plays safe and keeps the module. Package authors can remove that doubt with the sideEffects field in package.json:

{
  "name": "example-ui",
  "sideEffects": ["**/*.css", "./src/polyfill.js"]
}

"sideEffects": false declares the whole package side-effect free. If that claim is wrong, the result is a silent bug, most typically imported CSS files being dropped and styles vanishing. To mark a single function call as pure, there's the /*#__PURE__*/ annotation.

Habits that defeat tree shaking

  • Transpiling to CommonJS: if a tool like Babel rewrites modules into require() calls, analysis becomes impossible. The transpiler should hand ES module syntax to the bundler untouched.
  • Libraries that only ship CommonJS: using one helper can pull in the whole library. That's why checking for an ES module build matters when choosing a dependency.
  • Barrel files that re-export everything: icon and utility libraries that re-export hundreds of modules from one index.js make analysis harder when side-effect information is missing. Next.js offers the optimizePackageImports setting for such packages.
  • Only testing in development: tree shaking and minification mostly kick in for production builds, so judge bundle size from production output.

How it differs from minification and code splitting

TechniqueWhat it does
Tree shakingLeaves unused exports out of the bundle, across module boundaries
MinificationShortens what remains; strips whitespace, comments and unreachable code inside files
Code splittingDeletes nothing; moves code into separate chunks to change when it loads

The three complement each other and usually run together in modern bundlers.

Verifying the result

The most reliable check is to analyse the production bundle: if a library you use one function from shows up at full size, something is blocking the pruning. Tying bundle size to a performance budget in CI catches a new dependency quietly adding hundreds of kilobytes before it ships. Detailed configuration examples are in webpack's tree shaking guide.

Related terms

← Back to the glossary