Cutting Bundle Size with Rollup’s Tree‑Shaking: A Practical Guide for Library Authors
Learn how Rollup’s tree‑shaking can trim unused code from your library, the pitfalls to avoid, and a step‑by‑step example that shows real bundle size gains.
25 Aug 2025, 20:56 UTC

The Problem
When shipping a JavaScript library, every unused function you ship inflates the bundle that end‑users download. Even if a consumer imports only one feature, a poorly configured build can still ship the entire library, hurting load times and caching efficiency. Developers need a reliable way to let the bundler decide what to keep and what to drop.
Why Rollup’s Tree‑Shaking Matters
Rollup’s tree‑shaking is the de‑facto standard for eliminating dead code in ESM‑based projects. It relies on the static structure of import and export statements to build a dependency graph and then removes any export that is not reachable from the entry point. The result is a lean bundle that contains only the code that actually runs.
How the Algorithm Works
- Static analysis of ES Modules: Rollup parses the source files, records every
importandexport, and constructs a directed graph. - Reachability check: Starting from the entry file, Rollup walks the graph and marks all nodes that are reachable.
- Dead‑code elimination: Exports that were never marked as reachable are removed from the output.
- Side‑effect guard: If a module contains code that modifies global state or performs I/O during evaluation, Rollup treats the entire module as having side effects unless the
sideEffectsflag is set tofalseinpackage.jsonor the module is explicitly marked. - Property read analysis: With
treeshake: {propertyReadSideEffects: false}, Rollup assumes property reads are safe, allowing further pruning.
Practical Example
Below is a minimal library that exports three functions, but only one is used by the consumer. The example demonstrates how to configure Rollup to drop the unused functions and how to verify the outcome.
// lib/index.js
export function foo() {
console.log('foo called');
}
export function bar() {
console.log('bar called');
}
export function baz() {
console.log('baz called');
}
// lib/main.js – the library’s public entry point
import { foo } from './index.js';
export default foo;
// rollup.config.js
export default {
input: 'lib/main.js',
output: {
file: 'dist/library.js',
format: 'esm',
},
treeshake: {
moduleSideEffects: false, // assume no side effects unless marked
propertyReadSideEffects: false,
},
};
Run the build from a terminal that has Rollup installed globally or via npx rollup:
# Install Rollup locally if needed
npm install --save-dev rollup
# Build the library
npx rollup -c
After the build completes, inspect dist/library.js. The output should contain only the foo function and the default export, with bar and baz omitted. While we do not provide a live diff here, you can verify by searching for the function names in the bundle file. Compare the file size with the same build but with treeshake: false to see the quantitative gain.
When It Can Go Wrong
- Circular dependencies: If two modules import each other, Rollup’s reachability analysis may include both modules even if only one export is used, leading to larger bundles.
- Implicit side effects: Code that mutates global variables or performs I/O during module evaluation can be mistakenly removed if the module is marked as side‑effect free. Always set
sideEffectstofalseonly when you are sure there are no side effects. - Pre‑transpilation: If you run Babel or another transpiler before Rollup that converts ESM to CommonJS, Rollup will no longer see the static import/export structure and will skip tree‑shaking.
- CommonJS modules: Rollup can still bundle CommonJS modules, but it cannot perform tree‑shaking on them because the import graph is dynamic.
Actionable Takeaways
- Write your library using ES modules (
import/export) and avoidrequirecalls if you want tree‑shaking. - Set
sideEffects: falseinpackage.jsonor in the module file if you know the module has no side effects. This unlocks aggressive pruning. - Use the
treeshakeoption in Rollup’s config to fine‑tune the algorithm, especiallymoduleSideEffectsandpropertyReadSideEffects. - Run a quick sanity check: build with
treeshake: false, then enable it and compare bundle sizes. Search the output for the names of unused functions to confirm they are gone. - Watch out for circular dependencies and side‑effect‑heavy modules; refactor them or explicitly mark side effects to keep the bundle lean.
By following these steps, you can harness Rollup’s tree‑shaking to deliver a smaller, faster library that only ships what consumers actually need.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.