Webpack 5 is a beast, and migrating to it can feel like wrestling a kraken. The biggest surprise? The entire dependency graph is now rebuilt from scratch on every build, but it’s way faster.

Let’s see it in action. Imagine a simple index.js that imports a utils.js:

// src/utils.js
export function greet(name) {
  return `Hello, ${name}!`;
}
// src/index.js
import { greet } from './utils.js';

const message = greet('World');
console.log(message);

With a basic webpack.config.js:

// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

Running npx webpack will produce dist/bundle.js. Now, if you change utils.js to export function farewell(name) { return \Goodbye, ${name}!`; }, and update index.jstoimport { farewell } from './utils.js'; console.log(farewell('World'));`, Webpack 5’s internal caching and module federation capabilities mean it intelligently rebuilds only what’s necessary, making subsequent builds feel almost instantaneous.

The core problem Webpack 5 solves is faster, more flexible builds. It achieves this through a fundamentally new architecture: persistent caching and module federation. Persistent caching means Webpack remembers the results of previous builds and reuses them when possible, drastically speeding up rebuilds. Module federation, on its own complex topic, allows you to dynamically load code from different Webpack builds at runtime, enabling microfrontends and efficient code sharing between applications.

You control this through several key levers in your webpack.config.js:

  • mode: development or production. This is crucial. production enables optimizations like minification and tree-shaking; development prioritizes build speed and debugging.
  • entry: The starting point(s) for Webpack to build its dependency graph. Can be a single file, an array, or an object for multiple entry points.
  • output: Where and how Webpack should output its bundled files. filename defines the output file name, and path is the absolute directory path.
  • resolve: How Webpack resolves module requests. You can specify extensions (e.g., ['.js', '.jsx', '.ts']) to allow imports without specifying the extension.
  • plugins: The real powerhouses. These extend Webpack’s functionality. Think HtmlWebpackPlugin for generating HTML files with your bundles, or MiniCssExtractPlugin for extracting CSS into separate files.
  • module.rules: How to handle different types of modules. This is where you configure loaders like babel-loader for JavaScript transpilation or css-loader for CSS.

The most impactful breaking changes you’ll encounter during migration are:

  1. Removal of Legacy APIs: Many deprecated APIs and loader/plugin configurations have been removed. For example, uglifyjs-webpack-plugin is no longer the default minifier; terser-webpack-plugin is used instead. You’ll need to update your plugin configurations.
  2. Context Replacement: The context option in Webpack configuration has been removed. The default behavior is now path.resolve(__dirname, 'src') (or wherever your entry point is). If you relied on a custom context, you’ll need to adjust your file paths.
  3. node Polyfills Removed: Webpack 5 no longer includes polyfills for Node.js core modules like path, fs, or Buffer in the browser. If your client-side code directly imports these, you’ll need to find browser-compatible alternatives or use a library like node-polyfill-webpack-plugin.
  4. externals Behavior Change: The way externals are resolved has been refined. If you were using externals to include libraries via <script> tags, ensure your configuration correctly maps them. For instance, if you had externals: { jquery: 'jQuery' }, you’d need to ensure jQuery is globally available.
  5. Module Federation: While a new feature, it’s also a breaking change if you were previously simulating similar functionality. Its configuration is entirely new and requires a shift in thinking about how your applications share code.
  6. optimization.splitChunks Defaults: The defaults for splitChunks have been updated to be more intelligent and often lead to better code splitting out-of-the-box. However, if you had a very specific splitChunks configuration, you might need to re-evaluate it.

To migrate:

  • Update Dependencies: Start by upgrading webpack and webpack-cli to their latest v5 versions. Also, update all loaders and plugins you use to their compatible v5 versions.
    npm install --save-dev webpack@latest webpack-cli@latest
    # Then update other loaders/plugins
    
  • Address Removed APIs: Go through your webpack.config.js and remove any references to deprecated options or plugins. Consult the Webpack 5 migration guide for specific replacements. For uglifyjs-webpack-plugin, switch to terser-webpack-plugin.
    // Old
    // const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
    // module.exports = {
    //   //...
    //   plugins: [new UglifyJsPlugin()]
    // };
    
    // New
    const TerserPlugin = require('terser-webpack-plugin');
    module.exports = {
      //...
      optimization: {
        minimizer: [new TerserPlugin()],
      },
    };
    
  • Handle Node Polyfills: If you encounter errors like "Can’t resolve 'fs'" on the client-side, install and configure node-polyfill-webpack-plugin.
    npm install --save-dev node-polyfill-webpack-plugin
    
    // webpack.config.js
    const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
    
    module.exports = {
      //...
      plugins: [
        new NodePolyfillPlugin()
      ]
    };
    
  • Review externals: Test your application thoroughly. If external libraries aren’t loading, adjust your externals configuration or ensure they are correctly loaded via <script> tags before your bundled code runs.
  • Test Thoroughly: Run your build command (npx webpack) and then test your application in the browser. Fix any new errors that appear.

The next hurdle you’ll likely face is understanding and implementing Module Federation for microservice-like frontend architectures.

Want structured learning?

Take the full Webpack course →