Skip to main content

Command Palette

Search for a command to run...

Rollup Tree Shaking: Optimize JavaScript Bundles

Published
6 min read
T

Welcome to TopperBlog! 👋

I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.

🎯 What I Write About: • AI/ML Engineering & LLMs • Web3 & Blockchain Development
• System Design & Architecture • Interview Preparation (FAANG) • Freelancing & Remote Work • Modern Tech Stacks (Next.js, React, Rust, TypeScript) • Performance Optimization & Best Practices

💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.

📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.

🌐 Let's connect and grow together in this amazing tech journey!

#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering

Rollup Tree Shaking: Optimize JavaScript Bundles

The Build That Took 20 Minutes (Now Takes 30 Seconds)

Our CI pipeline was killing productivity. Then we switched build tools. Here's what happened.

Table of Contents

  • Modern Build Tools 2026
  • Architecture Overview
  • 5 Optimization Strategies
  • Configuration Guide
  • Performance Benchmarks
  • Migration Path
  • Troubleshooting
  • FAQ
  • Production Setup

Modern Build Tools in 2026

Speed is no longer negotiable.

Build Time Impact

// Business cost of slow builds
interface BuildCost {
  developerTime: number;    // $100/hour
  ciMinutes: number;        // $0.10/minute
  deploymentDelay: number;  // Lost revenue
}

const monthlyWaste = {
  developerTime: 40 * 100,  // $4,000
  ciMinutes: 1000 * 0.10,   // $100
  deploymentDelay: 'priceless'
};

Evolution of Bundlers

# 2018: Webpack (slow but powerful)
Build time: 5-20 minutes

# 2022: Vite (fast dev, slower prod)
Build time: 1-5 minutes

# 2026: Next-gen bundlers (instant everything)
Build time: 10-60 seconds

Why Speed Matters

Fast feedback loops = happy developers.

Architecture Overview

Understanding how bundlers work.

Build Pipeline

// Modern build pipeline
interface BuildPipeline {
  parse: 'AST generation';
  transform: 'Transpilation';
  bundle: 'Code splitting';
  optimize: 'Minification';
  output: 'File writing';
}

const pipeline: Record<keyof BuildPipeline, number> = {
  parse: 10,      // ms
  transform: 50,  // ms
  bundle: 100,    // ms
  optimize: 200,  // ms
  output: 40      // ms
};
// Total: 400ms vs 5min with old tools

Parallelization

// Concurrent processing
import { Worker } from 'worker_threads';

class ParallelBundler {
  private workers: Worker[];

  async bundle(files: string[]) {
    const chunks = this.splitIntoChunks(files);

    const results = await Promise.all(
      chunks.map(chunk => 
        this.processChunk(chunk)
      )
    );

    return this.mergeResults(results);
  }
}

Caching Strategy

Incremental builds save time.

Strategy 1: Configuration

Minimal Setup

// Modern bundler config
import { defineConfig } from 'bundler';

export default defineConfig({
  entry: './src/index.ts',
  output: {
    dir: './dist',
    format: 'esm'
  },
  plugins: [
    typescript(),
    minify()
  ]
});

Advanced Config

// Production optimization
export default defineConfig({
  build: {
    target: 'es2022',
    minify: 'esbuild',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['react', 'react-dom'],
          'utils': ['lodash-es', 'date-fns']
        }
      }
    }
  },
  optimizeDeps: {
    include: ['heavy-package']
  }
});

Environment Variables

// .env handling
import { loadEnv } from 'vite';

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd());

  return {
    define: {
      'import.meta.env.API_URL': JSON.stringify(env.VITE_API_URL)
    }
  };
});

Strategy 2: Code Splitting

Dynamic Imports

// Route-based splitting
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

Vendor Splitting

// Separate vendor bundles
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            // Split by package
            const match = id.match(/node_modules\/(.+?)\//);
            return match ? `vendor-${match[1]}` : 'vendor';
          }
        }
      }
    }
  }
};

Lazy Loading

Load heavy features on demand.

Strategy 3: Performance

Parallel Processing

// Multi-core utilization
export default {
  build: {
    // Use all CPU cores
    minify: {
      parallel: true,
      workers: os.cpus().length
    }
  }
};

Cache Configuration

// Persistent caching
export default {
  cacheDir: '.bundler-cache',
  build: {
    cache: {
      // Cache compiled modules
      module: true,
      // Cache dependencies
      dependencies: true
    }
  }
};

Asset Optimization

// Image optimization
export default {
  build: {
    assetsInlineLimit: 4096, // 4kb
    rollupOptions: {
      output: {
        assetFileNames: 'assets/[name]-[hash][extname]'
      }
    }
  }
};

Strategy 4: Tree Shaking

ES Modules

// Proper imports for tree shaking
// ❌ Bad: Imports everything
import _ from 'lodash';

// ✅ Good: Only imports what's used
import { debounce } from 'lodash-es';

// ✅ Better: Direct import
import debounce from 'lodash-es/debounce';

Side Effects

// package.json
{
  "name": "my-library",
  "sideEffects": false,
  // Or specify files with side effects
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.ts"
  ]
}

Dead Code Elimination

Mark unused code clearly.

Strategy 5: Monorepo Setup

Workspace Configuration

// pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'tools/*'

Shared Configuration

// packages/config/bundler.config.ts
export const sharedConfig = {
  plugins: [
    react(),
    typescript()
  ],
  build: {
    minify: true,
    sourcemap: true
  }
};

// apps/web/bundler.config.ts
import { sharedConfig } from '@config/bundler';

export default {
  ...sharedConfig,
  // App-specific overrides
};

Task Dependencies

// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"]
    }
  }
}

Performance Benchmarks

ToolDev StartBuild TimeHMRBundle Size
Webpack30s5min500ms1.2MB
Vite1s2min50ms1.0MB
Turbopack0.3s30s10ms950KB
esbuild0.1s10sN/A900KB

Migration Path

From Webpack

// 1. Install new bundler
npm install vite -D

// 2. Update package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

// 3. Create config
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  // Migrate webpack config here
});

Gradual Migration

Move one app at a time in monorepo.

Troubleshooting

Common Issues

// Error: Module not found
// Fix: Update import paths
// Before
import Component from 'src/Component';
// After
import Component from '@/Component';

// Error: Circular dependency
// Fix: Restructure imports or use dynamic imports
const Module = await import('./circular-dep');

Performance Problems

# Analyze bundle
npx vite-bundle-analyzer

# Check dependencies
npx depcheck

# Audit bundle size
npx bundlephobia

FAQ

Q1: Which bundler to choose?

Vite for apps, esbuild for libraries, Turbopack for Next.js.

Q2: How to reduce bundle size?

Tree shaking, code splitting, compression, and analyze dependencies.

Q3: HMR not working?

Check file watchers, exclude node_modules, restart dev server.

Q4: Build taking too long?

Enable caching, upgrade Node.js, use faster bundler.

Q5: Monorepo setup complexity?

Use Turborepo or Nx for enterprise-grade setup.

Production Setup

CI/CD Optimization

# GitHub Actions
- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: |
      ~/.pnpm-store
      .bundler-cache
    key: ${{ runner.os }}-deps-${{ hashFiles('**/pnpm-lock.yaml') }}

- name: Build
  run: pnpm build

Deployment

#!/bin/bash
# Production build script

# Clean
rm -rf dist

# Build
NODE_ENV=production pnpm build

# Verify
ls -lh dist/

# Deploy
aws s3 sync dist/ s3://bucket/ --delete

Conclusion

Fast builds enable fast iteration.

Key takeaways:

  • Choose modern tools
  • Configure caching
  • Split code intelligently
  • Monitor bundle size
  • Optimize continuously

Speed is a feature.

Resources:

  • Bundler Documentation
  • Performance Guides
  • Migration Tools
  • Community Plugins

Next Steps:

  1. Audit current build
  2. Choose new bundler
  3. Configure caching
  4. Migrate gradually
  5. Measure improvements

Speed up your builds today.

Rollup Tree Shaking: Optimize JavaScript Bundles