Skip to main content
Technology & EngineeringBuild Tools182 lines

Rollup

Rollup configuration for building JavaScript libraries with tree shaking and multiple output formats

Quick Summary24 lines
You are an expert in Rollup for building JavaScript libraries with optimized tree shaking, multiple output formats, and plugin-based extensibility.

## Key Points

- Always externalize `dependencies` and `peerDependencies` for library builds so consumers do not get duplicate copies.
- Set `sideEffects: false` in package.json to enable downstream tree shaking of your library.
- Use the `exports` field in package.json to define conditional entry points for ESM and CJS consumers.
- Bundle type declarations with `rollup-plugin-dts` to produce a single `.d.ts` file rather than a mirrored directory structure.
- Use array config to define multiple builds (ESM, CJS, UMD, types) in a single `rollup.config.mjs`.
- Prefer `@rollup/plugin-node-resolve` over bundling node_modules into the output for libraries.
- **Forgetting to externalize dependencies**: If peer or regular dependencies are not listed in `external`, they get bundled into your output, causing duplication for consumers.
- **CJS default export issues**: When outputting CJS, use `exports: 'named'` or `exports: 'auto'` to control how default exports are handled. Mismatches cause `require('lib').default` confusion.
- **CommonJS interop**: Packages using CommonJS need `@rollup/plugin-commonjs`. Without it, named imports from CJS packages will fail.
- **Missing resolve plugin**: Without `@rollup/plugin-node-resolve`, Rollup cannot find packages in `node_modules` because it only resolves relative and absolute paths by default.
- **Circular dependencies**: Rollup warns about circular dependencies. While they sometimes work at runtime, they often cause initialization order bugs and should be refactored.

## Quick Example

```bash
npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs
npm install --save-dev @rollup/plugin-typescript rollup-plugin-dts
```
skilldb get build-tools-skills/RollupFull skill: 182 lines
Paste into your CLAUDE.md or agent config

Rollup — Build Tools

You are an expert in Rollup for building JavaScript libraries with optimized tree shaking, multiple output formats, and plugin-based extensibility.

Core Philosophy

Overview

Rollup is a module bundler designed for JavaScript libraries. It produces clean, efficient output by leveraging ES module static analysis for tree shaking. Rollup excels at building packages that need to ship in multiple formats (ESM, CJS, UMD) and is the bundler used internally by Vite for production builds.

Setup & Configuration

Installation

npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs
npm install --save-dev @rollup/plugin-typescript rollup-plugin-dts

rollup.config.mjs

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { readFileSync } from 'fs';

const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'));

export default {
  input: 'src/index.ts',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      sourcemap: true,
      exports: 'named',
    },
    {
      file: pkg.module,
      format: 'esm',
      sourcemap: true,
    },
  ],
  external: [
    ...Object.keys(pkg.dependencies || {}),
    ...Object.keys(pkg.peerDependencies || {}),
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript({ tsconfig: './tsconfig.json' }),
  ],
};

package.json Fields

{
  "name": "my-lib",
  "main": "dist/index.cjs.js",
  "module": "dist/index.esm.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.esm.js",
      "require": "./dist/index.cjs.js",
      "types": "./dist/index.d.ts"
    }
  },
  "files": ["dist"],
  "sideEffects": false
}

Core Patterns

Multiple Entry Points

export default {
  input: {
    index: 'src/index.ts',
    utils: 'src/utils.ts',
    hooks: 'src/hooks/index.ts',
  },
  output: {
    dir: 'dist',
    format: 'esm',
    entryFileNames: '[name].js',
    chunkFileNames: 'chunks/[name]-[hash].js',
    sourcemap: true,
  },
  external: [/^react/],
  plugins: [resolve(), commonjs(), typescript()],
};

Type Declaration Bundling

import dts from 'rollup-plugin-dts';

export default [
  // Main build config (as above)
  mainConfig,
  // Declaration bundling
  {
    input: 'dist/types/index.d.ts',
    output: { file: 'dist/index.d.ts', format: 'esm' },
    plugins: [dts()],
  },
];

UMD Build for CDN

{
  input: 'src/index.ts',
  output: {
    file: 'dist/my-lib.umd.js',
    format: 'umd',
    name: 'MyLib',
    globals: {
      react: 'React',
      'react-dom': 'ReactDOM',
    },
    sourcemap: true,
  },
  external: ['react', 'react-dom'],
  plugins: [resolve(), commonjs(), typescript(), terser()],
}

Custom Plugin

function banner() {
  return {
    name: 'banner',
    renderChunk(code) {
      return `/* My Library v${pkg.version} | MIT License */\n${code}`;
    },
  };
}

Best Practices

  • Always externalize dependencies and peerDependencies for library builds so consumers do not get duplicate copies.
  • Set sideEffects: false in package.json to enable downstream tree shaking of your library.
  • Use the exports field in package.json to define conditional entry points for ESM and CJS consumers.
  • Bundle type declarations with rollup-plugin-dts to produce a single .d.ts file rather than a mirrored directory structure.
  • Use array config to define multiple builds (ESM, CJS, UMD, types) in a single rollup.config.mjs.
  • Prefer @rollup/plugin-node-resolve over bundling node_modules into the output for libraries.

Common Pitfalls

  • Forgetting to externalize dependencies: If peer or regular dependencies are not listed in external, they get bundled into your output, causing duplication for consumers.
  • CJS default export issues: When outputting CJS, use exports: 'named' or exports: 'auto' to control how default exports are handled. Mismatches cause require('lib').default confusion.
  • CommonJS interop: Packages using CommonJS need @rollup/plugin-commonjs. Without it, named imports from CJS packages will fail.
  • Missing resolve plugin: Without @rollup/plugin-node-resolve, Rollup cannot find packages in node_modules because it only resolves relative and absolute paths by default.
  • Circular dependencies: Rollup warns about circular dependencies. While they sometimes work at runtime, they often cause initialization order bugs and should be refactored.

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add build-tools-skills

Get CLI access →