Webpack 5 Reference: Config, Loaders, Code Splitting, Dev Server & Module Federation
Webpack 5 is still the most widely installed JavaScript bundler. Most large production apps, Create React App projects, and enterprise React/Angular/Vue builds run on webpack. The biggest v5 changes: Module Federation (share code across separately deployed apps), persistent disk cache (massively faster rebuilds), and asset modules replacing url-loader/file-loader. The config surface is large — this reference covers the patterns you actually need.
1. Core Config — Entry, Output, Mode
webpack.config.js structure, entry points, output, mode, and environment-specific configs
// webpack.config.js (or .ts with ts-node):
const path = require("path");
const { DefinePlugin } = require("webpack");
module.exports = (env, argv) => {
const isProd = argv.mode === "production";
return {
mode: isProd ? "production" : "development", // "none" | "development" | "production"
// production: enables minification, tree-shaking, scope hoisting automatically
// development: enables HMR, better error messages, eval source maps
entry: "./src/index.tsx",
// Multiple entry points:
// entry: { main: "./src/index.tsx", admin: "./src/admin.tsx" }
output: {
path: path.resolve(__dirname, "dist"),
filename: isProd ? "[name].[contenthash].js" : "[name].js",
// contenthash changes only when file content changes — long-term caching
chunkFilename: isProd ? "[id].[contenthash].js" : "[id].js",
assetModuleFilename: "assets/[hash][ext]",
clean: true, // clean dist/ before each build (replaces CleanWebpackPlugin)
publicPath: "/", // base URL for all assets in the HTML
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx"],
alias: {
"@": path.resolve(__dirname, "src"),
"@components": path.resolve(__dirname, "src/components"),
},
},
plugins: [
new DefinePlugin({
"process.env.NODE_ENV": JSON.stringify(isProd ? "production" : "development"),
"__VERSION__": JSON.stringify(process.env.npm_package_version),
}),
],
};
};
2. Loaders — TypeScript, CSS, Assets
babel-loader for JS/TS, css-loader + style-loader, CSS Modules, and asset modules
// npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
// npm install -D css-loader style-loader postcss-loader autoprefixer
module.exports = {
module: {
rules: [
// TypeScript + JSX via Babel (faster than ts-loader, no type-check):
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", { targets: "defaults" }],
["@babel/preset-react", { runtime: "automatic" }], // no React import needed
"@babel/preset-typescript", // strips types (no checking)
],
},
},
},
// CSS + PostCSS (global styles):
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: [
isProd ? MiniCssExtractPlugin.loader : "style-loader", // extract in prod, inject in dev
"css-loader",
{ loader: "postcss-loader", options: { postcssOptions: { plugins: ["autoprefixer"] } } },
],
},
// CSS Modules (*.module.css):
{
test: /\.module\.css$/,
use: ["style-loader", { loader: "css-loader", options: { modules: true } }],
},
// Asset modules (webpack 5 — replaces url-loader + file-loader):
{ test: /\.(png|jpg|gif|webp)$/, type: "asset/resource" }, // copy to output
{ test: /\.svg$/, type: "asset/source" }, // inline as string
{ test: /\.(woff2?|eot|ttf)$/, type: "asset/resource" }, // fonts
{
test: /\.(png|jpg)$/,
type: "asset", // auto: inline if < 8kb, file if >= 8kb
parser: { dataUrlCondition: { maxSize: 8 * 1024 } },
},
],
},
};
3. Code Splitting, Optimization & Tree Shaking
Dynamic imports, SplitChunksPlugin, tree shaking requirements, and bundle analysis
// Dynamic import — automatic code splitting:
const Dashboard = React.lazy(() => import("./pages/Dashboard"));
// webpack creates a separate chunk for Dashboard.tsx
// optimization config:
module.exports = {
optimization: {
// Extract vendor chunk (better long-term caching):
splitChunks: {
chunks: "all", // "async" (default, only async) | "all" (sync + async)
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
// Separate rarely-changing large libs:
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: "react-vendor",
chunks: "all",
priority: 20, // higher priority = matched first
},
},
},
// Keep runtime in a separate file (prevents hash changes in vendor chunk):
runtimeChunk: "single",
// Minification (webpack 5 built-in for JS; CSS needs plugin):
minimize: isProd,
minimizer: [
"...", // keep default JS minimizer (terser)
new CssMinimizerPlugin(), // npm install -D css-minimizer-webpack-plugin
],
},
};
// Tree shaking requirements:
// 1. ES modules (import/export — not require/module.exports)
// 2. "sideEffects": false in package.json (or list files with side effects)
// 3. mode: "production"
// Bundle analysis:
// npm install -D webpack-bundle-analyzer
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
plugins: [new BundleAnalyzerPlugin()] // opens treemap in browser
4. Dev Server, HMR & Source Maps
webpack-dev-server config, HMR, proxy for API, and source map options
// npm install -D webpack-dev-server
module.exports = {
devServer: {
port: 3000,
open: true,
hot: true, // enable HMR
historyApiFallback: true, // serve index.html for all 404s (SPA routing)
compress: true, // gzip responses
static: { directory: path.join(__dirname, "public") },
// Proxy API requests to avoid CORS:
proxy: [{
context: ["/api"],
target: "http://localhost:8080",
changeOrigin: true,
}],
// Custom headers:
headers: { "Access-Control-Allow-Origin": "*" },
},
// Source maps:
devtool: isProd
? "source-map" // prod: separate .map files, full quality
: "eval-cheap-module-source-map", // dev: fast, good enough for debugging
// Other options:
// "inline-source-map" — embedded in bundle (large, slow)
// "hidden-source-map" — external, no URL comment (for error tracking only)
// false — no source maps
};
5. Module Federation & HTML Plugin
HtmlWebpackPlugin for HTML generation, MiniCssExtractPlugin, and Module Federation
// HtmlWebpackPlugin — generates index.html with script tags:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { ModuleFederationPlugin } = require("webpack").container;
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html", // your HTML template
favicon: "./public/favicon.ico",
minify: isProd,
}),
// Extract CSS to separate files in production (no flash of unstyled content):
isProd && new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
chunkFilename: "[id].[contenthash].css",
}),
// Module Federation — share code between separately deployed apps (micro-frontends):
new ModuleFederationPlugin({
name: "shell",
remotes: {
// Load checkout app at runtime from another deployment:
checkout: "checkout@https://checkout.example.com/remoteEntry.js",
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}),
].filter(Boolean);
// In checkout app:
new ModuleFederationPlugin({
name: "checkout",
filename: "remoteEntry.js", // entry file exposed to shell
exposes: {
"./CheckoutWidget": "./src/CheckoutWidget",
"./Cart": "./src/Cart",
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
});
// In shell app code:
const CheckoutWidget = React.lazy(() => import("checkout/CheckoutWidget"));
Track Node.js and build tooling releases at ReleaseRun. Related: Vite 6 Reference | esbuild Reference | TypeScript Reference
🔍 Free tool: npm Package Health Checker — check webpack and its loader/plugin packages for known CVEs and active maintenance.
Founded
2023 in London, UK
Contact
hello@releaserun.com