Tree-shaking: why named imports beat namespace imports
What tree-shaking does
A bundler walks your import graph and drops any export your code doesn't reference. The "tree" is your module graph; the "shake" is unused leaves falling off. End result: smaller bundles.
What kills tree-shaking
import * as lodash from "lodash"; // imports ALL of lodash, no shake lodash.debounce(fn, 100);
vs.
import { debounce } from "lodash"; // bundler can drop everything else
debounce(fn, 100);The namespace import import * as X forces the bundler to keep every exported symbol — you might reference X.anything at runtime, it can't know.
The other killer: side effects
// some-pkg/index.js import "./polyfill.js"; // side-effect import — runs the file even if you import nothing
If a package's entrypoint runs code at import time, that code stays in your bundle even if you only used one export. package.json has "sideEffects": false to declare a library is side-effect-free; bundlers honour it.
Two practical rules
- Always use named imports unless the library forces a namespace.
- Prefer libraries that mark
"sideEffects": falseand only use ESM.
In 2026 nearly every modern library does this (lodash-es, date-fns, ramda). It's CommonJS-only libraries (the older Node-style module.exports) that still bloat bundles by default.
Sign up to start coding
Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial — card required, cancel anytime.
Sign up — free trial →First 10 lessons in each track are free. No card needed for those.