Webpack Cheat Sheet
Covers webpack configuration basics, loaders, plugins, code splitting, and optimization techniques for bundling modern JavaScript apps.
Basic Configuration
Core entry, output, and dev server setup.
// webpack.config.jsconst path = require('path');module.exports = { mode: 'production', // 'development' | 'production' | 'none' entry: './src/index.js', output: { filename: '[name].[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true, // clear dist/ before each build }, resolve: { extensions: ['.js', '.jsx'], }, devServer: { static: './dist', port: 3000, hot: true, },};
Loaders & Plugins
Transforming files and extending the build.
const HtmlWebpackPlugin = require('html-webpack-plugin');const MiniCssExtractPlugin = require('mini-css-extract-plugin');module.exports = { module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader', }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, { test: /\.(png|svg|jpg)$/, type: 'asset/resource', // built-in asset module (webpack 5+) }, ], }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), new MiniCssExtractPlugin(), ],};
Code Splitting & Optimization
Splitting vendor code and lazy-loading chunks.
module.exports = { optimization: { splitChunks: { chunks: 'all', // split shared/vendor code into separate chunks }, runtimeChunk: 'single', },};// Dynamic import creates an automatic split pointimport('./chart').then(({ renderChart }) => renderChart());
CLI & Key Concepts
Core terminology and common commands.
- webpack --mode production- build once with production optimizations enabled (minification, tree shaking)
- webpack serve- run webpack-dev-server with hot module replacement
- entry- the starting module(s) webpack uses to build its dependency graph
- output- where and how the resulting bundles are emitted to disk
- loader- transforms non-JS files (CSS, images, TypeScript) into modules webpack can bundle
- plugin- hooks into the compilation lifecycle for broader tasks like emitting HTML or extracting CSS
- tree shaking- dead code elimination based on static ES module imports/exports
Module Federation (Micro-Frontends)
Exposing and consuming modules at runtime across independently deployed builds.
const { ModuleFederationPlugin } = require('webpack').container;// host appmodule.exports = { plugins: [ new ModuleFederationPlugin({ name: 'host', remotes: { checkout: 'checkout@https://cdn.example.com/checkout/remoteEntry.js', }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true } }, }), ],};// remote appmodule.exports = { plugins: [ new ModuleFederationPlugin({ name: 'checkout', filename: 'remoteEntry.js', exposes: { './CartWidget': './src/CartWidget' }, shared: ['react', 'react-dom'], }), ],};// consuming in host codeconst CartWidget = React.lazy(() => import('checkout/CartWidget'));
Writing a Custom Loader & Plugin
Minimal loader that transforms source, and a plugin that hooks into the compilation lifecycle.
// loaders/strip-console-loader.jsmodule.exports = function stripConsoleLoader(source) { return source.replace(/console\.log\([^)]*\);?/g, '');};// plugins/manifest-plugin.jsclass ManifestPlugin { apply(compiler) { compiler.hooks.emit.tapAsync('ManifestPlugin', (compilation, cb) => { const manifest = {}; for (const name of Object.keys(compilation.assets)) { manifest[name] = compilation.assets[name].size(); } const json = JSON.stringify(manifest, null, 2); compilation.assets['manifest.json'] = { source: () => json, size: () => json.length, }; cb(); }); }}module.exports = { ManifestPlugin };
Persistent Caching & Build Performance
Filesystem cache and deterministic module ids to speed up rebuilds and stabilize long-term-cache hashes.
module.exports = { cache: { type: 'filesystem', // persist cache to node_modules/.cache/webpack buildDependencies: { config: [__filename], // invalidate cache when config itself changes }, }, optimization: { moduleIds: 'deterministic', // stable short hashes instead of numeric ids chunkIds: 'deterministic', realContentHash: true, // recompute hash after minification for accurate long-term caching }, experiments: { lazyCompilation: true, // compile async chunks only when actually requested },};
Conditional Resolution & Externals
Controlling which package export condition webpack picks and excluding a dependency from the bundle.
module.exports = { resolve: { conditionNames: ['import', 'module', 'browser', 'default'], mainFields: ['browser', 'module', 'main'], }, externals: { react: 'React', // expect a global `React` (loaded via <script>) instead of bundling lodash: { commonjs: 'lodash', amd: 'lodash', root: '_', }, }, externalsType: 'window',};
Compiler Internals Glossary
Terminology for reasoning about custom plugins and build performance.
- Compiler- the top-level singleton representing the full webpack environment; created once per build invocation
- Compilation- represents a single build of versioned assets; created fresh on every rebuild in watch mode
- Tapable hooks- the plugin system's event bus (SyncHook, AsyncSeriesHook, etc.) that Compiler and Compilation expose for plugins to tap into
- Chunk graph- the internal graph mapping modules to the output chunks they end up bundled into, distinct from the module dependency graph
- Stats object- the structured build report (assets, timings, warnings) queryable via compiler.run's callback for custom tooling
- asset modules- built-in replacement for file-loader/url-loader/raw-loader using type: 'asset', 'asset/resource', 'asset/inline', 'asset/source'
- Scope hoisting- concatenates modules into fewer closures in production mode to reduce function-call overhead and bundle size
Run webpack-bundle-analyzer after every major dependency change to visualize what's actually in your bundle: it's the fastest way to catch an accidentally duplicated dependency or an oversized import before it ships to production.