Table of Contents
- Prerequisites
- Initializing the Project
- Installing Webpack
- Basic Webpack Configuration
- Handling HTML Files
- Processing CSS and SCSS
- Managing Images and Fonts
- Setting Up a Development Environment
- Production Optimization
- Environment Variables
- Final Project Structure
- Troubleshooting Common Issues
- Conclusion
- References
Prerequisites
Before getting started, ensure you have the following installed:
- Node.js (v14 or later) and npm (v6 or later) or Yarn (v1.22 or later).
- Download Node.js from nodejs.org. npm is included with Node.js.
- Verify installation with:
node -v # Should return a version like v18.17.0 npm -v # Should return a version like 9.6.7
Initializing the Project
First, create a new project folder and initialize it with npm:
mkdir webpack-demo && cd webpack-demo
npm init -y
This generates a package.json file to manage dependencies and scripts.
Installing Webpack
Webpack requires two core packages:
webpack: The bundler itself.webpack-cli: Command-line interface to run Webpack commands.
Install them as development dependencies:
npm install webpack webpack-cli --save-dev
Verify installation by checking package.json—you’ll see webpack and webpack-cli under devDependencies.
Basic Webpack Configuration
Webpack works out of the box with sensible defaults, but custom configuration gives you full control. Let’s start with a minimal setup.
Project Structure
Create a basic folder structure:
webpack-demo/
├── src/ # Source files
│ └── index.js # Entry point
├── dist/ # Bundled output (auto-generated)
├── package.json
└── webpack.config.js # Webpack configuration
Add a simple src/index.js:
// src/index.js
console.log("Hello, Webpack!");
Default Build (No Config File)
Webpack’s default configuration assumes:
- Entry:
src/index.js - Output:
dist/main.js - Mode:
production(minifies output by default).
Run the default build:
npx webpack
Check the dist folder—you’ll see main.js, a minified bundle containing your code.
Custom Configuration File
For flexibility, create webpack.config.js in the root directory:
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js", // Entry file
output: {
filename: "bundle.js", // Output filename
path: path.resolve(__dirname, "dist"), // Output directory (absolute path)
},
};
Now run the build again with:
npx webpack
Webpack will use your config, outputting dist/bundle.js.
Add a Build Script
Simplify builds by adding a script to package.json:
{
"scripts": {
"build": "webpack"
}
}
Now run:
npm run build
Handling HTML Files
Webpack doesn’t bundle HTML by default. Use HtmlWebpackPlugin to generate an HTML file and inject your bundled JS.
Install the Plugin
npm install html-webpack-plugin --save-dev
Update Webpack Config
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// ... (entry/output)
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Demo", // Title for the HTML file
template: "./src/index.html", // Use a custom template (optional)
minify: {
collapseWhitespace: true, // Minify HTML in production
},
}),
],
};
Create a Template (Optional)
Add src/index.html (used as a template):
<!-- src/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h1>Hello, Webpack!</h1>
<!-- JS is injected automatically -->
</body>
</html>
Run npm run build—Webpack will generate dist/index.html with bundle.js injected.
Processing CSS and SCSS
Webpack treats CSS/SCSS as modules, but requires loaders to process them.
Loaders for CSS
css-loader: Resolves@importandurl()in CSS files.style-loader: Injects CSS into the DOM via<style>tags.
Install loaders:
npm install css-loader style-loader --save-dev
Configure CSS Handling
Add a module.rules section to webpack.config.js to process .css files:
module.exports = {
// ... (entry/output/plugins)
module: {
rules: [
{
test: /\.css$/i, // Match .css files
use: ["style-loader", "css-loader"], // Loaders (order matters: right to left)
},
],
},
};
Note: Loaders run from right to left. css-loader processes the CSS first, then style-loader injects it.
Add a CSS File
Create src/styles.css:
/* src/styles.css */
body {
background: #f0f0f0;
color: #333;
}
Import it into src/index.js:
// src/index.js
import "./styles.css";
console.log("Hello, Webpack!");
Run npm run build—the CSS will be injected into the DOM when index.html loads.
Processing SCSS/Sass
To use SCSS (Sass), add sass-loader and sass (replaces deprecated node-sass):
npm install sass-loader sass --save-dev
Update the CSS rule in webpack.config.js to handle .scss files:
module: {
rules: [
{
test: /\.s[ac]ss$/i, // Match .scss and .sass files
use: [
"style-loader", // Creates style nodes from JS strings
"css-loader", // Translates CSS into CommonJS
"sass-loader", // Compiles Sass to CSS
],
},
],
}
Rename styles.css to styles.scss and update the import in index.js:
import "./styles.scss";
Managing Images and Fonts
Webpack can bundle images, fonts, and other assets using url-loader (inlines small files as base64) and file-loader (emits files to output directory).
Install Loaders
npm install url-loader file-loader --save-dev
Configure Asset Rules
Add rules to webpack.config.js for images and fonts:
module: {
rules: [
// ... (CSS/SCSS rules)
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // Image formats
type: "asset/resource", // Replaces file-loader (Webpack 5+)
generator: {
filename: "assets/images/[name].[hash][ext]", // Output path/filename
},
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i, // Font formats
type: "asset/resource",
generator: {
filename: "assets/fonts/[name].[hash][ext]",
},
},
],
}
Note: Webpack 5+ has built-in asset/resource and asset/inline types, replacing file-loader and url-loader. Use type: "asset/inline" for small files (e.g., icons) to inline them as base64.
Use Assets in Code
Import an image in index.js and add it to the DOM:
import logo from "./assets/logo.png"; // Adjust path to your image
const img = document.createElement("img");
img.src = logo;
document.body.appendChild(img);
Setting Up a Development Environment
For a smooth development workflow, use webpack-dev-server for hot reloading and in-memory bundling.
Install webpack-dev-server
npm install webpack-dev-server --save-dev
Add a Start Script
Update package.json:
{
"scripts": {
"build": "webpack",
"start": "webpack serve --open"
}
}
Configure Development Mode
Add mode: "development" to webpack.config.js for unminified, fast builds:
module.exports = {
mode: "development", // "production" | "development" | "none"
devtool: "inline-source-map", // Easier debugging (shows original code)
devServer: {
static: "./dist", // Serve files from dist
hot: true, // Enable hot module replacement
},
// ... (other config)
};
Run the dev server:
npm start
Webpack will launch your browser, serve index.html, and auto-reload when files change.
Production Optimization
For production, optimize bundle size, caching, and performance.
Set Production Mode
Add a production-specific config or use mode: "production" (Webpack enables optimizations like minification by default).
Update package.json to support both modes:
{
"scripts": {
"build": "webpack --mode production",
"dev": "webpack serve --mode development"
}
}
Key Optimizations
1. Minification
-
JS: Webpack uses
TerserPlugin(enabled by default in production) to minify JS. -
CSS: Use
css-minimizer-webpack-pluginto minify CSS:npm install css-minimizer-webpack-plugin --save-devUpdate
webpack.config.js:const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); module.exports = { optimization: { minimizer: [ `...`, // Extend default minimizers (TerserPlugin) new CssMinimizerPlugin(), // Minify CSS ], }, };
2. Code Splitting
Split vendor code (e.g., React, Lodash) from your app code using splitChunks:
optimization: {
splitChunks: {
chunks: "all", // Split both initial and async chunks
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
},
},
}
3. Cache Busting
Add content hashes to filenames to avoid caching old versions:
output: {
filename: "[name].[contenthash].js", // e.g., "main.a1b2c3.js"
path: path.resolve(__dirname, "dist"),
},
4. Clean Output Directory
Use CleanWebpackPlugin to delete old files in dist before building:
npm install clean-webpack-plugin --save-dev
Update the config:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
plugins: [
new CleanWebpackPlugin(), // Cleans dist folder
new HtmlWebpackPlugin({ /* ... */ }),
]
Environment Variables
Inject environment variables (e.g., API URLs) using DefinePlugin or dotenv-webpack.
Using DefinePlugin
Add global constants to your code:
const { DefinePlugin } = require("webpack");
plugins: [
new DefinePlugin({
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
"process.env.API_URL": JSON.stringify("https://api.example.com"),
}),
]
Use in code:
console.log("API URL:", process.env.API_URL);
Using .env Files
For local variables, use dotenv-webpack:
npm install dotenv-webpack --save-dev
Create a .env file:
API_URL=https://api.example.com
Update the config:
const Dotenv = require("dotenv-webpack");
plugins: [new Dotenv()];
Final Project Structure
Your project should now look like this:
webpack-demo/
├── src/
│ ├── assets/
│ │ ├── logo.png
│ │ └── fonts/
│ ├── styles.scss
│ └── index.js
├── .env
├── package.json
├── webpack.config.js
└── dist/ (auto-generated)
Troubleshooting Common Issues
- Loaders Order: Loaders run right-to-left (e.g.,
use: ["style-loader", "css-loader"]→css-loaderfirst). - Missing Dependencies: Ensure all loaders/plugins are installed (e.g.,
sassforsass-loader). - File Not Found: Verify paths in
entry,output, anddevServer.static. - Hot Reload Issues: Check
devServer.hot: trueand ensurewebpack-dev-serveris up to date.
Conclusion
You now have a fully configured Webpack setup for development and production! Webpack’s flexibility lets you extend it further (e.g., TypeScript, React, Vue) with additional loaders and plugins. Refer to the Webpack documentation for advanced use cases.