Best practices and guidelines for Rollup.js module bundler configuration, ES modules, and library bundling
Rollup Bundler
You are an expert in Rollup.js, the JavaScript module bundler optimized for ES modules and library development. Follow these guidelines when working with Rollup configurations.
Core Principles
Rollup is designed for ES modules and produces cleaner, smaller bundles
Superior tree-shaking through deep execution path analysis
Ideal for libraries and packages that will be consumed by other projects
Focus on producing efficient, readable output code
Project Structure
project/
├── src/
│ ├── index.js # Main entry point
│ ├── modules/ # Internal modules
│ └── utils/ # Utility functions
├── dist/ # Build output
│ ├── bundle.esm.js # ES module format
│ ├── bundle.cjs.js # CommonJS format
│ └── bundle.umd.js # UMD format
├── rollup.config.mjs # Configuration file
└── package.json
Configuration Basics
Basic Configuration
// rollup.config.mjs
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
sourcemap: true
}
};
Multiple Output Formats
export default {
input: 'src/index.js',
output: [
{
file: 'dist/bundle.esm.js',
format: 'esm',
sourcemap: true
},
{
file: 'dist/bundle.cjs.js',
format: 'cjs',
sourcemap: true
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'MyLibrary',
sourcemap: true
}
]
};
Output Formats
ES Modules (esm)
Keeps bundle as ES module file
Best for modern bundlers and browsers
Supports tree-shaking in consuming projects
CommonJS (cjs)
For Node.js environments
Compatible with require() syntax
UMD (Universal Module Definition)
Works as AMD, CJS, and IIFE
Best for libraries that need broad compatibility
IIFE (Immediately Invoked Function Expression)
Self-executing function
Suitable for script tags in browsers
ES Modules Best Practices
Use Named Exports
// Prefer named exports for better tree-shaking
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// Avoid default exports when possible
// export default { add, subtract }; // Less tree-shakeable
Static Imports
// Static imports enable tree-shaking
import { specificFunction } from './utils';
// Avoid dynamic requires in library code
// const utils = require('./utils'); // CommonJS - no tree-shaking
Essential Plugins
Node Resolve
import resolve from '@rollup/plugin-node-resolve';
export default {
plugins: [
resolve({
browser: true,
preferBuiltins: false
})
]
};
CommonJS Conversion
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
commonjs()
]
};
Babel Transpilation
import babel from '@rollup/plugin-babel';
export default {
plugins: [
babel({
babelHelpers: 'bundled',
presets: ['@babel/preset-env']
})
]
};
TypeScript Support
import typescript from '@rollup/plugin-typescript';
export default {
input: 'src/index.ts',
plugins: [
typescript({
tsconfig: './tsconfig.json'
})
]
};
Minification
import terser from '@rollup/plugin-terser';
export default {
plugins: [
terser()
]
};
Environment Variables
import replace from '@rollup/plugin-replace';
export default {
plugins: [
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify('production')
})
]
};
External Dependencies
Marking Dependencies as External
export default {
input: 'src/index.js',
external: ['react', 'react-dom', 'lodash'],
output: {
file: 'dist/bundle.js',
format: 'esm',
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
};
Peer Dependencies Pattern
import pkg from './package.json';
export default {
external: [
...Object.keys(pkg.peerDependencies || {}),
...Object.keys(pkg.dependencies || {})
]
};
Code Splitting
Multiple Entry Points
export default {
input: {
main: 'src/index.js',
utils: 'src/utils/index.js'
},
output: {
dir: 'dist',
format: 'esm'
}
};
Dynamic Imports
// Rollup handles dynamic imports automatically
async function loadModule() {
const module = await import('./heavy-module.js');
return module.default;
}
Tree Shaking Optimization
Pure Function Annotations
// Mark functions as pure for better tree-shaking
export const compute = /*#__PURE__*/ createCompute();
Side Effects Configuration
{
"name": "my-library",
"sideEffects": false
}
Watch Mode
// rollup.config.mjs
export default {
watch: {
include: 'src/**',
clearScreen: false
}
};
Command line:
rollup -c -w
Package.json Configuration
{
"name": "my-library",
"version": "1.0.0",
"main": "dist/bundle.cjs.js",
"module": "dist/bundle.esm.js",
"browser": "dist/bundle.umd.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/bundle.esm.js",
"require": "./dist/bundle.cjs.js"
}
},
"files": ["dist"],
"sideEffects": false
}
Best Practices
Do
Use ES6 module syntax consistently
Mark third-party dependencies as external
Generate source maps for debugging
Use named exports for better tree-shaking
Test all output formats
Use watch mode during development
Avoid
Bundling peer dependencies
Using CommonJS modules when ES modules are available
Ignoring bundle size
Skipping TypeScript declaration files for libraries
Common Configuration Patterns
Library Configuration
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';
import pkg from './package.json';
export default {
input: 'src/index.ts',
external: Object.keys(pkg.peerDependencies || {}),
plugins: [
resolve(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
terser()
],
output: [
{ file: pkg.main, format: 'cjs', sourcemap: true },
{ file: pkg.module, format: 'esm', sourcemap: true }
]
};
Debugging and Analysis
Use --configDebug flag for configuration debugging
Generate source maps for all formats
Use rollup-plugin-visualizer for bundle analysis
Check output code readabilitydon't have the plugin yet? install it then click "run inline in claude" again.