How Rollup Tree-Shaking Removes Unused ES Module Exports
Learn how Rollup analyzes ES module graphs to drop unused exports, see a minimal configuration example, and understand the limits and common pitfalls that affect tree-shaking effectiveness.
14 May 2026, 20:20 UTC

Useful answer
Rollup can eliminate ES module exports that are never imported, producing a smaller bundle without manual code removal. This works when your source files use ES module syntax (import/export) and Rollup is configured to treat dependencies as ES modules.
How tree-shaking works: a worked example
Consider a tiny project with the following structure:
project/
├── src/
│ ├── main.js
│ └── utils.js
└── rollup.config.js
utils.js exports two functions, but only one is used in main.js:
// src/utils.js
export function used() {
return 'used';
}
export function unused() {
return 'unused';
}
// src/main.js
import { used } from './utils.js';
console.log(used());
A minimal Rollup configuration that leaves the output as an ES module bundle looks like this:
// rollup.config.js
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'es'
},
plugins: []
};
Run npx rollup -c from the project root (no special permissions required). When Rollup builds the bundle, it constructs a module graph starting at src/main.js. It sees that used is referenced, but unused is never imported. Because both exports are side-effect-free (they only return a value), Rollup's tree-shaking step removes the unused function from the final bundle. The generated dist/bundle.js will contain only the used function and the code that references it.
Verifying the result
You can confirm that the unused export disappeared by inspecting the bundle:
- Open
dist/bundle.jsin a text editor. - Search for the string
unused. If tree-shaking worked, the function definition will not be present. - Alternatively, run a source-map explorer such as
npx source-map-explorer dist/bundle.js(requires source maps enabled viaoutput.sourcemap: true) and verify that the visualized module only includesused.
If you later add an import of unused in main.js and rebuild, the function will appear again, confirming that the removal was based on actual usage.
Limits and common mistakes
ES module syntax is required
Rollup's static analyzer can only understand import and export statements. If a dependency is published as CommonJS (require/module.exports), Rollup will treat its exports as used unless you convert them to ES modules first. A typical mistake is omitting @rollup/plugin-commonjs (or a similar plugin) when bundling a library that ships CJS files, which leads to larger bundles because none of its exports are dropped.
Side effects are conservatively preserved
Even if a module's exports are unused, Rollup will keep the entire module if it detects any side effect (for example, a top-level console.log, a CSS import, or a polyfill). Authors can signal that a file is side-effect-free by adding "sideEffects": false to the package's package.json. If this field is missing or set to true, Rollup assumes the module must be executed and will not drop it. You can compare bundle sizes with and without this flag in a dependency to measure its impact.
Entry chunk signatures
For library bundles, the preserveEntrySignatures option controls how Rollup emits exports from the entry point. The default ("strict") tries to preserve the exact shape of the entry module, which can sometimes inhibit tree-shaking of re-exported identifiers. Setting it to "allow-extension" or false gives Rollup more freedom to drop unused entry exports, but may change the public API shape for consumers, so review the emitted entry chunk before publishing.
Practical checklist
- Write source files using ES module
import/export. - Run Rollup with a plugin that converts CJS to ES modules if you depend on such packages (
@rollup/plugin-commonjs). - Ensure dependencies that are truly side-effect-free declare
"sideEffects": falsein theirpackage.json. - Inspect the generated bundle (or use a source-map explorer) to verify that unused exports are absent.
- When building a library, experiment with
preserveEntrySignaturesif you notice unexpected exports in the output.
By following these steps, you can rely on Rollup's tree-shaking to keep your bundles lean, while avoiding the common pitfalls that cause the optimizer to retain unnecessary code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.