Front-end Engineering Lab

Bundle Analyzer Setup

Identify and eliminate heavy dependencies to reduce bundle size and improve load times.

Bundle Analyzer Setup

Problem

JavaScript bundles grow to several megabytes without notice. Single dependencies can add 200+ KB. Users on mobile wait 10+ seconds for apps to load.

Solution

Use bundle analyzers to visualize what's in your bundle, identify heavy dependencies, and replace them with lighter alternatives.

/**
 * Webpack Bundle Analyzer setup
 */
const webpackConfig = `
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
      openAnalyzer: false,
      generateStatsFile: true,
      statsFilename: 'bundle-stats.json',
    }),
  ],
};
`;

/**
 * Analyze bundle programmatically
 */
interface BundleStats {
  totalSize: number;
  modules: Array<{
    name: string;
    size: number;
    percentage: number;
  }>;
  largestModules: Array<{
    name: string;
    size: number;
  }>;
}

function analyzeBundleStats(statsFile: string): BundleStats {
  const stats = require(statsFile);
  const modules = stats.modules || [];

  const totalSize = modules.reduce(
    (sum: number, m: { size: number }) => sum + m.size,
    0
  );

  const sortedModules = modules
    .map((m: { name: string; size: number }) => ({
      name: m.name,
      size: m.size,
      percentage: (m.size / totalSize) * 100,
    }))
    .sort((a: { size: number }, b: { size: number }) => b.size - a.size);

  return {
    totalSize,
    modules: sortedModules,
    largestModules: sortedModules.slice(0, 10),
  };
}

/**
 * Common heavy dependencies and their lightweight alternatives
 */
interface DependencyAlternative {
  heavy: string;
  size: string;
  light: string;
  lightSize: string;
  savings: string;
}

const alternatives: DependencyAlternative[] = [
  {
    heavy: 'moment',
    size: '288 KB',
    light: 'date-fns',
    lightSize: '20 KB (tree-shakable)',
    savings: '93%',
  },
  {
    heavy: 'lodash',
    size: '72 KB',
    light: 'lodash-es (tree-shakable)',
    lightSize: '5-10 KB',
    savings: '85%',
  },
  {
    heavy: 'axios',
    size: '13 KB',
    light: 'native fetch',
    lightSize: '0 KB',
    savings: '100%',
  },
  {
    heavy: 'jquery',
    size: '87 KB',
    light: 'native DOM APIs',
    lightSize: '0 KB',
    savings: '100%',
  },
  {
    heavy: 'chart.js',
    size: '240 KB',
    light: 'recharts (React) or lightweight-charts',
    lightSize: '45 KB',
    savings: '81%',
  },
];

Package Analysis Commands

# Webpack Bundle Analyzer
npm install --save-dev webpack-bundle-analyzer
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

# Rollup Plugin Visualizer
npm install --save-dev rollup-plugin-visualizer
npx rollup -c --plugin visualizer

# Next.js Bundle Analyzer
npm install --save-dev @next/bundle-analyzer
ANALYZE=true npm run build

# Vite Bundle Analyzer
npm install --save-dev rollup-plugin-visualizer
vite build --mode analyze

# Source Map Explorer
npm install --save-dev source-map-explorer
source-map-explorer dist/**/*.js

Automated Bundle Size Monitoring

/**
 * CI/CD bundle size check
 */
interface BundleSizeConfig {
  maxSize: number;
  files: string[];
}

async function checkBundleSize(config: BundleSizeConfig): Promise<void> {
  const fs = await import('fs/promises');
  const path = await import('path');

  for (const file of config.files) {
    const stats = await fs.stat(file);
    const sizeKB = stats.size / 1024;

    console.log(`${path.basename(file)}: ${sizeKB.toFixed(2)} KB`);

    if (sizeKB > config.maxSize) {
      throw new Error(
        `Bundle size exceeded: ${sizeKB.toFixed(2)} KB > ${config.maxSize} KB`
      );
    }
  }

  console.log('✓ Bundle size check passed');
}

// Usage in CI
checkBundleSize({
  maxSize: 250, // KB
  files: ['dist/main.js', 'dist/vendor.js'],
}).catch((error) => {
  console.error(error);
  process.exit(1);
});

GitHub Action for Bundle Size

name: Bundle Size Check
on: [pull_request]
jobs:
  check-size:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: andresz1/size-limit-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

Common Optimizations

1. Replace Heavy Libraries

// Before: 288 KB
import moment from 'moment';
const date = moment().format('YYYY-MM-DD');

// After: 20 KB
import { format } from 'date-fns';
const date = format(new Date(), 'yyyy-MM-dd');

2. Tree-Shakable Imports

// Before: Imports entire library (72 KB)
import _ from 'lodash';
_.debounce(fn, 300);

// After: Import only needed function (5 KB)
import debounce from 'lodash-es/debounce';
debounce(fn, 300);

3. Code Splitting

// Before: Everything in main bundle
import HeavyComponent from './HeavyComponent';

// After: Load on demand
const HeavyComponent = lazy(() => import('./HeavyComponent'));

4. Remove Unused Code

# Find unused exports
npx ts-prune

# Remove unused dependencies
npx depcheck

Performance Impact

Before Optimization

  • Bundle size: 2.5 MB
  • Load time (3G): 12 seconds
  • Parse time: 1.8 seconds

After Optimization

  • Bundle size: 450 KB (82% smaller)
  • Load time (3G): 2.2 seconds (80% faster)
  • Parse time: 180ms (90% faster)

Best Practices

  1. Run analyzer monthly: Dependencies grow over time
  2. Set budget alerts: Fail CI if bundle exceeds limit
  3. Prefer native APIs: fetch over axios, DOM over jQuery
  4. Use tree-shakable libraries: Import only what you need
  5. Lazy load routes: Split by page/feature
  6. Monitor bundle size: Track in CI/CD
  7. Audit dependencies: Use npm ls to check tree

Bundle Size Budget Example

{
  "name": "my-app",
  "bundlesize": [
    {
      "path": "./dist/main.js",
      "maxSize": "200 KB"
    },
    {
      "path": "./dist/vendor.js",
      "maxSize": "150 KB"
    },
    {
      "path": "./dist/**/*.css",
      "maxSize": "50 KB"
    }
  ]
}

Tools Comparison

ToolBest ForOutput
webpack-bundle-analyzerWebpack projectsInteractive treemap
source-map-explorerAny bundler with source mapsTreemap
bundlephobiaChecking npm packages before installSize + tree-shake info
size-limitCI/CD size checksPass/fail + size report

Impact at Scale

For 10M users/month:

  • 2.5 MB bundle: 25 TB bandwidth/month
  • 450 KB bundle: 4.5 TB bandwidth/month
  • Savings: 20.5 TB bandwidth + faster experience

Bundle analysis is mandatory for production applications. A single unnecessary dependency can cost thousands in bandwidth and hurt user experience.

Quick Wins

  1. Replace moment.js → 93% savings
  2. Use lodash-es → 85% savings
  3. Code split routes → 50-70% initial load savings
  4. Remove unused code → 20-30% savings
  5. Optimize images → 40-60% asset savings

Run bundle analyzer today and find easy wins.

On this page