Removing Dead Code with JS Tree Shaking Guide

A practical walkthrough for finding and removing dead JavaScript code from your project using tree shaking. Configure your bundler, audit your dependencies, and verify the results.

7 min read

Removing dead JavaScript code with tree shaking is a practical skill that measurably improves your application's load time. While the tree shaking concept explains how it works, this guide walks through the actual steps: configuring your bundler, auditing your dependencies, and verifying the production output.

Think of this as a checklist. You run through it once, measure the improvement, and then repeat whenever you add new dependencies or notice bundle size creeping up.

Step 1: Switch to Production Mode

Tree shaking only runs in production builds. Development builds prioritize fast rebuilds over optimization, so they include all code. Verify you are building in production mode before measuring results.

For Webpack, set the mode explicitly:

javascriptjavascript
// webpack.config.js
module.exports = {
  mode: "production",
};

For Vite, run the build command which automatically uses production mode. For Rollup, the default behavior is to tree shake regardless, but production plugins like terser perform additional dead code removal.

Step 2: Add sideEffects to package.json

Open your project's package.json and add the sideEffects field. This is the single most impactful configuration change for tree shaking. Start by setting it to false, which tells the bundler every module in your project is safe to remove if its exports are unused:

jsonjson
{
  "name": "my-app",
  "sideEffects": false
}

Then list any files that must be excluded because they have genuine side effects. CSS files are the most common example:

jsonjson
{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

If you are unsure which files have side effects, start with only CSS exclusions and test thoroughly. Add more exclusions as you discover broken functionality.

Step 3: Audit Your Dependencies

Open node_modules and identify the largest libraries. A single monolithic library can dominate your bundle size. Common offenders and their tree-shakeable alternatives:

Heavy LibraryTree-Shakeable Alternative
lodash (full)lodash-es (ES module version)
moment.jsdate-fns or luxon
axios (full)ky or native fetch
underscorelodash-es

For lodash specifically, even importing a single function with CommonJS pulls in the entire library. Switching to the ES module version lets the bundler extract only the functions you actually call:

javascriptjavascript
// Before: imports entire lodash
import _ from "lodash";
_.debounce(fn, 300);
 
// After: imports only debounce
import { debounce } from "lodash-es";
debounce(fn, 300);

Run a bundle analysis after switching and verify the library's contribution shrinks significantly.

Step 4: Replace Barrel Files with Explicit Exports

Barrel files that use wildcard re-exports prevent tree shaking. The bundler cannot determine which exports are used, so it includes everything. Find barrel files in your project -- typically index.js files that aggregate exports from sibling modules -- and convert them to explicit re-exports.

Before, a barrel file that blocks tree shaking:

javascriptjavascript
// utils/index.js - forces all exports into the bundle
export * from "./dates.js";
export * from "./strings.js";
export * from "./numbers.js";

After replacing with explicit re-exports, the bundler can trace exactly which functions are imported and can safely remove the rest:

javascriptjavascript
// utils/index.js - bundler can trace usage
export { formatDate, parseDate } from "./dates.js";
export { capitalize, truncate } from "./strings.js";
export { round, clamp } from "./numbers.js";

The extra typing is worth the bundle size savings. For large utility folders, this change alone can reduce the final output by a noticeable amount.

Step 5: Verify with Bundle Analysis

After each change, build in production mode and analyze the output. The goal is to confirm that unused modules disappeared and that no new problems appeared. Use your bundler's analysis tool to generate a visualization, then compare it to the baseline from before your changes.

A practical workflow: take a screenshot of the treemap before starting, then another after each major change. The visual difference makes the impact immediately clear and helps catch regressions where a configuration change accidentally includes more code.

Step 6: Test Your Application

Tree shaking can break functionality if it removes code that has side effects. After enabling tree shaking, test every critical path in your application. Pay special attention to third-party integrations, analytics scripts, and polyfills -- these often have side effects that bundlers cannot detect through static analysis.

If a feature breaks, identify which dependency is missing, add it to the sideEffects exclusion list, rebuild, and verify the fix.

For bundle analysis configuration, the advanced code splitting guide includes tooling setup details.

Rune AI

Rune AI

Key Insights

  • Enable production mode and set sideEffects in package.json as the first two steps.
  • Audit your node_modules for large libraries that might offer tree-shakeable alternatives.
  • Replace barrel files that use export * with explicit re-exports.
  • Verify results with bundle analysis tools after every change.
  • Test functionality after enabling tree shaking to catch missing side effects.
RunePowered by Rune AI

Frequently Asked Questions

How much can tree shaking reduce my bundle size?

It varies by project, but reductions of twenty to fifty percent are common. Projects that import large utility libraries like lodash or moment.js often see the biggest wins because only a fraction of those libraries is typically used.

Why is my tree shaking not working even with ES modules?

Common causes: the library you are importing does not declare sideEffects in its package.json, your own package.json is missing the sideEffects flag, or you are using a barrel file with export * syntax that forces all modules to be included.

Can tree shaking break my application?

Yes, if a module has genuine side effects (like registering global event listeners or patching prototypes) and you mark it as side-effect-free. Always test thoroughly after enabling aggressive tree shaking, especially for third-party libraries.

Conclusion

Tree shaking is not a one-time configuration change. It is an ongoing practice of auditing imports, configuring sideEffects correctly, and verifying the production output. Start by running your bundler in production mode with the sideEffects flag enabled, then systematically identify and address the largest remaining dependencies. Each iteration should measurably shrink your bundle.