Complete Guide to Webpack: From Basics to Practical Usage

  • webpack
    webpack
  • babel
    babel
  • javascript
    javascript
  • css
    css
Published on 2024/12/25

Introduction

Even if you work as a frontend engineer, Webpack might be one of those tools you don’t get to touch very often.

In most cases, it’s defined at the start of a project, so you don’t have many opportunities to deal with it frequently.

However, there are times when you’re forced to look at it, such as when upgrading package versions or when newly created code or assets (like images) in a new directory aren’t included in the build.

I myself recently upgraded from Webpack 4 to 5 and struggled quite a bit.

This article is meant to help you understand the basics of Webpack and prepare for the day when you’ll need that knowledge.

Official webpack site

https://webpack.js.org/

Webpack is a convenient tool that can be used in many environments, and it’s difficult to cover everything in a single article. For environments using React and TypeScript, I introduce them in a separate article.

https://shinagawa-web.com/en/blogs/webpack-react-typescript

Goal of this article

We’ll write simple HTML, JavaScript, and CSS, bundle them with Webpack, and confirm that they display correctly in the browser.

Image from Gyazo

We’ll add files one by one and check behavior with Webpack as we go, making this a tutorial that helps you get a feel for how to configure Webpack.

What is webpack?

webpack is a tool called a module bundler, mainly used to bundle JavaScript code and other resources (CSS, images, fonts, etc.) into a single bundle file.

This makes it possible to deliver web applications efficiently.

Specifically, Webpack does the following:

  1. Module bundling
    It bundles multiple JavaScript files into one large file or several smaller files (chunks). This reduces the number of resource requests and improves performance.

  2. Transpiling
    It converts ES6+ code, TypeScript, and even JSX (used in React) into formats supported by browsers.

  3. Asset management
    It can also process non-JavaScript resources such as images, fonts, and stylesheets. These may be converted into appropriate formats, and URLs can be automatically rewritten.

  4. Code splitting
    It can output different parts of an application (such as per page) as separate files, so code that doesn’t need to be loaded initially can be loaded later on demand.

  5. Plugins and loaders
    In Webpack, you can use plugins and loaders to customize how resources are processed and to optimize code. Loaders are used to transform files, while plugins extend the build process.

Starting webpack with the minimum setup

Let’s start using webpack right away and gradually get a feel for its strengths.

Create a directory called webpack-tutorial and initialize it as a Node.js project.

npm init

You’ll be asked several questions, but you can just keep pressing Enter.

Image from Gyazo

Installing webpack

Install webpack with the following command:

npm i -D webpack webpack-cli

Since we’ll use it from the CLI, we also install webpack-cli.

Preparing sample code

Prepare sample JavaScript code as src/index.js.

index.js
function component(name) {
  const element = document.createElement('div');

  element.innerHTML = `Hello ${name}`

  return element;
}

document.body.appendChild(component('World'));

Next, create an HTML file that loads this script and save it at the project root.

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>webpack tutorial</title>
  </head>
  <body>
    <script src="./src/index.js"></script>
  </body>
</html>

After creating it, open index.html in the browser and confirm that Hello World is displayed.

Image from Gyazo

Bundling JavaScript files with webpack

Now that we have our sample code, let’s bundle it with webpack.

npx webpack

A new file is created as /dist/main.js.

Image from Gyazo

Let’s check the file.

Before formatting

main.js
document.body.appendChild(function(){const e=document.createElement("div");return e.innerHTML="Hello World",e}());

After formatting

main.js
document.body.appendChild(
  (function () {
    const e = document.createElement('div');
    return (e.innerHTML = 'Hello World'), e;
  })()
);

Although the style differs from our sample code, it performs the same processing.

To make sure this code actually works, we’ll change the JS file loaded by the HTML and test it.

Change the save directory from the project root to /dist/index.html, and fix the JS file path.

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>webpack tutorial</title>
  </head>
  <body>
-     <script src="./src/index.js"></script>
+     <script src="./main.js"></script>
  </body>
</html>

Check again in the browser.

Since Hello World is displayed, we’ve confirmed that the file bundled by webpack works correctly.

Image from Gyazo

Up to this point, everything worked without any Webpack-specific configuration.

When using webpack on its own, the mechanism is actually not that difficult.

However, this alone isn’t enough for real-world use, so let’s dig into webpack a bit more.

Detecting file changes

webpack has a watch mode that detects file changes and automatically re-bundles.

npx webpack --watch

With this enabled, when you modify a JS file, it will be re-bundled and the changes will be reflected in the browser immediately.

index.js
- document.body.appendChild(component('World'));
+ document.body.appendChild(component('Test'));

Image from Gyazo

Supporting older browsers

Babel is a JavaScript compiler (transpiler) used to convert modern JavaScript code into a form that works in older browsers.

It’s mainly used to transform code that uses modern JavaScript features into versions compatible with older browsers.

  1. Transpiling (conversion)

    • Converts new syntax and features from ES6+ (ECMAScript 2015 and later) to ES5 (the version supported by older browsers).
    • Examples: converting const and let to var, converting arrow functions to regular functions.
  2. Providing polyfills

    • When new JavaScript features don’t work in older environments, Babel can add code to emulate those features via core-js or @babel/polyfill.
    • Examples: Promise, Array.includes, etc.
  3. Support for React and TypeScript

    • Can convert JSX (the special syntax used in React) into regular JavaScript.
    • Can also convert TypeScript code to JavaScript using Babel (though it does not perform type checking).
  4. Plugins and presets

    • Babel has an extensible system that lets you customize transformations via plugins and presets.
    • Presets: Collections of plugins (e.g., @babel/preset-env).
    • Plugins: Modules that handle specific transformations (e.g., @babel/plugin-transform-arrow-functions).

Official Babel site

https://babeljs.io/

In the code we used earlier, we used const, which is a feature supported from ES6 onward.

This causes problems in browsers that only support up to ES5.

So we’ll use Babel to convert the code to ES5.

Since we’ll still be bundling with webpack, we also need to integrate Babel with webpack.

Introducing Babel

Install Babel with the following command:

npm i -D @babel/core @babel/preset-env

@babel/preset-env is required for converting ES6 -> ES5.

Create a .babelrc file at the project root and specify the preset we just installed.

{
  "presets": ["@babel/preset-env"]
}

Integrating with webpack

In webpack, we use programs called loaders to configure transformations.

We’ll use babel-loader as the loader that converts ES6 -> ES5, so install it.

npm i -D babel-loader

Configure the loader in the webpack config.

webpack.config.js
/** @type {import('webpack').Configuration} */
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
      },
    ],
  },
};

With this configuration, .js files will be transformed using babel-loader.

Checking Babel’s behavior

Now that the setup is complete, bundle with webpack.

npx webpack

This time, you’ll see that the output JS file is different from before.

const is no longer used.

main.js
(() => {
  var e;
  document.body.appendChild((((e = document.createElement('div')).innerHTML = 'Hello '.concat('Test')), e));
})();

We’ve confirmed that the JS file is generated in ES5 format, so it will now work in older browsers.

Bundling CSS

We’ve looked at bundling JS files and supporting older browsers.

Next, we’ll make it possible to handle CSS with webpack.

Installing loaders

When importing CSS in JavaScript code and applying it as an HTML <style> tag, you need two loaders:

  1. css-loader
    Makes Webpack recognize CSS files and include them in the bundle.

  2. style-loader
    Inserts CSS into HTML as <style> tags.

npm i -D style-loader css-loader

Configuring loaders in webpack

Configure the two loaders to be used for .css files.

webpack.config.js
/** @type {import('webpack').Configuration} */
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
      },
+       {
+         test: /\.css$/,
+         use: ["style-loader", "css-loader"],
+       },
    ],
  },
};

When loaders are defined in an array, they are applied from the end of the array.

So they run in the order css-loader -> style-loader.

Preparing sample code

Create a CSS file at /src/styles.css.

styles.css
body {
  padding: 40px;
  color: blue;
}

Import the CSS file in the JavaScript file.

index.js
+ import './styles.css'

Checking behavior

Now that the setup is complete, bundle with webpack.

npx webpack

When you check in the browser, the text is blue, confirming that the CSS is applied correctly.

Image from Gyazo

Webpack modes

So far, a warning has been displayed when running webpack.

The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.

The mode option has not been set, so Webpack will fall back to production for this value.
By setting the mode option to development or production, you can enable defaults appropriate for each environment.

Up to now, bundling has been done in production mode.

In webpack 5, there are three possible settings:

  1. development
  2. production
  3. none

Let’s try running webpack in development mode.

npx webpack --mode=development

If you look at /dist/main.js, you’ll see that it now contains comments.

/***/ "./src/index.js":
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./styles.css */ \"./src/styles.css\");\n\nfunction component(name) {\n  var element = document.createElement('div');\n  element.innerHTML = \"Hello \".concat(name);\n  return element;\n}\ndocument.body.appendChild(component('Test'));\n\n//# sourceURL=webpack://webpack-tutorial/./src/index.js?");

From the comments, you can see that this is the bundled result of index.js.
Comments and other information that weren’t present in production mode are now included, which seems useful during development.

Conclusion

In this article, we’ve covered the basic usage and configuration of webpack.

Webpack is a very powerful and feature-rich tool, which can make its configuration feel a bit complex.

However, by learning it step by step, you can greatly improve both application performance and development efficiency.

Use the content introduced here as a reference to add the settings your own projects need, and make full use of webpack’s capabilities.

Recommended article

Here is an article on building a React + TypeScript environment with Webpack:

https://shinagawa-web.com/en/blogs/webpack-react-typescript

Xでシェア
Facebookでシェア
LinkedInでシェア

Questions about this article 📝

If you have any questions or feedback about the content, please feel free to contact us.
Go to inquiry form