| name | webpack |
| description | [Applies to: **/*.{js,jsx}] This guide provides opinionated, actionable best practices for configuring webpack, focusing on performance, maintainability, and modern development workflows in 2025. |
| source | cursor_mdc |
webpack Best Practices
webpack remains the cornerstone of modern JavaScript application builds. Adhering to these definitive guidelines ensures your projects are performant, maintainable, and aligned with current best practices.
1. Configuration Structure
Always separate your webpack configurations by environment. Use webpack-merge to combine a common base with environment-specific overrides.
❌ BAD: Monolithic webpack.config.js with conditional logic.
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
mode: isProduction ? 'production' : 'development',
};
✅ GOOD: Modular configs with webpack-merge.
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
],
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader',
},
],
},
};
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
output: {
filename: '[name].bundle.js',
},
devServer: {
static: './dist',
hot: true,
},
});
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
output: {
filename: '[name].[contenthash].bundle.js',
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
],
optimization: {
minimize: true,
splitChunks: {
chunks: 'all',
},
},
});
2. Performance Optimizations
Prioritize build speed and bundle size from the start.
A. Caching
Enable persistent caching for faster incremental builds.
❌ BAD: No caching, slow rebuilds.
module.exports = { };
✅ GOOD: Filesystem caching.
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
};
B. Loaders Scope
Apply loaders to the absolute minimum set of files. Exclude node_modules where possible.
❌ BAD: Applying Babel to node_modules.
module.exports = {
module: {
rules: [{ test: /\.js$/, loader: 'babel-loader' }],
},
};
✅ GOOD: Explicitly include your source, exclude node_modules.
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/,
loader: 'babel-loader',
},
],
},
};
C. Code Splitting
Leverage optimization.splitChunks and dynamic import() for smaller, on-demand bundles.
❌ BAD: Single large bundle for the entire application.
import { largeModuleA } from './largeModuleA';
import { largeModuleB } from './largeModuleB';
✅ GOOD: Dynamic imports with splitChunks.
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
maxInitialRequests: 30,
maxAsyncRequests: 30,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
runtimeChunk: 'single',
},
};
const loadLargeModuleA = () => import('./largeModuleA');
const loadLargeModuleB = () => ();
3. Essential Plugins
Use these plugins for robust and optimized builds.
A. HtmlWebpackPlugin
Always generate your index.html and inject bundles automatically.
❌ BAD: Manually updating <script> tags in index.html.
<script src="./dist/main.bundle.js"></script>
✅ GOOD: Let HtmlWebpackPlugin handle it.
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
title: 'My App',
}),
],
};
B. DefinePlugin
Inject environment variables safely and enable dead-code elimination. mode handles process.env.NODE_ENV automatically. For custom variables, use DefinePlugin.
❌ BAD: Hardcoding API keys or relying on client-side process.env.
const API_URL = 'https://dev.api.example.com';
✅ GOOD: Injecting via DefinePlugin.
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.API_URL': JSON.stringify(process.env.API_URL || 'http://localhost:3000/api'),
}),
],
};
const API_URL = process.env.API_URL;
4. Modern Tooling & Maintenance
Stay current with webpack and Node.js versions.
A. Keep Dependencies Updated
Regularly update webpack, webpack-cli, webpack-dev-server, and Node.js for performance gains and security fixes.
B. output.clean
Always use output.clean: true to prevent stale files in your dist directory.
❌ BAD: Manually deleting dist or having old files linger.
"scripts": {
"build": "rm -rf dist && webpack --config webpack.prod.js"
}
✅ GOOD: Built-in clean option.
module.exports = {
output: {
clean: true,
},
};