Rollup.js Guide

An interactive tutorial for the next-generation JavaScript module bundler.

1. Project Setup & Basic Configuration

This section walks you through the initial steps of creating a new project, installing Rollup, and creating your first bundle. We'll set up the basic file structure and configuration needed to get started.

Step 1: Initialize the Project

Create a directory and initialize a new Node.js project.

mkdir rollup-project
cd rollup-project
npm init -y

Step 2: Install Rollup

Install Rollup as a development dependency.

npm install --save-dev rollup

Step 3: Create Source Files

Create a `src` directory with these two files:

src/logger.js

// A simple utility function
export const logMessage = (message) => {
  console.log(`[INFO] ${message}`);
};

src/main.js

import { logMessage } from './logger.js';

const heading = document.createElement('h1');
heading.textContent = 'Hello, Rollup!';
document.body.appendChild(heading);

logMessage('Application has started.');

Step 4: Create a Basic Rollup Configuration

Create a `rollup.config.js` file in the root of your project.

rollup.config.js

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife' // Immediately Invoked Function Expression
  }
};

2. Using Plugins for Common Tasks

Rollup's core functionality is extended through a rich ecosystem of plugins. Here, we'll add some of the most common plugins to handle modules from `node_modules` and transpile modern JavaScript for broader compatibility.

Step 1: Install Plugins

Install plugins for resolving Node modules and transpiling with Babel.

npm install --save-dev @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-babel @babel/core @babel/preset-env

Step 2: Update Rollup Configuration

Import and add the plugins to your `rollup.config.js`. This is the complete file at this stage.

rollup.config.js

import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true,
  },
  plugins: [
    resolve(),
    commonjs(),
    babel({ babelHelpers: 'bundled' })
  ]
};

3. Development vs. Production Configurations

Different environments require different build outputs. For development, we want fast rebuilds, a local server, and source maps. For production, we want a minified, optimized bundle. This section shows how to manage both from a single configuration file.

Step 1: Install Dev/Prod Plugins

Install plugins for serving content, live reloading, and minification.

npm install --save-dev rollup-plugin-serve rollup-plugin-livereload @rollup/plugin-terser

Step 2: Update `package.json` Scripts

Add `dev` and `build` scripts to your `package.json`.

package.json

{
  ...
  "scripts": {
    "build": "NODE_ENV=production rollup -c",
    "dev": "rollup -c -w"
  },
  ...
}

Step 3: Update Rollup Configuration

Use an environment variable to conditionally apply plugins. This is the complete file at this stage.

rollup.config.js

import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import terser from '@rollup/plugin-terser';

const isProduction = process.env.NODE_ENV === 'production';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: !isProduction,
  },
  plugins: [
    resolve(),
    commonjs(),
    babel({ babelHelpers: 'bundled', exclude: 'node_modules/**' }),

    !isProduction && serve({
      contentBase: ['dist'],
      port: 3000,
    }),
    !isProduction && livereload('dist'),

    isProduction && terser()
  ]
};

4. Handling CSS and Images

Modern web development involves more than just JavaScript. Learn how to use Rollup plugins to import, process, and bundle assets like CSS files and images directly within your project.

Step 1: Install Asset Plugins

Install plugins for handling CSS and image files.

npm install --save-dev rollup-plugin-postcss @rollup/plugin-image

Step 2: Update Rollup Configuration

Add the asset plugins to your configuration. This is the complete file at this stage.

rollup.config.js

import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import terser from '@rollup/plugin-terser';
import postcss from 'rollup-plugin-postcss';
import image from '@rollup/plugin-image';

const isProduction = process.env.NODE_ENV === 'production';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: !isProduction,
  },
  plugins: [
    resolve(),
    commonjs(),
    babel({ babelHelpers: 'bundled', exclude: 'node_modules/**' }),
    image(),
    postcss({
      extract: 'bundle.css',
      minimize: isProduction,
    }),
    !isProduction && serve({ contentBase: ['dist'], port: 3000 }),
    !isProduction && livereload('dist'),
    isProduction && terser(),
  ]
};

5. Using Rollup with TypeScript

Integrate the power of static typing into your Rollup workflow. This section covers setting up the TypeScript compiler and the official Rollup plugin to build type-safe applications.

Step 1: Install Dependencies

Install TypeScript and the Rollup TypeScript plugin.

npm install --save-dev typescript @rollup/plugin-typescript

Step 2: Configure TypeScript

Create a `tsconfig.json` file to configure the TypeScript compiler.

tsconfig.json

{
  "compilerOptions": {
    "target": "es2020",
    "module": "esnext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "sourceMap": true
  },
  "include": ["src/**/*"]
}

Step 3: Update Rollup Configuration

Add the TypeScript plugin and update the input file. This is the complete file at this stage.

rollup.config.js

import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import terser from '@rollup/plugin-terser';
import postcss from 'rollup-plugin-postcss';
import image from '@rollup/plugin-image';
import typescript from '@rollup/plugin-typescript';

const isProduction = process.env.NODE_ENV === 'production';

export default {
  input: 'src/main.ts',
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: !isProduction,
  },
  plugins: [
    resolve(),
    commonjs(),
    typescript({ sourceMap: !isProduction }),
    babel({ babelHelpers: 'bundled', exclude: 'node_modules/**' }),
    image(),
    postcss({
      extract: 'bundle.css',
      minimize: isProduction,
    }),
    !isProduction && serve({ contentBase: ['dist'], port: 3000 }),
    !isProduction && livereload('dist'),
    isProduction && terser(),
  ]
};

6. Code Splitting with Dynamic Imports

Improve your application's initial load time by splitting code into smaller chunks that are loaded on demand. Rollup has first-class support for the dynamic `import()` syntax, making code splitting easy and efficient.

Step 1: Use a Dynamic Import in Your Code

Use `import()` to load a module dynamically, for example on a button click.

src/main.ts

// ...
button.onclick = () => {
  import('./dynamic-module.js')
    .then(module => {
      module.showDynamicMessage();
    });
};
// ...

Step 2: Update Rollup Configuration

To enable code splitting, your configuration needs several changes. The `output.format` must be changed to `es`, and `output.file` must be replaced with `output.dir`. The following is the complete and final `rollup.config.js` incorporating all plugins and features from the entire tutorial.

rollup.config.js

import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import terser from '@rollup/plugin-terser';
import postcss from 'rollup-plugin-postcss';
import image from '@rollup/plugin-image';
import typescript from '@rollup/plugin-typescript';

const isProduction = process.env.NODE_ENV === 'production';

export default {
  input: 'src/main.ts',
  output: {
    dir: 'dist',
    format: 'es',
    sourcemap: !isProduction,
  },
  plugins: [
    resolve(),
    commonjs(),
    typescript({ sourceMap: !isProduction }),
    babel({ babelHelpers: 'bundled', exclude: 'node_modules/**' }),
    image(),
    postcss({
      extract: 'bundle.css',
      minimize: isProduction,
    }),

    // For development
    !isProduction && serve({
      contentBase: ['dist'],
      port: 3000,
    }),
    !isProduction && livereload('dist'),

    // For production
    isProduction && terser(),
  ],
};

7. Understanding Output Formats: `iife` vs. `es`

The output format determines how your bundled code is structured. Choosing the right one is crucial for compatibility and functionality. This section explains the two most common formats for web development in more detail.

`iife` (Immediately Invoked Function Expression)

This format is ideal for simple, single-file browser scripts. It wraps your entire bundle in a function that is executed immediately, creating a private scope. This prevents your code from adding variables to the global `window` object, which avoids conflicts with other scripts on the page.

Use when: You have a single entry point and don't need code splitting. It's great for compatibility with older browsers and simple script setups.

Example Output (dist/bundle.js)

(function () {
    'use strict';

    // All your bundled code is contained here...

})();

`es` (ES Modules)

This is the modern format for web applications. It outputs standard JavaScript modules that modern browsers can understand. Its primary advantage is that it's required for code splitting. When Rollup finds a dynamic `import()`, it creates a separate "chunk" file that the main bundle can load on demand.

Use when: You are building a modern application and want to use code splitting for better performance. Remember to load your script in HTML with <script type="module">.

Example Output (Multiple Files)

// dist/main.js
import { showDynamicMessage } from './chunk-abc.js';
// ... your main entry code ...

// dist/chunk-abc.js
const showDynamicMessage = () => { /*...*/ };
export { showDynamicMessage };

8. Conclusion & Next Steps

Congratulations on completing this guide! You now have a solid foundation for using Rollup.js to build efficient, modern JavaScript applications. You've learned how to set up a project, use essential plugins, manage different build environments, handle assets, and implement advanced features like TypeScript and code splitting.

From here, you can explore more advanced topics to further enhance your build process: