Trying Rollup for React Applications

A comparison between Rollup and Webpack for React apps.

Nathan Sebhastian
Bits and Pieces

--

Webpack has been the most popular bundling tool for front-end development with React, and since both Create React App and Next.js used Webpack by default, I never bothered to learn about other bundlers before.

But I got curious about how to use Rollup lately, so I decided to explore Rollup and see if it offers anything interesting for React developers. Rollup is a module bundler for JavaScript that works similarly to Webpack, but is said to have easier configuration and smaller build size.

This tutorial will show you how to create a React-Rollup starter app from scratch and how Rollup compares against Webpack in terms of developer experience and configuration complexity.

If you get lost when following the tutorial, you can refer to the starter repository to compare your code with.

As a side note, if you’d like to understand what’s all the excitement about with Webpack’s new Module Federation, I recommend you read my previous blog:

Setting up a React-Rollup starter

To get started with Rollup, you need to install the package using NPM:

npm install rollup --global

Once Rollup is installed, create your React application manually by creating a new folder and name it react-rollup-test :

mkdir react-rollup-test

Inside the directory, create a newpackage.json file by running the npm init command. You can go with the default settings offered by the command:

{
"name": "react-rollup-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

Once done, let’s install the required packages for your React app. First, you’ll need both react and react-dom packages:

npm i react react-dom

Then, you need the rollup package, along with several Rollup plugins as follows:

  • rollup-plugin-serve for serving your app in the development environment.
  • rollup-plugin-livereload for automatic browser refresh when the build file is changed.
  • @rollup/plugin-node-resolve for importing modules installed with NPM.
  • @rollup/plugin-babel for transpiling JSX code into valid JavaScript code.
  • @rollup/plugin-commonjs so that Rollup can import code in CommonJS module format.
  • @rollup/plugin-replace for replacing the process.env.NODE_ENV variable with a static string.

Let’s install the above packages as development dependencies with --save-dev flag:

npm i rollup rollup-plugin-serve rollup-plugin-livereload @rollup/plugin-node-resolve @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-replace --save-dev

Next, you need to create a Rollup configuration file, which uses the CommonJS module format with export default just like Webpack. Here’s the minimum content of rollup.config.js file, I will explain it below:

import serve from "rollup-plugin-serve";
import livereload from "rollup-plugin-livereload";
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
export default {
input: "src/index.js",
output: {
file: "dist/bundle.js",
format: "iife",
sourcemap: true,
},
plugins: [
nodeResolve({
extensions: [".js"],
}),
replace({
'process.env.NODE_ENV': JSON.stringify( 'development' )
}),
babel({
presets: ["@babel/preset-react"],
}),
commonjs(),
serve({
open: true,
verbose: true,
contentBase: ["", "public"],
host: "localhost",
port: 3000,
}),
livereload({ watch: "dist" }),
]
};

Generally, Rollup only needs you to define the input and the ouput on its configuration file. You can select the format of the output bundle:

  • iife if you run the code in the browser
  • cjs for running the bundle in Node.js server
  • umd for both browser and Node.js

To smoothen the developer experience, the Serve and Live Reload plugin is configured:

  • serve will start the development server and serve your content from the specified contentBase option. You also can specify the options for port, host, and whether it will automatically open the browser for you once the bundling process is finished.
  • livereload will watch the dist folder and automatically refresh your browser when the folder content is changed.

Your Rollup configuration is ready for testing, but you still need an index.html file that will serve as the entry point of your application and download the bundle produced by Rollup. The HTML file below is modified from CRA’s default index.html file:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React - Rollup Test</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script src="../dist/bundle.js"></script>
</body>
</html>

Finally, you need some React code which will be taken from src/index.js file. Let’s follow CRA’s convention and create an index.js file to render your React application:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.querySelector('#root'));

And create another file named App.js where you write the application code. Since this is a test, it will just output some static content first:

import React from 'react';function App(){
return <h1>Hello World From React-Rollup</h1>;
}
export default App;

With that, your React-Rollup application is ready. Run the following command from the terminal to start the application:

rollup -c rollup.config.js -w

The -c flag will tell Rollup to pick up the configuration file while the -w flag will watch files included in the bundle and rebuild when you change them.

You can add the script to your package.json file scripts option so you can run the command with npm start:

"scripts": {
"start": "rollup -c rollup.config.js -w"
},

Because the Serve plugin is installed and configured, Rollup will automatically open the browser and navigates toward localhost:3000 as specified in the config file:

React-Rollup application test

When you change any file in the src folder, Rollup will automatically rebuild the bundle. When the bundle is overwritten, the Live Reload plugin will refresh the browser.

Handling CSS and images with Rollup

So far, so good. Let’s add CSS and image processing ability to Rollup before wrapping it all up.

To do that, you just need to install Rollup’s official plugin:

  • @rollup/plugin-image for processing images, including JPG, PNG, GIF, SVG, and WebP files.
  • @rollup/plugin-postcss for processing CSS files and inject it to the head tag of your HTML file
npm i @rollup/plugin-image @rollup/plugin-postcss --save-dev

Then add the plugins to your configuration file:

export default {
input: "src/index.js",
output: {
file: "dist/bundle.js",
format: "iife",
sourcemap: true,
},
plugins: [
image(),
postcss({
extensions: [".css"],
}),
// the rest is omitted...
]
};

Finally, you need to add some CSS and image files. You can visit the code repository, where I test the build using the SVG logo and CSS file included in Create React App.

Comparing configuration format

From the above experiment, I do think that the configuration format for Rollup is easier to understand. You just need to specify the input and output options for the most simple config.

Any Rollup plugins you need are installed and imported from the config file itself:

import serve from "rollup-plugin-serve";
import livereload from "rollup-plugin-livereload";
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import image from '@rollup/plugin-image';
import postcss from 'rollup-plugin-postcss';
export default {
plugins: [
image(),
postcss({
extensions: [".css"],
}),
nodeResolve({
extensions: [".js"],
}),
replace({
'process.env.NODE_ENV': JSON.stringify( 'development' )
}),
babel({
presets: ["@babel/preset-react"],
}),
commonjs(),
serve({
open: true,
verbose: true,
contentBase: ["", "public"],
host: "localhost",
port: 3000,
}),
livereload({ watch: "dist" }),
]
};

One of the thing that tripped me up when learning Webpack is that you need to use the loaders, which doesn’t come with webpack- prefix and used without writing import from node_modules .

Compare the following Webpack rules options with Rollup’s plugin option above:

// no import for loaders at the topmodule.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']

},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
],

},
{
test: /\.(png|jpe?g|gif)$/i,
use: 'file-loader'

},
]
},
};

The loaders above are applied using regex for the test options, which looks confusing and dirty as you add more rules for processing different file format.

Rollup has no built-in HMR support

One big feature that is missing from Rollup is the Hot Module Replacement or HMR, which allows you to swap your module code without having to refresh the browser.

When using HMR, the application state will not be reset because of browser refresh, and any changes in JS/CSS code will immediately be visible on the browser.

There is an unofficial Hot Reload plugin for Rollup, but it’s not maintained anymore. If you want HMR with Rollup, you can try the Nollup package. It’s an unofficial development server for Rollup that has HMR available.

Server-Side Rendering

I haven’t tried on setting up the configuration for Server-Side Rendering yet, but I think both bundlers need to compile the application in cjs format because it will be rendered using ReactDOMServer’s renderToString() method.

Once you understand how Rollup and Webpack works, rendering React app on the server should be similar between the two bundlers.

Conclusion

If you’re building React application from scratch, then you may want to go with Rollup because its configuration is easier to understand and you can use HMR with Nollup.

Webpack’s configuration is boring and hard to read, but it has more community support and official plugins. It also has built-in support for HMR, which is quite indispensable for front-end development today.

Build Great Design Systems and Micro Frontends

Take frontend development to the next level with independent components. Build and collaborate on component-driven apps to easily unlocks Micro Frontends, and to share components.

OSS Tools like Bit offer a great dev experience for building and composing independent components, and build solo or together with your team.

Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

--

--

Web Developer and Writer. Sharing what I learn on productivity and success.