Real apps need an HTML page, CSS files and environment values, all provided by plugins.
Plugins are objects with an apply(compiler) method that hook into the build. You add instances to the plugins array.
The ones nearly every app uses:
- HtmlWebpackPlugin (
html-webpack-plugin): generatesindex.htmlwith<script>(and<link>) tags for the emitted bundles, including hashed names, optionally from your own template. - MiniCssExtractPlugin (
mini-css-extract-plugin): extracts CSS into separate.cssfiles for production instead of injecting it with JavaScript. Use itsloaderin place ofstyle-loader, typically only in production. - DefinePlugin (built in,
webpack.DefinePlugin): replaces expressions at compile time, e.g.__API_URL__orprocess.env.API_URL. Values are inserted as code, so wrap strings inJSON.stringify. - CopyWebpackPlugin: copies static files that are not imported.
- ProvidePlugin, BundleAnalyzerPlugin (
webpack-bundle-analyzer) and many more.
Built-in plugins live on the webpack object; third-party ones are npm packages. mode already adds several plugins for you (e.g. minification in production), so do not add them twice.
Like Vite's VITE_ variables, anything you define ends up in the client bundle; never inject secrets.
Example
// webpack.config.js
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const isProd = process.env.NODE_ENV === "production";
module.exports = {
mode: isProd ? "production" : "development",
module: {
rules: [{
test: /\.css$/i,
use: [isProd ? MiniCssExtractPlugin.loader : "style-loader", "css-loader"],
}],
},
plugins: [
new HtmlWebpackPlugin({ title: "Shop", template: "./src/index.html" }),
new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" }),
new webpack.DefinePlugin({
__API_URL__: JSON.stringify(process.env.API_URL ?? "http://localhost:8000"),
}),
],
};Remember
- Plugins hook into the compiler for everything loaders can't do
- HtmlWebpackPlugin writes index.html with the right script tags
- MiniCssExtractPlugin emits CSS files in production
- DefinePlugin inserts code: wrap strings in JSON.stringify
Watch out for
new DefinePlugin({ API: 'https://x' }), which inserts an unquoted URL as code and breaks the bundle.