Reduce Your Bundle Size with Effective Tree Shaking
Modern JavaScript bundlers are smart—but they’re not mind readers. If you want smaller production bundles, you need to structure your code in a way that makes unused code easy to remove.
What actually makes tree shaking work?
Tree shaking relies on static analysis. That means your code has to be predictable and explicit so the bundler can safely eliminate what’s not used.
Here are the fundamentals:
1. Stick to ES Modules
Use import / export syntax so the bundler can analyze dependencies at build time.
import { fetchData } from "./api";
export const load = () => fetchData();
Avoid CommonJS where possible:
const { fetchData } = require("./api");
This pattern makes it harder for bundlers to optimize.
- Be precise with imports
Pulling in an entire library when you only need one function is one of the fastest ways to bloat your bundle.
// ❌ Pulls in everything
import _ from "lodash";
const result = _.debounce(fn, 300);
// ✅ Only what you need
import { debounce } from "lodash-es";
const result = debounce(fn, 300);
// ✅ Even more direct
import debounce from "lodash-es/debounce";
const result = debounce(fn, 300);
- Declare side effects explicitly
Bundlers avoid removing files that might cause side effects. You can help them by clarifying this in package.json:
{
"sideEffects": false
}
⚠️ Be careful: if your code includes things like global styles, polyfills, or initialization logic, marking everything as side-effect-free can break your app.
- Prefer named exports in reusable code
Named exports give bundlers clearer signals about what’s being used.
export const Card = () => {};
export const Tooltip = () => {};
Then import only what you need:
import { Card } from "@/components/ui";
Don’t guess—inspect your bundle
Instead of assuming optimizations are working, analyze the output.
Install analyzer
npm install --save-dev webpack-bundle-analyzer
Example setup
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "static",
openAnalyzer: false,
reportFilename: "report.html",
}),
],
};
Build and review
npm run build
Open the generated report and look for unexpectedly large modules.
Common hidden bundle killers
Here are issues that often show up in real projects:
・Heavy date libraries used for simple formatting
・Importing entire icon packs instead of individual icons
・Duplicate dependencies caused by version mismatches
・Legacy browser polyfills that aren’t needed anymore
・Source maps accidentally included in production builds
Practical improvements
Replace heavy utilities
// Instead of a large library
import { format } from "date-fns";
const label = format(new Date(), "yyyy-MM-dd");
Limit icon imports
// ❌ Loads everything
import * as Icons from "lucide-react";
// ✅ Only what you use
import { Search, User } from "lucide-react";
Final thoughts
Tree shaking isn’t automatic magic—it’s a collaboration between your code and your tooling.
If you:
・write explicit imports
・structure exports clearly
・and verify your bundle output
you’ll consistently ship smaller, faster applications.
Top comments (0)