DEV Community

Cover image for ReactJs Performance ~ Tree Shaking and Bundle Analysis ~
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

ReactJs Performance ~ Tree Shaking and Bundle Analysis ~

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();
Enter fullscreen mode Exit fullscreen mode

Avoid CommonJS where possible:

const { fetchData } = require("./api");
Enter fullscreen mode Exit fullscreen mode

This pattern makes it harder for bundlers to optimize.

  1. 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);
Enter fullscreen mode Exit fullscreen mode
  1. 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
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Be careful: if your code includes things like global styles, polyfills, or initialization logic, marking everything as side-effect-free can break your app.

  1. Prefer named exports in reusable code

Named exports give bundlers clearer signals about what’s being used.

export const Card = () => {};
export const Tooltip = () => {};
Enter fullscreen mode Exit fullscreen mode

Then import only what you need:

import { Card } from "@/components/ui";
Enter fullscreen mode Exit fullscreen mode

Don’t guess—inspect your bundle

Instead of assuming optimizations are working, analyze the output.

Install analyzer

npm install --save-dev webpack-bundle-analyzer
Enter fullscreen mode Exit fullscreen mode

Example setup

const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: "static",
      openAnalyzer: false,
      reportFilename: "report.html",
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Build and review

npm run build
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

Limit icon imports

// ❌ Loads everything
import * as Icons from "lucide-react";
// ✅ Only what you use
import { Search, User } from "lucide-react";
Enter fullscreen mode Exit fullscreen mode

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)