DEV Community

Cover image for Your Bundle is Lying to You: Hidden Costs of Tree-Shaking
Abhinav Shinoy
Abhinav Shinoy

Posted on

Your Bundle is Lying to You: Hidden Costs of Tree-Shaking

We’ve all heard it:

“It’s fine, unused code gets tree-shaken out.”

But here’s the truth…

Your bundle is lying to you.
And tree-shaking isn't the silver bullet you think it is.

In this post, we’ll look at how tree-shaking really works, what gets left behind, and why your production bundle might be heavier than it needs to be — even when everything looks optimized.


🧩 What Is Tree-Shaking (Really)?

Tree-shaking is a bundler optimization technique that removes unused exports from ES modules during bundling.

Example:

// utils.js
export function used() {}
export function unused() {}
Enter fullscreen mode Exit fullscreen mode

If only used() is imported elsewhere, a good bundler will drop unused() from the final build.

Sounds great, right? So what’s the problem?


🚨 Problem 1: Side Effects Are Sticky

Many libraries contain side effects — things that run when the file is imported, even if nothing is used.

// file.js
console.log('This runs no matter what');
export const something = 123;
Enter fullscreen mode Exit fullscreen mode

Tree-shaking won’t remove this file unless the bundler knows it's side-effect-free.

If the library doesn’t declare:

"sideEffects": false
Enter fullscreen mode Exit fullscreen mode

…or it contains CSS imports, polyfills, or runtime setup, the whole file stays in — even if you use nothing from it.


🚨 Problem 2: Misleading Import Patterns

Some APIs are hard to shake because of how you import them.

Example:

// BAD
import * as _ from 'lodash';
Enter fullscreen mode Exit fullscreen mode

This pulls in the entire lodash bundle — even if you use just one function.

Instead, you should:

// GOOD
import debounce from 'lodash/debounce';
Enter fullscreen mode Exit fullscreen mode

Even better? Use modern tree-shakable alternatives like lodash-es.


🚨 Problem 3: Transpilation Can Break It

Your code might be perfectly optimized, but your build toolchain might sabotage it.

For example:

  • Babel + CommonJS modules = 💣
  • TypeScript with incorrect module targets = 🔥

Tree-shaking only works on ES modules (ESM). If your transpiler converts ESM to CommonJS (require()), the bundler can’t see what’s unused.

🛠 Fix: Set module to "esnext" and let your bundler handle module format.


🚨 Problem 4: You’re Still Paying for Parse and Eval

Even if dead code is dropped from the final JS file, you may still load, parse, or evaluate more than you think.

For example:

  • Large components wrapped in React.lazy() might be excluded from initial render… but still downloaded eagerly.
  • Some lazy-loaded code gets preloaded via <link rel="preload">.
  • Vendors like analytics or error tracking may import large polyfill chunks behind the scenes.

📦 The bundle size might look small — but your browser knows the truth.


🚨 Problem 5: Third-Party Packages Lie Too

That one dependency you installed? It might say it’s tree-shakable — but dig into the source and you’ll find:

  • Mixed ESM + CommonJS output
  • Side effects galore
  • Index files that re-export everything

Always test the actual output, not just what the docs claim.


✅ How to Fight Back

Here’s how to take control of your bundle:

🔍 1. Analyze It

Use tools like:

…and see what’s really in your output.


✂️ 2. Use ESM-Only Libraries

Prefer packages that:

  • Offer native ES module builds
  • Mark "sideEffects": false in their package.json

Bonus points for tree-shakable named exports.


📦 3. Import Precisely

Avoid star imports or named imports from index barrels.

import { format } from 'date-fns'
🚫 import * as dateFns from 'date-fns'


🧪 4. Test with Production Builds

Dev builds lie even more than your bundles.
Always test with minified, production-mode builds.

You’ll often be surprised.


💡 Final Thoughts

Tree-shaking is a great tool — but it’s not magic.

It doesn’t protect you from:

  • Bad imports
  • Lazy-but-eager modules
  • Bloated dependencies
  • Toolchain quirks

So next time your Lighthouse score drops or your TTI climbs, don’t assume your bundler saved you.
Open the analyzer. Inspect the output. Trust nothing.

Because sometimes…
Your bundle is lying to you.


Top comments (1)

Collapse
 
fstrube profile image
Franklin Strube

Great article. Thanks for sharing!