Plugins: HTML, CSS extraction and defines

webpack · Essentials · 12 min

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): generates index.html with <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 .css files for production instead of injecting it with JavaScript. Use its loader in place of style-loader, typically only in production.
  • DefinePlugin (built in, webpack.DefinePlugin): replaces expressions at compile time, e.g. __API_URL__ or process.env.API_URL. Values are inserted as code, so wrap strings in JSON.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.

Check yourself

  1. Why wrap DefinePlugin string values in JSON.stringify?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: webpack.js.org/concepts/plugins/