What Makes Rollup Different

Rollup was built specifically for ES modules when most bundlers were still wrestling with CommonJS. This head start means it actually understands your import and export statements instead of trying to retrofit support.

Tree-Shaking That Actually Works

The main reason you'd use Rollup is tree-shaking - it removes code you don't actually use. Webpack claims to do this too, but Rollup was designed around it from day one.

Here's where it gets good: import one function from a library and Rollup bundles just that function. With Webpack, you often get chunks of unused code because it can't figure out what you're actually using.

But tree-shaking breaks down when:

  • Libraries export everything through a barrel file (index.js that re-exports 50 modules)
  • You're stuck with CommonJS modules - the plugin helps but it's not magic
  • Some library decides to be "clever" with dynamic exports
  • You import anything from lodash the wrong way (use lodash-es or individual imports like import { debounce } from 'lodash-es')

Performance Reality Check

JavaScript bundling process workflow diagram

On my projects, Rollup builds finish while I'm getting coffee. Webpack builds finish while I'm making dinner. esbuild finishes before I can alt-tab, but you give up optimization.

  • Build times: Fast enough that you won't get bored waiting, unlike Webpack where you have time for a full lunch break
  • Bundle size: Actually smaller because tree-shaking works, not just claims to work
  • Memory usage: Doesn't require 32GB of RAM like Webpack on enterprise codebases

The esbuild preset trades optimization for speed - your builds are instant but bundles are bigger.

ES6 modules enable better tree shaking

When Rollup Makes Sense

Perfect for libraries: If you're publishing to npm, Rollup produces clean, optimized output that other tools can consume easily. Vue, React Router, and D3 all use it.

Good for apps prioritizing bundle size: If every KB matters and you can deal with the lack of dev server, Rollup will give you smaller bundles than alternatives.

Skip it if: You need hot module replacement, complex asset processing, or you're building a massive app with hundreds of entry points. Just use Vite instead - it uses Rollup for production builds anyway.

Real Problems You'll Hit

  • No dev server: Watch mode rebuilds on file changes but without hot reload, you're constantly refreshing. I spent 2019 refreshing browsers like a caveman until Vite saved us
  • CommonJS plugin hell: The plugin gives up on certain packages. react-dom is notorious for this. Spent 3 hours debugging why nothing was building - turns out Material-UI v4 has weird exports the plugin can't handle
  • Plugin compatibility: Rollup 3.2.0 broke our CI pipeline because they changed how externals work. No warning, no migration guide, just broken builds on Monday morning
  • Circular dependencies: Rollup doesn't just complain, it spams you with warnings until you fix them. Had a circular import in our Redux store that took down production because the bundle loaded modules in the wrong order

When to Actually Use Each Bundler

What You Need

Rollup

Webpack

Vite

esbuild

Parcel

Best For

Libraries

Big apps

Modern apps

Speed demons

Quick demos

Bundle Size

Actually small

Bloated as hell

Small (uses Rollup)

OK

OK

Build Speed

Fast enough

Slow as shit

Instant dev, fast prod

Lightning

Unpredictably slow

Tree Shaking

Actually works

Lies about working

Works (via Rollup)

Basic

Decent

Dev Server

None (use Vite)

Works but heavy

Best in class

None

Built-in but slow

Config Complexity

Simple → nightmare

Always nightmare

Just works

Minimal

Zero config until you need config

CommonJS Hell

Plugin hell

Handles it well

Plugin hell

Decent

No problem

When It Breaks

Plugin updates

Everything

Rarely

Never

Random Tuesday

Getting Rollup Working (Without Losing Your Mind)

Basic Install and Setup

Install it as a dev dependency (don't install globally unless you enjoy pain):

npm install --save-dev rollup

The CLI works for simple stuff:

rollup src/main.js --file dist/bundle.js --format esm

But you'll need a config file once things get real:

// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'  // or 'cjs' if you hate the future
  },
  plugins: [
    // You'll need these unless you enjoy pain
    resolve(), // Find node_modules 
    commonjs(), // Deal with legacy CommonJS garbage
  ]
}

Node.js module bundling workflow

Plugins You Actually Need

The plugin system is where Rollup shines, but also where shit breaks:

Essential plugins:

Plugin hell warnings:

  • Plugin order matters - resolve() before commonjs() or your build just fails silently. Spent 3 hours debugging why nothing was building. Turns out I had them backwards.
  • Updates break everything - pin versions in production or enjoy random build failures on Tuesday. The CommonJS plugin in version 22.x has a memory leak with large modules.
  • Plugins conflict in mysterious ways. The TypeScript plugin and Babel plugin hate each other sometimes. No error message, just silence.
  • rollup-plugin-visualizer will show you that Material-UI is 90% of your bundle. This saved my ass when one import pulled in 200KB of unused code.

Real Config for Real Projects

Here's what a working library config looks like:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';

export default [
  // ES modules (the good stuff)
  {
    input: 'src/index.ts',
    output: { 
      file: 'dist/index.esm.js', 
      format: 'esm' 
    },
    external: ['react', 'lodash'], // Don't bundle dependencies
    plugins: [resolve(), commonjs(), typescript()]
  },
  // CommonJS for backwards compatibility
  {
    input: 'src/index.ts',
    output: { 
      file: 'dist/index.cjs.js', 
      format: 'cjs' 
    },
    external: ['react', 'lodash'],
    plugins: [resolve(), commonjs(), typescript(), terser()]
  }
];

Shit That Will Break Your Build

Watch mode is slow:

rollup -c --watch

It rebuilds on changes but don't expect hot reload. Use Vite for development instead.

CommonJS modules that don't cooperate: The plugin tries its best but some packages are just broken. Look for ES module alternatives or find a different package.

Circular dependencies: Rollup hates these and will spam you with warnings. Fix them or your bundle might work weirdly.

Plugin compatibility: Major Rollup updates often break plugins. Check compatibility before upgrading or your build will just stop working.

External dependencies configuration: Forget to mark a peer dependency as external and your bundle will be massive. Remember to check package.json and mark peers/deps as external.

TypeScript Without Tears

TypeScript logo

TypeScript and Rollup integration

import typescript from '@rollup/plugin-typescript';

export default {
  plugins: [
    typescript({
      declaration: true,        // Generate .d.ts files
      declarationDir: 'dist',  // Put them somewhere useful
      exclude: ['**/*.test.ts'] // Don't bundle tests
    })
  ]
}

TypeScript gotchas:

  • The plugin uses your tsconfig.json but ignores half the settings for no reason
  • Declaration files end up in random places. I once spent 2 hours figuring out why .d.ts files were in src/ instead of dist/
  • Incremental compilation doesn't work with Rollup. Your builds are always from scratch.

Bundle Analysis (When Things Go Wrong)

Bundle analyzer showing dependency sizes

Add the visualizer to see what's eating your bundle size:

import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    // ... other plugins
    visualizer({
      filename: 'dist/stats.html',
      open: true  // Opens in browser after build
    })
  ]
}

This saved my ass when a single import pulled in 200KB of unused code.

Questions You'll Actually Ask

Q

Why does my build suddenly take forever?

A

Your watch mode is probably rebuilding everything instead of just what changed.

Rollup's watch mode isn't great for large projects. Use Vite for development

  • it has instant HMR and only uses Rollup for production builds.Also check if you accidentally imported something massive. One bad import can pull in half of lodash.
Q

How do I fix "Circular dependency" warnings?

A

Rollup really doesn't like circular dependencies and will spam you with warnings. These usually happen when:

  • Two modules import each other
  • A parent module imports a child that imports the parent
  • Deep circular chains through multiple files

Fix: Restructure your code to eliminate the circular imports, or extract shared code to a separate module both can import.

Q

Why isn't tree-shaking working for library X?

A

Tree-shaking breaks when libraries don't play nice:

  • Barrel exports: Library exports everything through index.js that re-exports 50 modules
  • CommonJS modules: Tree-shaking only works with ES modules
  • Side effects: Library does stuff on import that Rollup can't detect
  • Dynamic exports: Library uses dynamic imports or exports Rollup can't analyze

Fix: Use lodash-es instead of lodash, import specific functions (import { debounce } from 'lodash-es'), or find a different library.

Q

My bundle includes way too much stuff. What's wrong?

A

You probably forgot to mark dependencies as external. If you don't tell Rollup a dependency is external, it bundles the entire thing.

export default {
  external: ['react', 'react-dom', 'lodash'], // Don't bundle these
  // ... rest of config
}

Use `rollup-plugin-visualizer` to see what's eating your bundle size.

Q

Can I replace Webpack with Rollup for my app?

A

Maybe, but probably just use Vite instead. Vite gives you the best of both - esbuild's speed for development and Rollup's optimization for production.

Rollup alone lacks:

  • Dev server with hot reload
  • Built-in asset processing
  • Complex loader chains
  • The massive plugin ecosystem
Q

How do I handle TypeScript?

A
npm install --save-dev @rollup/plugin-typescript typescript
import typescript from '@rollup/plugin-typescript';

export default {
  plugins: [typescript()]
}

Gotchas:

  • Plugin might ignore some tsconfig.json settings
  • Declaration files sometimes end up in wrong places
  • Type checking happens during build, slowing things down
Q

What's the deal with CommonJS plugins?

A

The `@rollup/plugin-commonjs` plugin converts require() and module.exports to ES modules so Rollup can understand them.

It works most of the time but some packages are just broken. When it fails, you'll get weird errors about exports not being found. Try finding an ES module version of the package instead.

Q

Should I use Rollup for a big application?

A

Probably not directly. Rollup works great for libraries and medium apps, but big applications need more tooling.

Better options for big apps:

  • Vite: Uses Rollup for production, esbuild for dev
  • Webpack: Still the king for complex applications
  • Next.js/Nuxt: Framework-specific solutions
Q

Why do plugin updates keep breaking my build?

A

Plugin compatibility is Rollup's biggest pain point. Major updates often break plugins, and plugins sometimes conflict with each other.

Survival tips:

  • Pin plugin versions in production
  • Test updates in a separate branch
  • Plugin order matters - resolve() before commonjs() always
  • Check plugin changelogs before updating Rollup
Q

How fast is Rollup really?

A

On typical projects: 2-8 seconds for production builds. Not the fastest (that's esbuild at 0.1s) but good balance of speed and optimization.

With different presets:

  • esbuild preset: Fast builds, less optimization
  • Babel preset: Slower builds, maximum compatibility
  • Default: Decent speed, good optimization

For development, just use Vite.

Actually Useful Rollup Resources