What does a barrel file cost?
One tidy index.ts, one import, and suddenly the route bundle contains a charting library nobody on that page uses.
- Level
- SYSTEMS
- Read time
- 08 min
- Experiment
- Available
- Type
- INVESTIGATION
The question
Why does importing a single helper from a barrel file sometimes pull in an entire package, and why does tree shaking not save you?
Hypothesis
A barrel re-exports every module in a directory. Unless every one of those modules is provably free of side effects, the bundler must keep them.
Method
Laboratory available
The instrument for this investigation runs in the laboratory, where the controls, the live model and the observation log share one workstation.
Enter the laboratoryAdd packages to the graph and watch the transitive closure and the shipped estimate move. The same mechanics apply inside your own source tree.
What we observed
export * from "./Button";export * from "./Chart"; // pulls in the charting libraryexport * from "./DatePicker"; // pulls in the date libraryexport * from "./Table";Importing Button from that barrel asks the bundler to evaluate the whole module graph behind it. With clean ESM and no side effects it can drop the unused branches. One sideEffects omission, one CommonJS dependency or one module-scope registration is enough to keep everything.
Why it happens
What keeps a module alive
- 01The package does not declare
"sideEffects": false, so the bundler must assume importing it does something observable. - 02A module runs code at import time: registering a web component, polyfilling, mutating a prototype, reading configuration.
- 03The dependency is CommonJS, so its exports cannot be analysed statically.
- 04A namespace import is indexed dynamically, which defeats static analysis.
The fix is boring: import from the module, not from the directory. Keep barrels for genuinely small, side-effect-free groups, and let the build measure the difference rather than trusting that tree shaking will handle it.
Further research
Check the claim rather than the convention. A bundle analyser will tell you which modules a route actually contains, and comparing two builds, one importing through the barrel and one importing directly, settles the argument in about ten minutes.
References
- 01MDNTree shaking
- 02webpacksideEffects
- 03Node.jsPackage entry points