DEV Community

Mohamed Siraj
Mohamed Siraj

Posted on

Webpack 5: The Next Generation Module Bundler

In the ever-evolving landscape of JavaScript development, having a reliable and efficient build tool is essential. Webpack has long been the go-to module bundler for many developers, and with the release of Webpack 5, it continues to improve the development experience with new features and optimizations.

What is Webpack?

Webpack is a static module bundler for modern JavaScript applications. It examines your entire project, builds a dependency graph, and generates one or more bundles. It's not just for JavaScript—Webpack can handle CSS, images, fonts, and more with the right loaders and plugins.

Key Improvements in Webpack 5

1. Improved Performance

Webpack 5 brings significant performance improvements through persistent caching, which allows it to store and reuse previously compiled modules between builds:

// webpack.config.js
module.exports = {
  // ...
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

2. Tree Shaking Enhancements

Tree shaking (the process of eliminating unused code) has been improved to handle more scenarios:

// webpack.config.js
module.exports = {
  // ...
  optimization: {
    usedExports: true,
    minimize: true
  }
};
Enter fullscreen mode Exit fullscreen mode

3. Module Federation

One of the most exciting features in Webpack 5 is Module Federation, which allows multiple independent builds to form a single application:

// host application webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
        app2: 'app2@http://localhost:3002/remoteEntry.js'
      },
      shared: ['react', 'react-dom']
    })
  ]
};

// remote application webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button'
      },
      shared: ['react', 'react-dom']
    })
  ]
};
Enter fullscreen mode Exit fullscreen mode

Setting Up a Basic Webpack 5 Project

Let's walk through setting up a simple project with Webpack 5:

Step 1: Initialize a new project

mkdir webpack5-demo
cd webpack5-demo
npm init -y
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Webpack dependencies

npm install webpack webpack-cli webpack-dev-server --save-dev
npm install html-webpack-plugin --save-dev
Enter fullscreen mode Exit fullscreen mode

Step 3: Create project structure

webpack5-demo/
├── src/
│   ├── index.js
│   └── component.js
├── webpack.config.js
└── package.json
Enter fullscreen mode Exit fullscreen mode

Step 4: Add content to files

src/component.js:

export function component() {
  const element = document.createElement('div');
  element.innerHTML = 'Hello Webpack 5!';
  return element;
}
Enter fullscreen mode Exit fullscreen mode

src/index.js:

import { component } from './component';

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

webpack.config.js:

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

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true, // Clean the output directory before emit
  },
  devServer: {
    static: './dist',
    hot: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Webpack 5 Demo',
    }),
  ],
  optimization: {
    runtimeChunk: 'single',
  },
  cache: {
    type: 'filesystem', // Use filesystem caching
  },
};
Enter fullscreen mode Exit fullscreen mode

Step 5: Update package.json scripts

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

Step 6: Run the development server

npm start
Enter fullscreen mode Exit fullscreen mode

Adding Basic Loaders for Assets

Step 1: Install CSS loader and style loader

npm install css-loader style-loader --save-dev
Enter fullscreen mode Exit fullscreen mode

Step 2: Add a CSS file

src/style.css:

body {
  background-color: #f0f0f0;
  font-family: Arial, sans-serif;
}

.title {
  color: #333;
  text-align: center;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Update the component and webpack config

src/component.js:

import './style.css';

export function component() {
  const element = document.createElement('div');
  element.innerHTML = '<h1 class="title">Hello Webpack 5!</h1>';
  return element;
}
Enter fullscreen mode Exit fullscreen mode

webpack.config.js (add module rules):

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

Implementing JavaScript Transpilation with Babel

Step 1: Install Babel dependencies

npm install @babel/core @babel/preset-env babel-loader --save-dev
Enter fullscreen mode Exit fullscreen mode

Step 2: Update webpack configuration

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    },
    // ... other rules
  ]
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Use modern JavaScript features

// src/component.js
export const component = () => {
  const element = document.createElement('div');

  // Using template literals
  element.innerHTML = `
    <h1 class="title">Hello Webpack 5!</h1>
    <p>Current time: ${new Date().toLocaleTimeString()}</p>
  `;

  return element;
};
Enter fullscreen mode Exit fullscreen mode

Creating a Production Build

For production builds, you might want to split your configuration:

webpack.prod.js:

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = merge(common, {
  mode: 'production',
  devtool: 'source-map',
  output: {
    filename: '[name].[contenthash].js',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
  optimization: {
    minimizer: [
      new TerserPlugin(),
      new CssMinimizerPlugin(),
    ],
    splitChunks: {
      chunks: 'all',
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

Then update your package.json:

"scripts": {
  "start": "webpack serve --config webpack.dev.js",
  "build": "webpack --config webpack.prod.js"
}
Enter fullscreen mode Exit fullscreen mode

Using Module Federation

Module Federation allows for code sharing between independently built applications. Here's a simple example of how to implement it:

Host App (App A)

// webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      filename: 'remoteEntry.js',
      remotes: {
        appB: 'appB@http://localhost:3002/remoteEntry.js',
      },
      shared: ['react', 'react-dom'],
    }),
  ],
};

// App.js
import React, { Suspense } from 'react';
const RemoteButton = React.lazy(() => import('appB/Button'));

const App = () => (
  <div>
    <h1>Host Application</h1>
    <Suspense fallback="Loading Button...">
      <RemoteButton />
    </Suspense>
  </div>
);
Enter fullscreen mode Exit fullscreen mode

Remote App (App B)

// webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'appB',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button',
      },
      shared: ['react', 'react-dom'],
    }),
  ],
};

// src/Button.js
import React from 'react';

const Button = () => (
  <button>App B Button</button>
);

export default Button;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Webpack 5 brings significant improvements to the already powerful bundler, with features like persistent caching, improved tree shaking, and Module Federation. These enhancements make it easier to build faster, more efficient, and modular JavaScript applications.

By understanding and implementing these features, you can take full advantage of what Webpack 5 has to offer and improve both your development experience and the performance of your applications.

Remember that Webpack is highly configurable, and this article only scratches the surface of what's possible. Be sure to check the official Webpack documentation for more detailed information and advanced configurations.

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post →

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay