Bundling in React

Published on July 11, 2022

Introduction

Bunding is the process of transforming source code into files that can be placed on a web server and used by web browsers. To acheive this, a bundler takes HTML, CSS and/or JavaScript files and related files to merge, split or minify(make the files as small as possible) the code into one file that can be easily loaded by browsers. In the case of JavaScript, bundlers processes the source code from one standard to another. Case in point, modules, introduced in the ES6 standard, allow you to write reusable pieces of code that can be imported/exported to any other files when needed. However, not all browsers support the import and export syntax. Bundling can be used to transform ES6 JavaScript into ES5 JavaScript so that the code can run even in old browsers.

Webpack is one such tool for building and bundling JavaScript applications. There exists othery options to choose from for your bundling needs: Parcel, Vite, Esbuild. This article covers Webpack.

Bundling

When webpack processes your application, it combines all the modules into one or more static bundles, that can be served to the browser as assets. These bundles include all the code needed to run the application, including all its dependencies.

Get Started

create a new directory. Inside of the directory, create subdirectories and files as shown below:

├── build
├── package.json
├── src
│   └── index.js
└── webpack.config.js

the package.json:

{
    "name": "project webpack",
    "version": ",
    "description": "a webpack project",

}

Step 1. Install webpack:

To use webpack, you’ll first need to install it.

$ npm i -D webpack webpack-cli

Step 2. Add The Configuration

In the webpack.config.js file in the root folder, add the following code:

const path = require("path")

const config = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "build"),
    filename: "main.js",
  },
}

module.exports = config
  • entry property defines where webpack will start when bundling the app. When webpack bundles the code, it includes all the imported code in the entry point as well as the import’s imports. And since some of the imports are packages, the bundled JavaScript code will also contain the contents of these libraries.
  • output property defines where the bundled code will be stored. This needs to be an absolute path, which is easy to create with Node’s path.resolve method. __dirname is a global variable in Node that returns the absolute path of the current JS file.

To execute the bundling, define an npm script :

package.json
{
    ...
    "scripts": {
        "build": "webpack --mode=development"
    }
}

The mode parameter can be set to either development, production or none. It helps to enable webpack’s built-in optimizations for each of the environments.

Step 3. Add an entry point

Webpack builds your application by starting at a single entry point and following the dependencies of that file. In the configuration above, we’ve specified that the entry point is src/index.js

So, you’ll need to create an index.js file in the src directory. This can be an empty file for now.

Step 4. Run Webpack

With the build script in place, run:

$ npm run build

This should produce a new /build directory with a main.js file inside it.

Webpack in React

To convert the above app into a react app, install react:

$ npm i react react-dom

Add The Entry Point

Create an index.js file. This will be the entry point for your React application, and it should look something like this:

import React from 'react
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(<App />)

Add an HTML Template file

In the /build folder, create an index.html file to serve as the “main page” for the app. The main page loads the JavaScript code through a script tag:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Webpack Project</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="./main.js"></script>
  </body>
</html>

Loaders

Bundling the app at this point results in an error. By default, Webpack only knows how to deal with plain JS. React, however, uses a lot of JSX, which might look like JS but isn’t:

src/app.js
const App = () => {
    return (
        <div>Hello webpack..</div>
    )
}

To fix this, we use loaders to tell Webpack of the files that need “processing” before attempting to bundle them, and what tool to use to process these files. Babel is a tool that converts your React’s JSX into JavaScript that Webpack can understand.

$ npm i -D @babel/core babel-loader @babel/preset-react

This will install the necessary Babel packages as dev dependencies for your project. Update the configuration file webpack.config.js:

...
const config = {
    ...
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-react']
                }
            }
        ]
    }
}

The loader configuration consists of 3 parts:

  • test property specifies the type of files the loader is for. In this case, it’s for .js files
  • loader property specifies which loader will process the files that meet the test
  • options property and is used to specify any extra parameters for the loader

The bundling process is now successful when npm run build is executed. To test the bundled app, open /build/index.html on the browser.

Transpilers

Transpiling is the process of transforming source code from one language to another. Babel transforms source code from one form of JavaScript to another. For instance, since most browsers do not support the newer JavaScript standards, ES6, ES7 and so forth, babel can transpile the code into the older ES5 standard.

Babel transpilation is enabled by applying plugins or presets to your configuration file. The [documentation has excellent coverage of plugins.

In the current config file, we only have one preset, @babel/preset-react:

Add @babel/preset-env as well:

$ npm i -D @babel/preset-env

This preset contains everything to transform any new JavaScript features into the old ES5 standard. Update the babel loader configuration:

...
options: {
    presets: ['@babel/preset-env', '@babel/preset-react']
}
...

babel’s presets are excuted in a last to first order. This means the react preset is run first, followed by the env preset.

The following result is the of transpiling simple JSX code: transpiled code

CSS in Webpack

Trying to add CSS to the app and running the npm run build command results in the loader error: css-webpack-error

When using CSS, we need to include a css loader and a style loader:

$ npm i -D css-loader style-loader

In the config file’s rules object, insert the following:

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

css-loader loads the css files and resolves special css syntax like imports and url(). style-loader takes the transformed CSS and injects it into the DOM by adding a <style>tag in the page's` section.

Now npm run build works, and the CSS definitions are added to the `main.js“ file together with the JavaScript

To extract the css into a separate file, mini-css-extract-pugin helps with that

Using webpack-dev-server

Currently, any changes to the code warrant a rerun of the npm run build command and a manual refresh of the page. Webpack-dev-server helps to improve your work workflow by providing a web server and the ability to use live reloading. To set it up:

$ npm i -D webpack-dev-server

In the configuration file, add a new property to tell the dev server where to look for files:

const config = {
    // ...
    devServer: {
    static: path.resolve(__dirname, "build"),
    compress: true,
    port: 3000,
  },
    // ...
}

Add a script for starting the dev server:

package.json
"scripts": {
    //...
    "start": "webpack serve --mode=development"
}

npm start command will start the dev server in the specified port 3000, i.e. http://localhost:3000 Updates to the code causes the browser to autorefresh the page. When the dev server bundles the code, it does not write into the output file main.js but instead, the bundled results are kept only in memory.

Minification

Minification refers to the optimization process of any source code e.g. by removing unnecessary characters and comments. By running webpack in production mode, you can reduce the size of your main.js file by a significant percentage:

"scripts": {
    "build": "webpack --mode=proudction"
    // ...
}

This helps to improve the performance of your application by reducing the amount of data that needs to be transmitted over the network and parsed by the browser, i.e. the bundle size.

Summary

React and webpack are powerful tools that can be used together to build and optimize modern JavaScript applications. In this post, we covered how to use webpack to manage your dependencies, and optimize your assets.