DEV Community

Mohammad Faisal
Mohammad Faisal

Posted on • Edited on

6 Webpack Concepts for Advanced React Developers

To read more articles like this, visit my blog

More often than not, we use create-react-app for scaffolding a new React application and call it a day.

But under the hood, a lot of libraries are working to ensure everything just works. And webpack is the most important of them.

Today, we will take a deep dive into webpack and try to understand the different parts of webpack that are relevant to React development.

What’s the Problem?

We know that our browsers don’t understand anything but HTML, JavaScript, and CSS. So the obvious questions are.

  • How our browsers understand other files like .jsx

  • How are all the different files loaded into the browser?

  • How our application is bundled?

  • How we can optimize them?

Webpack to the Rescue

Webpack is here to save the day for us! It’s the most popular bundler out there and for obvious reasons!

What webpack does is convert everything into browser-readable files that are nicely depicted in the following image:

Source: [webpack](https://webpack.js.org/)

Why Should You Care?

Most of the time you wouldn’t. But the knowledge of webpack can help you in some advanced cases:

  • Scaffold an application from scratch

  • Performance optimization

  • Implement micro-frontends

Generally, having an overview of the bundling process makes you more confident about your craft and understand bugs in a production system.

How Webpack Works

Typically, webpack is configured through a file named webpack.config.js. This is where we can configure everything!

Let’s start with a simple configuration file and try to understand its various pieces.

Example

The following is a very simple configuration file for webpack:

const path = require('path')

module.exports = {

  entry: path.resolve(__dirname, 'src', 'index.js'),

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }

}
Enter fullscreen mode Exit fullscreen mode

Let's talk about entry and output.

1. Entry

entry is the entry point of your application. This is the place where webpack starts its journey. We know that our React applications are like a tree. All the components span out from App.js.

Photo by the author.

But where does our App.js live? It lives inside the index.js file, and that’s why webpack enters into the application via index.js and creates a dependency graph to determine which files are needed to be loaded first.

2. Output

This is another easy concept to understand. After all the work is done, webpack creates a bundle file. In output, we can specify the name and the location of the output file.

It has also other uses too. If you want to generate dynamic bundle names for the production system for version management you can do that here!

Let's take this a step further and try to understand how our .jsx or .css files are handled.

3. Loader

Loaders are a very important concept in webpack. They are like compilers. Webpack checks for a certain type of file and uses appropriate loaders to handle them.

A typical configuration of the loader can be like the following:

module.exports = {
    entry: { ... as before}
    output: { ... as before },

    module: {
        rules: [
            {
                test: /\.[jt]sx?$/, // matches .js, .ts, .jsx and .tsx files
                use: ['babel-loader'],, // uses babel-loader for the specified file types
                include: path.resolve(__dirname, 'src'),
                exclude: /node_modules/,
            }
        ],
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, look at the module part. It takes an array of rules. Each rule has several parts:

  • test -> It checks for a certain file type. It uses regular expression.

  • use -> It specifies the list of loaders used for this particular file type.

  • include -> Which files should be processed.

  • exclude -> Which should not be processed.

Sometimes we need more than one type of loader for a specific file. A good example is loading CSS files. The rule for that is:

{
    test: /\.css$/, // matches .css files only
    use: ['style-loader', 'css-loader'],
},
Enter fullscreen mode Exit fullscreen mode

Here, both css-loader and style-loader are used to process the file. One important thing to note here is these loaders are loaded in the reversed order.

That means first the css-loader will work and then style-loader will work on the output produced by css-loader.

Similarly, for our .scss files:

{
  test: /\.scss$/,
  use: ['style-loader', 'css-loader', 'sass-loader']
},
Enter fullscreen mode Exit fullscreen mode

4. Plugins

Plugins are another very important aspect of webpack. Plugins are the main reason why webpack is so powerful.

Plugins are like libraries. They can tap into any stage of the compilation process and do whatever they wish.

const {CleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = merge(common ,{
    entry:  {...},
    output: {...},
    module: {...}

    plugins: [ new CleanWebpackPlugin() ]
})
Enter fullscreen mode Exit fullscreen mode

In the example above, we used a plugin named clean-webpack-plugin. What this plugin does is clean the output folder each time we run our build command.

There are lots of plugins that you can take advantage of. You can refer to webpack’s official website if you are interested.

5. Mode

This is a simple concept to understand. You would want to use a different setup for your development and production. For this, you can use mode.

module.exports = merge(common ,{
    entry:  {...},
    output: {...},
    module: {...},
    plugins:{...},

    mode : 'development' or 'production'
})
Enter fullscreen mode Exit fullscreen mode

If you set the mode to production, then your output bundle will be minified and optimized.

6. Development Server

This is the final setup for your application. While developing the application, you will not want to compile it every time you change something. That’s why you need a devServer setup for your application.

module.exports = merge(common ,{
    entry:  {...},
    output: {...},
    module: {...},
    plugins:{...},
    mode :  {...},

    devServer: {
        contentBase: path.join(__dirname, 'public/'),
        port: 3000,
        publicPath: 'http://localhost:3000/dist/',
        hotOnly: true,
    },
})
Enter fullscreen mode Exit fullscreen mode

Now this setup will serve your application from the dist that you set up earlier as your output.

Conclusion

There you go. Now you should have a basic understanding of webpack and its different parts. Here is a full setup if you are interested:

const path =  require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
    entry: path.resolve(__dirname, 'src', 'index.js'),
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'bundle.js'
    },
    mode: 'development',
    module: {
        rules: [
            {
                test:  /\.[jt]sx?$/,
                use: ['babel-loader'],
                exclude: /node_modules/,
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.scss$/,
                use: ['style-loader', 'css-loader', 'sass-loader']
            },
            {
                test: /\.html$/,
                use: ['html-loader']
            }
        ]
    },
    plugins: [new CleanWebpackPlugin()],
    devServer: {
        contentBase: path.join(__dirname, 'public/'),
        port: 3000,
        publicPath: 'http://localhost:3000/dist/',
        hotOnly: true,
    }
}
Enter fullscreen mode Exit fullscreen mode

Have a great day!

Have something to say? Get in touch with me via LinkedIn or Personal Website

Top comments (1)

Collapse
 
webdiscus profile image
webdiscus

How our browsers understand other files like .jsx

The first "problem" in Webpack is "Entry". Webpack "understand" only JavaScript as an entry point.

But there is new powerful html-bundler-webpack-plugin what allows to specify an HTML template as an entry point.

All source scripts and styles can be referenced directly in HTML, similar to how it works in Vite.

<html>
<head>
  <!-- specify source style files -->
  <link href="./style.scss" rel="stylesheet">
  <!-- specify source script files here and/or in body -->
  <script src="./App.ts" defer="defer"></script>
</head>
<body>
  <h1>Hello World!</h1>
  <!-- specify source image files -->
  <img src="./map.png">
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

The plugin detects all source files referenced in a template and extracts processed assets to the output directory. In the generated HTML and CSS, the plugin substitutes the source filenames with the output filenames.

You can very easy define HTML templates as entry points in the entry option of the plugin.

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      // define a relative or absolute path to template pages
      entry: 'src/views/',
      // OR define templates manually
      entry: {
        index: 'src/views/home.html', // => dist/index.html
        'news/sport': 'src/views/news/sport/index.html', // => dist/news/sport.html
      },
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

It is also a huge developer experience improvement as many things can be inferred from the HTML.