cheat sheet

rollup

Daily-driver reference for rollup — CLI commands, multi-format output, plugin chain patterns, library publishing config.

rollup — CLI & plugins

What it is

rollup is the library-focused JS bundler — clean tree-shaken ESM/CJS/UMD output, a plugin model that became the standard for the ecosystem (Vite plugins are Rollup-plugin-compatible). For libraries you publish, this is still the default. For apps, use Vite (which wraps Rollup for prod).

For full ecosystem context, see packages-npm/npm-rollup. This page covers the CLI and plugin patterns.

Install

bash
npm install -D rollup
# common companions
npm install -D @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript tslib
npm install -D @rollup/plugin-terser rollup-plugin-dts

Output: rollup CLI in node_modules/.bin/.

Day-to-day commands

CommandWhat it does
rollup -cBuild using rollup.config.js
rollup -c rollup.lib.config.jsUse a specific config
rollup -c -wWatch mode
rollup -i src/index.js -o dist/out.js -f esmCLI-only build (no config)
rollup -c --silentSuppress non-error output
rollup -c --environment NODE_ENV:productionPass env vars (read via process.env)
rollup -c --bundleConfigAsCjsRead rollup.config.js as CJS

rollup.config.js is loaded as ESM by default if your package.json has "type": "module"; otherwise CJS. You can also name files rollup.config.mjs / rollup.config.cjs to force a mode.

Common scenarios

Library bundle — ESM + CJS

javascript
// rollup.config.js
import typescript from "@rollup/plugin-typescript";
import nodeResolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";

export default {
  input: "src/index.ts",
  output: [
    { file: "dist/index.mjs", format: "es" },
    { file: "dist/index.cjs", format: "cjs", exports: "named" },
  ],
  external: ["react"],
  plugins: [nodeResolve(), commonjs(), typescript()],
};
bash
npx rollup -c

Output:

text
src/index.ts → dist/index.mjs, dist/index.cjs...
created dist/index.mjs, dist/index.cjs in 412ms

Match package.json:

json
{
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

Plugin chain — TS + JSX + replace + minify

javascript
import typescript from "@rollup/plugin-typescript";
import nodeResolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import replace from "@rollup/plugin-replace";
import terser from "@rollup/plugin-terser";

export default {
  input: "src/index.tsx",
  output: { file: "dist/bundle.js", format: "es", sourcemap: true },
  external: ["react", "react-dom"],
  plugins: [
    nodeResolve({ extensions: [".js", ".ts", ".tsx"] }),
    commonjs(),
    replace({
      "process.env.NODE_ENV": JSON.stringify("production"),
      preventAssignment: true,
    }),
    typescript({ jsx: "react-jsx" }),
    terser(),
  ],
};

Plugin order is top-to-bottom (opposite of webpack loaders). replace before typescript substitutes in TS source; after substitutes in compiled JS.

Multi-format output

javascript
export default {
  input: "src/index.ts",
  output: [
    { file: "dist/my-lib.mjs", format: "es" },
    { file: "dist/my-lib.cjs", format: "cjs", exports: "named" },
    {
      file: "dist/my-lib.umd.js",
      format: "umd",
      name: "MyLib",
      globals: { react: "React", "react-dom": "ReactDOM" },
    },
  ],
  external: ["react", "react-dom"],
};
javascript
import pkg from "./package.json" with { type: "json" };

export default {
  output: {
    file: "dist/index.mjs",
    format: "es",
    banner: `/*! ${pkg.name} v${pkg.version} | ${pkg.license} */`,
  },
};

Bundle .d.ts files

javascript
// rollup.dts.config.js
import dts from "rollup-plugin-dts";

export default {
  input: "dist/types/index.d.ts",
  output: { file: "dist/index.d.ts", format: "es" },
  plugins: [dts()],
};

Two-step library build:

json
{
  "scripts": {
    "build:types": "tsc --emitDeclarationOnly --outDir dist/types",
    "build:bundle": "rollup -c",
    "build:dts": "rollup -c rollup.dts.config.js",
    "build": "npm run build:types && npm run build:bundle && npm run build:dts"
  }
}

Preserve modules (per-file output)

javascript
output: {
  dir: "dist",
  format: "es",
  preserveModules: true,
  preserveModulesRoot: "src",
}

Mirrors src/ structure under dist/. Best for libraries where consumers do their own tree-shaking.

Watch mode

bash
npx rollup -c -w

Output:

text
rollup v4.21.0
bundles src/index.ts → dist/index.mjs...
created dist/index.mjs in 412ms

[1500] waiting for changes...

Useful flags

FlagWhat it does
-c, --config <path>Path to config file
-w, --watchWatch mode
-i, --input <file>Entry point (CLI-only build)
-o, --file <file>Output file
-f, --format <fmt>Output format (es, cjs, umd, iife, amd, system)
-n, --name <name>Global name for UMD/IIFE builds
-e, --external <list>Comma-separated external IDs
-m, --sourcemapEmit sourcemap
--silentSuppress non-error output
--bundleConfigAsCjsForce-load config as CJS
--environment <list>Set env vars (NODE_ENV:production,DEBUG:true)
--treeshake.<flag>Tweak tree-shaking heuristics

Configuration

Beyond config files, rollup honours config-as-function for dynamic settings:

javascript
import { defineConfig } from "rollup";

export default defineConfig((cmdArgs) => ({
  input: "src/index.ts",
  output: {
    file: cmdArgs.configMinify ? "dist/index.min.js" : "dist/index.js",
    format: "es",
  },
}));
bash
npx rollup -c --configMinify

Output:

text
src/index.js → dist/...
created dist in 612ms

defineConfig exists for IDE autocomplete; vanilla export default { ... } works the same.

Common output options

OptionPurpose
fileSingle-file output
dirMulti-file output (chunks)
formates, cjs, umd, iife, amd, system
nameGlobal name (UMD/IIFE only)
globalsMap of external → global var (UMD/IIFE)
banner / footerPrepend/append strings
sourcemaptrue, "inline", "hidden", false
exports"auto", "default", "named", "none"
preserveModulesPer-file output (skip bundling)

Common pitfalls

  1. Could not resolve "X" from "src/Y.js". Missing @rollup/plugin-node-resolve, or the module is meant to be external (add to external).
  2. 'default' is not exported by "node_modules/foo". CJS dep lacks a default export. Add @rollup/plugin-commonjs with requireReturnsDefault: "auto".
  3. Mixing named and default exports warning. Set output.exports: "named" explicitly, or refactor source to use only one style.
  4. Tree shaking removed a side-effecting import. Mark the file in package.json "sideEffects": ["./src/register.ts"].
  5. process.env.NODE_ENV un-substituted. Add @rollup/plugin-replace with preventAssignment: true.
  6. Plugin order matters. replace before TS → substitute in source. After → substitute in compiled output.
  7. .d.ts per file instead of one consolidated file. Use the rollup-plugin-dts two-step pattern above.

See also