Rollup Reference: Config, Plugins, Tree Shaking, Library Builds & Formats
Rollup is the bundler of choice for JavaScript libraries. React, Vue, Svelte, D3, and most npm packages use Rollup to produce their distributed builds. The reason: Rollup’s tree-shaking was the original and remains the best — it produces smaller, cleaner output than webpack for library builds. For applications (with CSS, images, HMR), use Vite (which wraps Rollup) instead. Rollup directly when you need maximum control over your library’s output format.
1. Config — rollup.config.js
rollup.config.js structure, input/output, formats, and external dependencies
// npm install -D rollup
// rollup.config.js (or .mjs):
import { defineConfig } from "rollup";
export default defineConfig({
input: "src/index.ts", // entry point (or array for multiple)
output: [
// ESM for modern bundlers (tree-shakeable):
{
file: "dist/index.esm.js",
format: "esm",
sourcemap: true,
},
// CJS for Node.js and older bundlers:
{
file: "dist/index.cjs.js",
format: "cjs",
exports: "named", // "default" | "named" | "auto"
sourcemap: true,
},
// UMD for browsers (script tag, AMD, CJS):
{
file: "dist/index.umd.js",
format: "umd",
name: "MyLib", // global variable name in browser
globals: { react: "React", "react-dom": "ReactDOM" },
sourcemap: true,
},
],
// Don't bundle peer dependencies:
external: ["react", "react-dom", "react/jsx-runtime"],
// Or regex:
external: (id) => id.startsWith("react"),
plugins: [],
});
2. TypeScript, JSX & Common Plugins
@rollup/plugin-typescript, @rollup/plugin-node-resolve, commonjs, and terser
// npm install -D @rollup/plugin-typescript tslib
// npm install -D @rollup/plugin-node-resolve @rollup/plugin-commonjs
// npm install -D @rollup/plugin-terser
// npm install -D @rollup/plugin-replace @rollup/plugin-json
import typescript from "@rollup/plugin-typescript";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import terser from "@rollup/plugin-terser";
import replace from "@rollup/plugin-replace";
import json from "@rollup/plugin-json";
import { defineConfig } from "rollup";
export default defineConfig({
input: "src/index.ts",
external: ["react", "react-dom"],
plugins: [
// Resolve node_modules imports:
resolve({ browser: true }), // prefer browser fields in package.json
// Convert CommonJS require() to ES imports (needed for many npm packages):
commonjs(),
// TypeScript:
typescript({
tsconfig: "./tsconfig.json",
declaration: true, // generate .d.ts files
declarationDir: "dist/types",
rootDir: "src",
}),
// Replace constants at build time:
replace({
preventAssignment: true,
"process.env.NODE_ENV": JSON.stringify("production"),
"__VERSION__": JSON.stringify(process.env.npm_package_version),
}),
// Import JSON files:
json(),
// Minify (production builds only):
process.env.NODE_ENV === "production" && terser({
compress: { drop_console: true },
}),
].filter(Boolean),
});
3. Tree Shaking & Output Analysis
How Rollup’s tree shaking works, pure annotations, and bundle visualisation
// Tree shaking — Rollup statically analyses imports/exports:
// Only exported+imported code is included in the bundle
// Pure annotation — mark side-effect-free calls:
// Rollup can remove code with side effects even if not used.
// Mark pure: no side effects:
export const utils = /*#__PURE__*/ createUtils();
// Rollup can remove createUtils() if utils is never imported
// preserveModules — output each file separately (no bundle, just transpile):
output: {
dir: "dist",
format: "esm",
preserveModules: true, // keeps original file structure
preserveModulesRoot: "src", // strips "src/" prefix from output paths
entryFileNames: "[name].js",
}
// dist/index.js, dist/utils/helpers.js, etc.
// Tree-shakeable at file level — better for library consumers
// Bundle analysis:
// npm install -D rollup-plugin-visualizer
import { visualizer } from "rollup-plugin-visualizer";
plugins: [visualizer({ filename: "stats.html", open: true, gzipSize: true })]
// Generate bundle stats to JSON:
// rollup --config --bundleConfigAsCjs 2> /dev/null | grep -c "modules"
// Check what Rollup included/excluded:
// rollup.config.js: treeshake: { moduleSideEffects: false }
// package.json: "sideEffects": false — tells bundlers this package is side-effect free
// OR: "sideEffects": ["./src/polyfills.js"] — list files with side effects
4. Multiple Outputs & Library package.json
Build ESM + CJS + types, package.json exports field, and dual-format publishing
// Multi-format build script (build.js):
import { rollup } from "rollup";
import typescript from "@rollup/plugin-typescript";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
const bundle = await rollup({
input: "src/index.ts",
external: ["react"],
plugins: [resolve(), commonjs(), typescript({ declaration: false })],
});
await Promise.all([
bundle.write({ file: "dist/index.esm.js", format: "esm", sourcemap: true }),
bundle.write({ file: "dist/index.cjs.js", format: "cjs", exports: "named", sourcemap: true }),
]);
await bundle.close();
// Generate types separately (faster than tsc via rollup):
// package.json scripts:
// "build:types": "tsc --emitDeclarationOnly --outDir dist/types"
// "build": "node build.js && npm run build:types"
// package.json exports field (modern — preferred over "main"/"module"):
{
"name": "@my-org/my-lib",
"exports": {
".": {
"import": "./dist/index.esm.js", // ESM import (bundlers, Node ESM)
"require": "./dist/index.cjs.js", // require() (Node CJS)
"types": "./dist/types/index.d.ts" // TypeScript types
},
"./utils": {
"import": "./dist/utils.esm.js",
"require": "./dist/utils.cjs.js"
}
},
"main": "./dist/index.cjs.js", // fallback for older tools
"module": "./dist/index.esm.js", // Rollup/webpack hint
"types": "./dist/types/index.d.ts",
"files": ["dist"] // only include dist/ in npm publish
}
5. Watch Mode, CLI & Plugins Reference
rollup –watch, CLI flags, and the key plugin hooks for custom plugins
# CLI:
npx rollup -c # build using rollup.config.js
npx rollup -c --watch # watch mode
npx rollup -c rollup.prod.config.js # specify config file
npx rollup src/index.ts -f esm -o dist/out.js # inline (no config)
# Rollup plugin hooks (for writing plugins):
const myPlugin = () => ({
name: "my-plugin",
// Transform source code:
transform(code, id) {
if (!id.endsWith(".ts")) return;
return { code: code.replace(/__VERSION__/g, "1.0.0"), map: null };
},
// Resolve custom imports:
resolveId(source) {
if (source === "virtual-module") return "\0virtual-module";
return null; // let other plugins handle it
},
// Load virtual modules:
load(id) {
if (id === "\0virtual-module") {
return `export const version = "${process.env.npm_package_version}";`;
}
return null;
},
// Called after bundle is generated:
generateBundle(options, bundle) {
// Inspect or modify bundle object
},
});
// Popular Rollup plugins:
// @rollup/plugin-alias — import path aliases
// @rollup/plugin-image — import images as base64/URL
// @rollup/plugin-strip — remove debugger + console statements
// @rollup/plugin-wasm — import .wasm files
// rollup-plugin-dts — bundle .d.ts declaration files
// rollup-plugin-peer-deps-external — auto-detect peer deps as external
Track Node.js and JavaScript tooling releases at ReleaseRun. Related: Vite 6 Reference | esbuild Reference | TypeScript Reference
🔍 Free tool: npm Package Health Checker — check rollup and its plugin packages for known CVEs and active maintenance.
Founded
2023 in London, UK
Contact
hello@releaserun.com