Making Rollup Tree Shaking Work: Practical Tips for Removing Unused JavaScript
Learn how Rollup’s static analysis removes unused JavaScript, what configuration and side‑effect markings are required, and how to verify the savings in a real‑world bundle.
02 Feb 2026, 11:56 UTC

Problem: Bundles Grow Even When You Import Little
You add a utility library to a project, import just one helper function, and notice the production bundle is still large. The extra size comes from code you never called but that remains in the output.
Thesis
Rollup’s tree shaking can drop that unused code, but it only works when the module format, side‑effect markings, and configuration align with its static analysis. Understanding those prerequisites lets you reliably shrink bundles.
How Rollup Builds the Dependency Graph
Rollup treats the entry point as the root of a static dependency graph. It follows ES Module import and export statements, marking each exported symbol that is reachable from the root. Symbols that are never referenced are considered dead and can be omitted.
Because the analysis is static, dynamic patterns like import() with a variable specifier or computed property access (obj[prop]) break the graph; Rollup must conservatively keep the whole module to avoid removing something that might be used at runtime.
Side‑Effect Awareness
Even if a function’s return value is unused, Rollup will keep the module if it has side effects. Side effects include:
- Assignments to
window,global, orprototype - CSS imports that affect styles
- Polyfills that modify built‑ins
To tell Rollup a file is pure, set "sideEffects": false in the package’s package.json. If the file does have side effects, list them explicitly, e.g., "sideEffects": ["*.css", "src/polyfills.js"].
Worked Example: Trimming a Utility Library
Suppose you have a library utils-js that exports:
// utils-js/src/index.js
export function helpers() { /* … */ }
export function formatDate() { /* … */ }
export function deepClone() { /* … */ }
Your application only needs formatDate:
// src/main.js
import { formatDate } from 'utils-js';
console.log(formatDate(new Date()));
With the default Rollup config, the bundle will still contain helpers and deepClone because the library is shipped as CommonJS or its package.json lacks "sideEffects": false. To enable tree shaking:
- Ensure the library publishes ES Module files (e.g.,
"module": "dist/utils-js.esm.js"). - Add
"sideEffects": falsetoutils-js/package.jsonif none of its files have side effects. - Configure Rollup to treat the entry as an ES module and allow the built‑in treeshake:
// rollup.config.js
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'iife',
name: 'app'
},
plugins: [terser()],
// Rollup’s treeshake is on by default; we can fine‑tune it:
onwarn: (warning, warn) => {
if (warning.code === 'THIS_IS_UNDEFINED') return;
warn(warning);
}
};
After building, inspect the bundle (e.g., with grep or a visualizer) to confirm that helpers and deepClone strings are absent. If they remain, check:
- Whether the library’s
package.jsonincorrectly marks side effects. - If any import in your app uses a dynamic path.
- If the library is being pulled in via a CommonJS wrapper that disables static analysis.
Trade‑offs and Limitations
Tree shaking is most effective with pure ES Modules. Libraries that ship only CommonJS or rely on runtime require cannot be fully pruned. Aggressive "sideEffects": false settings can accidentally drop needed CSS or polyfills, breaking the app. Always verify the final bundle behaves as expected in a browser or Node environment.
Actionable Closing
To make Rollup’s tree shaking work for you:
- Prefer ES Module imports (
import/export) overrequire. - Set accurate
sideEffectsflags in your dependencies. - Run a production build and manually check the output for dead code.
- If you see unexpected retention, trace the import path for dynamism or mis‑marked side effects.
When these conditions are met, Rollup will reliably strip unused exports, giving you a smaller bundle without changing your application logic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.