DEV Community

Cover image for webpack adoption guide: Overview, examples, and alternatives
Megan Lee for LogRocket

Posted on • Originally published at blog.logrocket.com

webpack adoption guide: Overview, examples, and alternatives

Written by John Reilly
✏️

If you're a JavaScript developer, you've probably heard of webpack, a JavaScript bundler that helps you bundle your code into a single file. It's a great tool for optimizing your code and improving performance.

This article will give you an overview of webpack, its history, and how it works. The goal is to help you decide if it’s the right tool to adopt for your next JavaScript project.

It'll be a little different than your typical "what is webpack?" article in that I write this as the maintainer of ts-loader, a loader used for integrating TypeScript with webpack. I've worked in the webpack ecosystem for some years now, and I'll share some of my experiences with you.

I'll start with a little history around bundling, aiming to convey why webpack came to be such a popular choice. If you’d like, you can jump ahead to when we start talking about the rise of the module bundler or what webpack is and how to use it.


A short history of web development

To answer the question "what is webpack?", we need to understand what a bundler is. To grasp that, we first need a little history lesson.

The early days of JavaScript

If you started web development after 2016, you might not realize that bundling is a relatively new concept. Let's roll back the clock to the late 2000s, when JavaScript development started to go mainstream.

Around that time, jQuery was becoming popular and the web was a very different place. We didn't have the same tools we have today — not npm, webpack, React, TypeScript, or even ES2015. We were still writing JavaScript in ES5.

So what did building a frontend application look like back then? Well, I was a web developer, and I can tell you that it was a lot of work. To make a simple app, you would have to:

  • Go to the websites of libraries you wanted to use, usually jQuery and jQuery UI
  • Download the library files you needed — both jquery-1.4.4.js and the minified jquery-1.4.4.min.js because there weren't minification tools back then
  • Include the library files in your HTML file using script tags, and significantly before other JavaScript files that would depend upon jQuery
  • For bonus points, you would also download the jQuery UI CSS files and include them in your HTML file
  • For extra bonus points, you would figure out a way to serve up non-minified versions of your JavaScript files in development, and minified versions in production

Not only was this process a lot of work, but it was also very error-prone. If you forgot to include a library, included it in the wrong order, included the wrong version, forgot to minify your files, or forgot to include the CSS files, your app would break and it would be very difficult to debug.

Rise of the task runners

As web development became more popular, people started to realise that the process was fairly repetitive, so they started to automate it. Around 2012, we started to see the birth of the task runner. There were two main task runners that became popular: Grunt and Gulp.

These task runners allowed you to automate the process of combining and minifying JavaScript and CSS files, and including them in your HTML file. They also allowed you to automate other tasks, like running tests, linting your code, and deploying your app.

While task runners did improve the web development experience, they didn't solve all the problems. It was still very easy to make mistakes. You could still forget to include a library, include it in the wrong order, get a path wrong, forget to minify your files, or forget to include the CSS files. Also, it was still very difficult to debug.

Still, task runners made the process so much better than what we had before, so they became very popular.

The rise of the module bundler

Around 2014, a new tool started to become popular: the module bundler. A module bundler is a tool that allows you to write your code in modules and then bundle those modules into a single file. It also allows you to use other tools, like TypeScript, and CSS preprocessors like Sass and Less.

That's a lot of words — let's unpack them a little.

For some time, the defacto way of acquiring JavaScript libraries has been through npm, a package manager for JavaScript. However, it’s important to remember that npm started out as the package manager for Node.js.

npm was originally used to house packages that were used to build Node.js applications. It was never intended to be used for frontend development. In fact, for a while there was an alternative frontend package manager called Bower.

The thing is, there's a lot of commonality between Node.js and frontend development. For example, both use JavaScript, and you're unlikely to need to run a web server in the browser.

However, whether running in a browser or on a server, you might want to order an array with lodash, or make use of TypeScript, or perform validation with Zod. So, it makes sense to use the same package manager for both.

The first tool that tackled this was Browserify. As the name suggests, it was a tool that allowed you to use Node.js style modules in the browser. It did this by taking your code and recursively walking through it, finding all the require calls, and bundling them into a single file.

By doing this, Browserify performed two useful functions, both of which are tremendously significant:

  1. It opened up the ecosystem of Node.js packages to frontend developers. This made available a rich ecosystem of modules that can be used to speed up the task of web development
  2. It allowed you to write your code in modules, which made code easier to reason about

The value of modularity is less obvious, but still very important. It's worth remembering that JavaScript didn't have modules until ES2015. However, npm had its own module standard called CommonJS.

Given that Browserify and webpack were both created before ES2015, they both used CommonJS modules in the context of the browser. This was a huge improvement over the previous way of doing things, which was to include a bunch of script tags in your HTML file and write all your code in a giant global object.

The reason this improvement was so wildly different was because the dependencies in your codebase moved from being implicit to being explicit. Instead of having to remember to include a bunch of script tags in your HTML file, you could just require the modules you needed.

This approach made it much easier to reason about your codebase. What's more, you had a package.json file that listed all your dependencies, so you could see at a glance what your dependencies were.

Further reading:


What is webpack?

Now we understand a little of the history, we come to webpack. By the way, it's definitely not "Webpack" or "WebPack" — it's "webpack".

The person initially behind webpack is Tobias Koppers, an engineer from Germany. Many, many people have contributed to the project since then, but Tobias is the person who has done the most work on it.

I mentioned that I was a web developer while the web was evolving its developer tooling. In my case, I was a longtime user of Gulp, and then Browserify. I moved to webpack in 2015.

I can't remember exactly why I moved, but I think it was because I wanted to use TypeScript, and webpack had better TypeScript support than Browserify. We’ll see more on this later.

I also think I was attracted to webpack because it was a more holistic solution than Browserify. It had a plugin system, and it had loaders. I'll talk about those in a moment as well.

First and foremost, it's worth saying that webpack is a module bundler. It takes your code and recursively walks through it to:

  • Find all the require or import calls
  • Build up a dependency graph
  • Perform preprocessing tasks
  • Produce a runnable output in the form of HTML, CSS, and JavaScript

It also allows you to use other tools, like TypeScript, and CSS preprocessors like Sass and Less.

Two of the most surprising things about webpack are its popularity and longevity. The web development world is famous for having the attention span of a distracted toddler. Tools replace tools, libraries replace libraries, and frameworks replace frameworks.

But webpack has been around for a long time, and it's still the most popular bundler. At the time of this writing it still has 110 million downloads a month. That's a lot! Why is that?

I think there are a few reasons.

Firstly, webpack’s rich ecosystem and flexibility makes it possible to solve pretty much all web development problems. There are newer, shinier, faster tools starting to displace webpack, but as a reliable tool that can solve all your problems, it’s hard to beat.

That doesn't mean it's the easiest tool to work with on all occasions. The internet is awash with people bitterly complaining about the scars they bear from configuring webpack.

It's true that webpack can be difficult to configure. But it's also true that webpack is a very powerful tool. Once you have it working, you generally don't have to touch it again.

Secondly, webpack has become a "primitive," by which I mean that it has become a library that other libraries depend upon. For example, if you use Docusaurus, you're also using webpack as the underlying build tool. Many projects that need a build tool have likewise picked webpack for this purpose.

This has led to a huge ecosystem of plugins and loaders that utilize webpack. It's also led to a plethora of tutorials and blog posts related to webpack. If you have a problem, it's likely that someone else has had the same problem and has written a blog post about it.


Getting started with webpack

As you might expect from such a big project, webpack’s documentation is quite comprehensive. While we won't go through every scenario and use case of webpack in this guide, we want to give you a sense of what working with webpack looks like.

For the purposes of this article, let's get started with a simple example. We'll create a simple "Hello, webpack" app and enrich it as we go through the piece. First, let's make a folder, create a package.json file, and install the webpack dependencies we need:

mkdir hello-webpack
cd hello-webpack
npm init -y
npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev
Enter fullscreen mode Exit fullscreen mode

The dependencies we're installing are:

  • webpack
  • webpack-cli — A command line interface for webpack
  • webpack-dev-server — A development server that allows you to serve up your app in a browser
  • html-webpack-plugin — A plugin that allows you to generate an HTML file that includes your bundled JavaScript files

Configuration with webpack.config.js

While it’s possible to use webpack without configuring it, it's more typical to have a configuration file. If you’re using a single configuration file, this file is often called webpack.config.js.

It's also common to have more than one configuration file. For example, you might have one for development and another for production.

In our case, we'll create a single webpack.config.js to use with our example app:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development', // mode can be "development", "production" or "none" https://webpack.js.org/configuration/mode/
  entry: './src/index.js', // the entry point of our app https://webpack.js.org/concepts/entry-points/
  devtool: 'inline-source-map', // the type of sourcemap to generate for debugging https://webpack.js.org/configuration/devtool/
  plugins: [
    new HtmlWebpackPlugin(), // a plugin to generate an HTML file https://github.com/jantimon/html-webpack-plugin
  ],
  output: {
    // where to put the bundled output https://webpack.js.org/concepts/output/
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },
};
Enter fullscreen mode Exit fullscreen mode

This may seem a little overwhelming, so we'll through this configuration file property by property in a moment.

One thing that might puzzle you is the absence of a module section to cover loaders. This is because webpack supports processing JavaScript by default. We'll add a module section later when we want to process other types of files.

Further reading:

mode

This is the mode that webpack will run in. It essentially tells webpack to provide helpful defaults around how builds are performed.

The mode can be development, production or none. We're using development because we're developing locally. If we were building for production, we'd use production.

Incidentally, we can override this on the command line with the --mode flag. We’ll explore more on this later.

entry

This is the entry point of our app. It's the file that webpack will start with. In this case, it's src/index.js. It is possible to have multiple entry points, but we'll keep it simple for now.

devtool

This is the type of sourcemap that webpack will generate. There are many different types of sourcemap, and they all have different tradeoffs.

We're using inline-source-map because we're developing locally and we'd like to be able to debug our source code in the browser. If we were building for production, we might make a different choice.

plugins

This is a list of plugins that we want to use. We're using the HtmlWebpackPlugin to generate an HTML file that includes our bundled JavaScript files.

We'll talk more about plugins later.

output

This is where we want webpack to put the bundled output.

We're using dist as the folder name. We're also using a [name].[contenthash].js naming convention for our bundled JavaScript file. This means that webpack will generate a file called main.[contenthash].js in the dist folder.

The [contenthash] part is a hash of the file’s contents. This means that if the contents of the file change, the hash and filename will change accordingly. This is useful because we can cache the file for a long time, and if the contents change, the filename will change and the browser will download the new file.

We're also providing the clean: true option, which deletes the contents of our dist folder on each build.

Setting up some simple code

What we need now is some code to bundle. Let's create a src folder and a src/index.js file, our first JavaScript file:

function app() {
  const element = document.createElement('div');

  element.innerHTML = 'Hello, webpack';

  return element;
}

document.body.appendChild(app());
Enter fullscreen mode Exit fullscreen mode

Local development with webpack-dev-server

Now we're going to add two scripts to our package.json file. One to build our app, and one to serve it up in a browser while we're developing it:

 "scripts": {
    "build": "webpack build --mode production",
    "start": "webpack serve --open"
  },
Enter fullscreen mode Exit fullscreen mode

With this in place, we can develop locally with npm start. This will serve up our app in a browser at http://localhost:8080/ using webpack-dev-server:

> hello-webpack@1.0.0 start
> webpack serve --open

<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
<i> [webpack-dev-server] On Your Network (IPv4): http://172.30.170.28:8080/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8080/
<i> [webpack-dev-server] Content not from webpack is served from '/Users/jreilly/code/github.com/hello-webpack/public' directory
<i> [webpack-dev-middleware] wait until bundle finished: /
asset main.4d4379bc3adfa037dc27.js 621 KiB [emitted] [immutable] (name: main)
asset index.html 252 bytes [emitted]
runtime modules 27.3 KiB 12 modules
modules by path ./node_modules/ 178 KiB
  modules by path ./node_modules/webpack-dev-server/client/ 71.8 KiB 16 modules
  modules by path ./node_modules/webpack/hot/*.js 5.3 KiB
    ./node_modules/webpack/hot/dev-server.js 1.94 KiB [built] [code generated]
    ./node_modules/webpack/hot/log.js 1.86 KiB [built] [code generated]
    + 2 modules
  modules by path ./node_modules/html-entities/lib/*.js 81.8 KiB
    ./node_modules/html-entities/lib/index.js 7.91 KiB [built] [code generated]
    ./node_modules/html-entities/lib/named-references.js 73 KiB [built] [code generated]
    ./node_modules/html-entities/lib/numeric-unicode-map.js 339 bytes [built] [code generated]
    ./node_modules/html-entities/lib/surrogate-pairs.js 537 bytes [built] [code generated]
  ./node_modules/ansi-html-community/index.js 4.16 KiB [built] [code generated]
  ./node_modules/events/events.js 14.5 KiB [built] [code generated]
./src/index.js 163 bytes [built] [code generated]
webpack 5.89.0 compiled successfully in 861 ms
Enter fullscreen mode Exit fullscreen mode

If we open the browser at http://localhost:8080, we'll see our "Hello, webpack" message.

Building for production

We can build our app for production with npm run build:

npm run build

> hello-webpack@1.0.0 build
> webpack build --mode production

asset main.82d3f64b186c8eec8e7c.js 862 bytes [emitted] [immutable] [minimized] (name: main)
asset index.html 235 bytes [emitted]
./src/index.js 163 bytes [built] [code generated]
webpack 5.89.0 compiled successfully in 516 ms
Enter fullscreen mode Exit fullscreen mode

This has created a dist folder and a dist/index.html file. Alongside that, it's created a dist/main.82d3f64b186c8eec8e7c.js file. If you open the index.html file in a browser, you'll see your "Hello, webpack" message.

At this point we have a simple app built with webpack. It's not doing much, but it's a start — and it'll give us a chance to talk about some other webpack-related concepts. Let's add some more features.


Integrating webpack with plugins and loaders

If you want to do anything more than the most basic of apps, you'll need to use plugins and loaders with webpack. Let's add some more features to our app using plugins and loaders.

Loaders

Loaders allow webpack to process other types of files — for example, TypeScript — and convert them into valid modules that can be consumed by your application and added to the dependency graph. An example of a loader is ts-loader, which allows you to use TypeScript with webpack.

I should not brush past this — I'm the primary maintainer of ts-loader, and I'm very proud of it. It gets around 30 million downloads a month at the time of this writing, which suggests that roughly a quarter of webpacks users are also ts-loader users.

ts-loader is a great loader, and I'm very happy to have worked on it since 2016. Let's install ts-loader and TypeScript, then create a tsconfig.json file:

npm install typescript ts-loader --save-dev
npx tsc --init
Enter fullscreen mode Exit fullscreen mode

Now we need to configure webpack to use ts-loader. We do this by updating our entry point to be a TypeScript file and adding a module section to our webpack.config.js file:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development', // mode can be "development", "production" or "none" https://webpack.js.org/configuration/mode/
-  entry: './src/index.js', // the entry point of our app https://webpack.js.org/concepts/entry-points/
+  entry: './src/index.ts', // the entry point of our app https://webpack.js.org/concepts/entry-points/
  devtool: 'inline-source-map', // the type of sourcemap to generate for debugging https://webpack.js.org/configuration/devtool/
  plugins: [
    new HtmlWebpackPlugin(), // a plugin to generate an HTML file https://github.com/jantimon/html-webpack-plugin
  ],
+  module: {
+    rules: [
+      {
+        test: /\.([cm]?ts|tsx)$/,
+        loader: 'ts-loader',
+      },
+    ],
+  },
  output: {
    // where to put the bundled output https://webpack.js.org/concepts/output/
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },
};
Enter fullscreen mode Exit fullscreen mode

The test property is a regular expression that matches the files we want to process. In this case, we're matching .ts, .tsx, and .cts files. The loader property is the name of the loader we want to use — in our case, ts-loader.

Let's rename our src/index.js file to src/index.ts and change the code to use TypeScript:

function app(): HTMLDivElement {
  // the only TypeScript change we made is to add a return type
  const element = document.createElement('div');

  element.innerHTML = 'Hello, webpack';

  return element;
}

document.body.appendChild(app());
Enter fullscreen mode Exit fullscreen mode

And just like that, we can use TypeScript in our app!

What is ts-loader actually doing? Well, it's taking our TypeScript code, and converting it into JavaScript.

For each TypeScript file, ts-loader is invoked. It takes the TypeScript code, and passes it to the TypeScript compiler. The TypeScript compiler converts the TypeScript code into JavaScript.

ts-loader then takes the JavaScript code and passes it to webpack. webpack then takes the JavaScript code and bundles it.

This is what all loaders do: they take a file, process it, and pass it to webpack. There are many loaders available, and you can even write your own. They aren't restricted to languages that compile to JavaScript.

Plugins

Plugins allow you to do all kinds of things with webpack. The definition in the webpack documentation is delightfully broad:

“Plugins are the backbone of webpack. [...] They serve the purpose of doing anything else that a loader cannot do.”

This is helpful when you remind yourself that a loader takes a file, processes it, and passes the output of that processing to webpack. It is single-file-oriented, if you like. A plugin is what you use when you want to do something that isn't single-file-oriented.

So maybe it's easier to give you some examples. We already have one plugin in our app — the HtmlWebpackPlugin. This plugin generates an HTML file that includes our bundled JavaScript files.

Further reading:

DefinePlugin

Let's add another plugin to our app. We'll add the DefinePlugin, which allows you to define global constants that can be used in your code. Let's add it to our webpack.config.js file:

const path = require("path");
+const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: './src/index.ts', // the entry point of our app https://webpack.js.org/concepts/entry-points/
  devtool: "inline-source-map",
  plugins: [
      new HtmlWebpackPlugin(),
+      new webpack.DefinePlugin({
+        'MODE': JSON.stringify("PRODUCTION"),
+      })
  ],
  module: {
    rules: [
      {
        test: /\.([cm]?ts|tsx)$/,
        loader: 'ts-loader',
      },
    ],
  },
  output: {
    filename: "[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },
};
Enter fullscreen mode Exit fullscreen mode

With the change above, we've added the DefinePlugin to our list of plugins. We've also defined a global constant called MODE that will be available in our code. We've set the value of MODE to be the value of the NODE_ENV environment variable. We'll use this in our code in a moment.

Let's update our src/index.ts file to use the MODE constant, and tell TypeScript about it:

+declare const MODE: string;

function app(): HTMLDivElement {
  const element = document.createElement("div");

-  element.innerHTML = 'Hello, webpack';
+  element.innerHTML = `Hello, webpack, we are in ${MODE} mode.`;

  return element;
}

document.body.appendChild(app());
Enter fullscreen mode Exit fullscreen mode

Now if we build our app, we'll see that the MODE constant is available in our code, and we can use it. At runtime, it will be replaced with the value we defined in our webpack.config.js file, and our app will say:

Hello, webpack, we are in PRODUCTION mode.
Enter fullscreen mode Exit fullscreen mode

fork-ts-checker-webpack-plugin

Before we move on, let's add one more plugin to our app. fork-ts-checker-webpack-plugin allows you to run the TypeScript compiler in a separate process and relieve ts-loader of the responsibility of handling type checking. This is useful because it means that webpack can run in parallel with the TypeScript compiler, which can significantly speed up your build times.

I have worked on this plugin, as it has a sibling relationship with ts-loader. It's quite common to use both together — ts-loader to compile your code, and fork-ts-checker-webpack-plugin to type check it.

Let's install fork-ts-checker-webpack-plugin:

npm install fork-ts-checker-webpack-plugin --save-dev
Enter fullscreen mode Exit fullscreen mode

And let's configure it in our webpack.config.js file:

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = {
  mode: "development",
  entry: './src/index.ts', // the entry point of our app https://webpack.js.org/concepts/entry-points/
  devtool: "inline-source-map",
  plugins: [
      new HtmlWebpackPlugin(),
      new webpack.DefinePlugin({
        'MODE': JSON.stringify('PRODUCTION'),
      }),
+      new ForkTsCheckerWebpackPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.([cm]?ts|tsx)$/,
        loader: 'ts-loader',
+        // we only need to explicitly specify transpileOnly option if you use ts-loader < 9.3.0
+        options: {
+          transpileOnly: true
+        }
      },
    ],
  },
  output: {
    filename: "[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },
};
Enter fullscreen mode Exit fullscreen mode

We can see here that we're providing some configuration to ts-loader by setting it to transpileOnly: true. This means that ts-loader will only transpile our code and will not type check it.

We're also adding the ForkTsCheckerWebpackPlugin to our list of plugins. This will run the TypeScript compiler in a separate process and perform type checking there.

It's worth noting that because of this excellent PR by the primary maintainer Piotr Oleś, we don't need to explicitly specify transpileOnly: true anymore. It's the default behaviour of ts-loader when the fork-ts-checker-webpack-plugin is detected. I've left it in the example above to illustrate what configuring a loader looks like.

To test that this is working, let's add a type error to our src/index.ts file:

declare const MODE: string;

function app(): HTMLDivElement {
  const element = document.createElement("div");

  element.innerHTML = `Hello, webpack, we are in ${MODE} mode.`;

-  return element;
+  return elemen;
}

document.body.appendChild(app());
Enter fullscreen mode Exit fullscreen mode

And let's build our app:

npm run build

> hello-webpack@1.0.0 build
> webpack build --mode production

assets by status 1.09 KiB [cached] 2 assets
./src/index.ts 204 bytes [built] [code generated]

ERROR in ./src/index.ts:8:10
TS2552: Cannot find name 'elemen'. Did you mean 'element'?
     6 |   element.innerHTML = `Hello, webpack, we are in ${MODE} mode.`;
     7 |
  >  8 |   return elemen;
       |          ^^^^^^
     9 | }
    10 |
    11 | document.body.appendChild(app());

webpack 5.89.0 compiled with 1 error in 1710 ms
Enter fullscreen mode Exit fullscreen mode

We can see that the TypeScript compiler has picked up our type error. If we remove the fork-ts-checker-webpack-plugin from our list of plugins, we'll see that the type error is no longer picked up:

npm run build

> hello-webpack@1.0.0 build
> webpack build --mode production

asset main.a1d11e49d0129cad93aa.js 885 bytes [emitted] [immutable] [minimized] (name: main)
asset index.html 235 bytes [emitted] [compared for emit]
./src/index.ts 204 bytes [built] [code generated]
webpack 5.89.0 compiled successfully in 772 ms
Enter fullscreen mode Exit fullscreen mode

So, we can see that the fork-ts-checker-webpack-plugin is working.

We've seen a number of examples of plugins. Almost all customization of webpack is done through plugins and loaders. If you want to do something with webpack, it's likely that there's a plugin or loader that will help you do it.

Further reading:


webpack and the competition

There has been a lot of competition in the bundler space. For a long time, webpack has been the most popular bundler. But it's not the only game in town, and never has been.

It's beyond the scope of this article to do a full comparison of webpack and its competitors, but let’s take a quick look.

For the longest time, webpack has been the most popular bundler. Apparently incapable of being dislodged from that position. However, it looks like that might be changing.

If we look at the npm download stats for webpack for the last five years, we can see that, for the first time, its popularity is starting to decrease. It's still the most popular bundler, but it's starting to decrease in popularity and competitors are starting to increase.

This chart compares the npm download stats for webpack, esbuild, SWC, and Vite over the last five years: Line Graph Comparing The Number Of Downloads Each Year Since 2019 For Swc, Esbuild, Vite, And Webpack Vite is a bundler that came out of the Vue ecosystem. It's a very fast bundler that uses esbuild under the hood. esbuild is a bundler that came out of the Go ecosystem. swc is a super-fast TypeScript and JavaScript compiler written in Rust. All of these compete with webpack in some way.

The thing to note about all these competitors is that they are all faster than webpack. There's a reason for that: webpack is written in JavaScript, which — for most of the history of bundlers — was what bundlers were implemented in. But the next generation of tools are written in other languages.

esbuild is written in Go. swc is written in Rust. These languages have allowed massively improved performance. In terms of speed, webpack cannot compete with these tools.

It's worth noting that even the creator of webpack, Tobias Koppers is now working on a Rust-based successor to webpack named Turbopack.

Speed is a very attractive proposition, and as we can see, we're starting to see the community move away from webpack. It's not a mass exodus — when people are starting new projects now, they're more than likely to use one of the newer tools.

When I've started a new project over the last year I've tended to use Vite. I've not used webpack for a new project for a long time, and I'm not alone in this.

Next-generation tools keep appearing. Bun is an alternative JavaScript runtime implemented in Zig. It also ships with its own built-in Bun bundler, which is reportedly even faster than esbuild and Rspack! We're likely to see even more of these tools in the future.

Further reading:


Conclusion

In this article, we looked at what webpack is and why it's so popular.

We started by reviewing its history and how it came to be the most popular bundler. Then, we examined how to get started with webpack, looking at some important high-level concepts such as plugins and loaders.

To close out, we also considered some of the competition and where the bundler space is likely heading. To be clear: webpack is not going anywhere. But it's fair to say that it is starting to be displaced by some of the newer tools. This trend is only going to continue.


Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.

NPM:

$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
Enter fullscreen mode Exit fullscreen mode

Script Tag:

Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Enter fullscreen mode Exit fullscreen mode

3.(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • ngrx middleware
  • Vuex plugin

Get started now

Top comments (2)

Collapse
 
webdiscus profile image
webdiscus

Instead of the html-webpack-plugin you can try to use the modern and powerful html-bundler-webpack-plugin.
The HTML bundler works like Vite. You can define source style and script files directly in HTML template. The plugin supports many template engines (EJS, Pug, Twig, Handlebars, Nunjucks, and other) out of the box.

Collapse
 
pengeszikra profile image
Peter Vivo

Great summarizing of bundler history. My tipp for vite is works a little better for NPM react module bundling. react-state-factory ts npm module used by vite