6 ways to configure Webpack

There are webpack plugins to also compile, minify, shim, chunk, and bundle code. However, webpack was not designed to execute tasks such as linting, building, or testing your app. For this purpose, there are task runners such as Grunt, Gulp or npx.

In order to manage the functionality of webpack, it must be configured. Here are six different ways, in which webpack's configuration can be written.

1. Zero Config

As of webpack version 4, you are not required to specify a configuration. By default, webpack assumes that your code starts at src/index.js and will be bundled to dist/main.js. This is very convenient and promotes convention over configuration but it does not use webpack's full potential.

Without a configuration, webpack does not know whether code should be compressed for faster execution or bundled with source maps for better tracking of errors. Webpack expresses its confusion with the following warning:

WARNING in configuration

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.

You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

Let's have a look at options to tell webpack how it should be configured.

2. Command Line Interface

To see all available commands and options to configure webpack from the command line interface, you can run webpack --help. This command will show you a list of arguments and how to use them. The following execution mimics the default (zero config) behaviour of webpack:

webpack --entry=./src/index.js --output-path=./dist --output-filename=main.js

As you can see, CLI configurations can become quite long. In order to minimize the writing effort, there is also a shorthand version of the above command:

webpack ./src/index.js -o ./dist

The simplified notation is at the expense of comprehensibility, which is why we will look at configuration files in the next step.

3. CommonJS Configuration File

Webpack can be instructed to read in a configuration file. By default, a file named webpack.config.js is being used. You can create it by using the npx webpack init command or by writing it yourself:

webpack.config.js

const path = require("path");

const config = {
  entry: "./src/index.js",
  mode: "development",
  module: {
    rules: [
      {
        exclude: /(node_modules)/,
        test: /\.(js|jsx)$/i,
        loader: "babel-loader"
      }
    ]
  },
  output: {
    path: path.resolve(__dirname, "dist")
  },
  plugins: []
};

module.exports = config;

The configuration uses the CommonJS module syntax with require and module.exports. Make sure that your package.json does not define "type": "module", otherwise you will receive the following error:

[webpack-cli] ReferenceError: require is not defined

The configuration file should also be in the root of your project.

4. ESM Configuration File

If your package.json file specifies "type": "module" and you want to make use of ECMAScript modules, then you can also modernize your webpack configuration:

webpack.config.js

import path from "path";

const config = {
  entry: "./src/index.js",
  mode: "development",
  module: {
    rules: [
      {
        exclude: /(node_modules)/,
        test: /\.(js|jsx)$/i,
        loader: "babel-loader"
      }
    ]
  },
  output: {
    path: path.resolve("./dist")
  },
  plugins: []
};

export default config;

5. TypeScript Configuration File

For those of you who like to work with TypeScript, webpack offers the possibility to use a configuration file written in TypeScript.

Webpack v5 already ships with TypeScript definitions, so you don't have to install @types/webpack but you need to install typescript, ts-node and @types/node.

Because the extension .ts does not correspond to the standard .js extension, webpack has to be informed about this via the --config argument:

webpack --config webpack.config.ts

You also have to make sure that the test patterns of your "rules" and your "resolve" definitions include the TypeScript extension:

webpack.config.ts

import path from "path";
import { Configuration } from "webpack";

const config: Configuration = {
  entry: "./src/index.js",
  mode: "development",
  module: {
    rules: [
      {
        exclude: /(node_modules)/,
        test: /\.[tj]sx?$/,
        loader: "babel-loader"
      }
    ]
  },
  output: {
    path: path.resolve(__dirname, "./dist")
  },
  plugins: [],
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx"]
  }
};

export default config;

☝️ Because the exemplary webpack configuration loads Babel, we can still point to a JavaScript entry file as Babel makes it possible to use JavaScript and TypeScript code simultaneously.

⚠️ Please note that TypeScript configuration files cannot be used with ESM (see ESM in webpack.config.ts isn't supported).

6. Node Interface

In addition to the execution via webpack-cli, webpack also supports a programmatic interface. This allows you to compile your frontend code on a Node.js server. Here is an example:

import express from "express";
import { webpack } from "webpack";
import webpackConfig, { webappDir } from "../webpack.config.js";

export function useWebpack(app: express.Express) {
  const webpackCompiler = webpack(webpackConfig);
  const webpackDevMiddleware = require("webpack-dev-middleware");
  const webpackHotMiddleware = require("webpack-hot-middleware");

  app.use(webpackDevMiddleware(webpackCompiler));
  app.use(webpackHotMiddleware(webpackCompiler));

  app.use(express.static(webappDir));
}

Instead of consuming your existing webpack.config.js file, you can also pass a configuration object to the webpack API.

Want more?

If you liked this post, then subscribe to TypeScript TV on YouTube. Alternatively, you can follow me on DEV to learn about best practices with TypeScript & JavaScript.

29