Beginner's Webpack Tutorial
Webpack takes all your files (e.g. multiple javascript files, css files, scss files, fonts, images, etc.), crumples them up into one big ball, and spits them out again. You're left with - in many cases - just one, big file that contains all your css and javascript code, which you can neatly load in your html files.
Why use it? Well, because once you have more than just a few lines of javascript code it gets really messy; you'd have to import all of the files in exactly the right order. It also makes it much easier to split up CSS into multiple files, and makes it possible to use SCSS (which Webpack automatically turns into CSS for you)!
Don't be discouraged by the long and frankly confusing set up. Not only will this teach you valuable skills (such as working with terminal commands), but it's also time you will save yourself later once the set up is complete and you start coding.
You can find the files/codes for this entire example project HERE.
Please note that simply copying the contents of those files is not the same as installing webpack. You will still need to run the installation commands, for example.
If you want to look at real project that uses webpack, check out the one for this very website!
This tutorial was written for webpack 5. If you use version 4 or older some of the information in this guide will be incorrect!
You can check which version of webpack you have installed by looking into yourpackage.json
or running this command:npm list --depth=0 | findstr webpack
If you're using webpack 4 I highly recommend upgrading to 5.
If you have questions or feedback regarding the tutorial, please use the comment section at the end of the page! Don't be shy; I know it's frustrating when you're stuck, and I'd love to help.
Requirements
npm
We will be installing webpack with npm. This means you need node / npm installed on your device, and your project needs to be inialized as an npm project (= you need to have a package.json
file in your root folder).
Read my npm tutorial to find out how to do all of these things.
You also need to be able to use a command line / terminal. The tutorial link above will also teach you how to do that.
If your project is a git repository make sure that node_modules
is in your .gitignore
file. (See here).
SCSS
We will be using SCSS in this tutorial, so you need to know how it works.
If you don't know SCSS yet, here's a quick explanation: SCSS is a CSS preprocessor. We will use Webpack to turn SCSS code (
.scss
) into valid CSS code (.css
), which you will upload to your website like normally. Any valid CSS code is valid SCSS code, so you don't need to learn anything new! But SCSS also gives you more freedom to write CSS code, such as nesting rules, variables, functions and other re-usable code, for loops, etc.Here is a great tutorial with which you can learn the basics in just 10 minutes (Read the sections "Variables" and "Nesting", all others are optional).
Show SCSS Example
Here is a simple, realistic SCSS example:
$font: Helvetica, Arial;
$textColor: black;
$linkColor: #333333;
body {
font: $font;
color: $textColor;
font-size: 14px;
}
a {
color: $linkColor;
b {
text-decoration: underline;
}
&:hover {
color: darken($linkColor, 1.2);
}
}
This would be turned into this CSS by Webpack:
body {
font: Helvetica, Arial;
color: black;
font-size: 14px;
}
a {
color: #333333;
}
a b {
text-decoration: underline;
}
a:hover {
color: #303030;
}
Project Structure
Our project is going to have a structure like this:
/dist
- index.html <- your home page
- generated files...
/src
- css/ <- your (s)css files are in here
example.(s)css
- js/ <- your javascript files are in here
example.js
- img/ <- your images are in here
- fonts/ <- your fonts are in here
- index.js <- your entry javascript file (which loads all others)
- main.scss <- your entry scss file (which loads all others)
.gitignore
package-lock.json
package.json
webpack.config.js
Explanation:
- Your
dist
folder (short for "distribution") includes all files that the browser of a user visiting your site will be able to see and use, including all of your html files. By default, webpack's generated javascript file will be placed directly in this folder. (Alternatively, you could call this folderpublic
if you prefer.)- The
/dist/index.html
file will be your homepage. You should create this one now.
- The
- Your
src
folder (short for "source") includes all files that are only necessary for development, for example the javascript and CSS files that webpack will 'swallow up': Since they will be included in the file generated by webpack these files themselves do not need to be loaded by the browser. This also means you don't have to upload the contents of this folder to your website!- The
/src/index.js
will be your main javascript file, in which you will load all your other javascript files (see "Usage" below).
- The
webpack.config.js
is the configuration file of webpack. This is where we tell webpack how to do what.
If you don't know the files
.gitignore
,package-lock.json
andpackage.json
, read my npm tutorial.
Installation
Run npm install --save-dev webpack webpack-cli
in your project folder to install webpack.
This will install the following packages:
- webpack
- webpack-cli (This makes it possible for us to use the command line interface for webpack)
You should now see a new folder and file in your project:
The packages will be added to the dependency list in your package.json
. Also, if you didn't have them before, a node_modules
folder and a package-lock.json
file will be generated. You should never manually change the contents of node_modules
or package-lock.json
! More information about that here.
CSS
Now, run this to install all required packages for handling CSS and SCSS:
npm install --save-dev css-loader sass-loader sass mini-css-extract-plugin
This will install the following packages:
- css-loader (To interpret @import and url() in CSS files.)
- sass-loader (To compile SCSS to CSS.)
- sass (A requirement for sass-loader.)
- mini-css-extract-plugin (Extracts CSS into separate files)
You don't need sass-loader and sass if you don't use SCSS; However, I highly recomennd using SCSS.
Configuration
The configuration file is the file in which you tell webpack what to do and how.
Create a file webpack.config.js
in your root folder. Webpack will automatically detect and use this configuration file.
We will start with this configuration: (Paste it into your configuration file now.)
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
// This enables production mode, which minifies the JS and CSS files for fast loading times:
mode: "production",
// Define entry file (which loads every other file):
entry: "./src/index.js",
// Define output file/directory:
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
assetModuleFilename: "assets/[path][name][ext]", // Preserve original file paths and file names for resources (images, fonts, etc.)
},
module: {
rules: [
// Rule for .css files:
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader, // extracts css into a file
"css-loader", // necessary to load css
],
},
// Rule for .scss files:
{
test: /\.scss$/i,
use: [
MiniCssExtractPlugin.loader, // extracts css into a file
"css-loader", // necessary to load css
"sass-loader", // turns scss into css
],
},
],
},
plugins: [
// define name for generated css files:
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
// Generate source maps for easier debugging:
devtool: "source-map",
};
Additional Explanation:
entry
defines your main javascript file which webpack should load. Your main javascript file is the one that loads all others.output
defines where webpack should save the generated file(s) (which will include your javascript, css, and so on). You can change the file name and path to your liking. The default is /dist/main.jsfilename
: this will be the name of your generated JavaScript filepath
: this will be the location of your generated files (In this example the 'dist' folder)assetModuleFilename
: this defines the file path and name of any asset files webpack uses (such as images, fonts, etc.). In this example they will be saved in an 'assets' subfolder in the 'dist' folder and retain their original path and file names.
module.rules
is an array of rules which specify how webpack handles your files (in our case we have a rule for.css
and a rule for.scss
files)
Advanced Tip: You can have multiple configuration files (e.g. for different purposes), but then you need to specify which one it should use, e.g. like this:
npx webpack --config webpack2.config.js
In previous versions of Webpack it was necessary to add configuration to be able to use assets in your CSS and JavaScript files (such as images, fonts, etc.) Thankfully, in Webpack 5 we don't need to do this anymore.
CSS directly in DOM (Optional)
If you want, you can also add your CSS code into your head directly like this <style>...</style>
instead of into a CSS file.
Keep in mind that this will loading the styles of your website slightly slower, so I don't recommend it unless you have a good reason.
Show me how
First, install style-loader:
npm install --save-dev style-loader
Next, we're just replacing MiniCssExtractPlugin.loader
with "style-loader"
:
// Rule for .css files:
{
test: /\.css$/i,
use: [
"style-loader", // extracts css into a file
"css-loader", // necessary to load css
],
},
// Rule for .scss files:
{
test: /\.scss$/i,
use: [
"style-loader", // extracts css into a file
"css-loader", // necessary to load css
"sass-loader", // turns scss into css
],
},
You can now remove the <link rel="stylesheet" href="main.css" />
line from your HTML files (if you have it) because the CSS will be added directly.
Done!
(You can deinstall MiniCssExtractPlugin if you want.)
Usage
We will create 2 SCSS and 2 JavaScript files to test if our setup works.
Remember the project structure we're going for:
/dist
- index.html
- generated files...
/src
- css/ <- your scss files are in here
example.(s)css
- js/ <- your javascript files are in here
example.js
- img/
- fonts/
- index.js <- your entry javascript file (which loads all others)
- main.scss <- your entry scss file (which loads all others)
.gitignore
package-lock.json
package.json
webpack.config.js
CSS
Our src/main.scss
could look like this:
@import "css/example1";
@import "css/example2";
@import "css/example3";
Use it to import all other css files. Keep in mind that you will get an error if you try to import files that do not exist!
The other files are completely normal, like this:
src/css/example1.scss
:
body {
text-decoration: underline;
}
JavaScript
Our src/index.js
could look like this:
import "./main.scss"; // loads all css
import { doExample } from "./js/example.js"; // loads the function doExample() from example.js
document.addEventListener("DOMContentLoaded", () => {
document.body.innerHTML = "It works!";
doExample();
});
Use this file to import all other JavaScript files (by importing their functions and running the functions) and your main SCSS file (see first line).
The other JavaScript files are completely normal, except that all function that we import somewhere else need the export
keyword:
src/js/example.js
:
export function doExample() {
console.log("This example works too!");
}
Assets
You can use assets (such as images, fonts, etc.) in your CSS and JavaScript files normally:
@font-face {
font-family: "rainyhearts";
src: url("fonts/rainyhearts.ttf") format("truetype");
font-weight: 500;
font-style: normal;
font-display: swap;
}
body {
font-family: "rainyhearts";
background-image: url(img/example.png);
}
The path should be relative to your src folder, not the file you're currently editing!
For example, if example.png is in src/img :
Correct: background-image: url(img/example.png);
Inorrect: background-image: url(../img/example.png);
Add your assets to yoursrc
folder (e.g. in src/img and src/fonts). All assets that you use in your CSS and JavaScript will be copied into thedist
folder by Webpack. Remember not to change the generated files in thedist
folder; Only manage your assets in thesrc
folder.
Running Webpack
We'll now see if everything works so far.
Run npx webpack
to build (= generate) your files, or...
Run npx webpack --watch
to watch your files. (This means they will automatically build if you change any of them, and is very practical when working on your project.)
Remember these two commands! You will use them every time you work on your project! Note that these commands start with npX, not npM!
You could also define custom npm commands to run webpack, e.g.
npm run watch
. Here's how to do this.
A JavaScript and CSS file (and, later, perhaps other asset files once you've configured them) will be generated in your dist
folder.
(Note that no CSS file will be generated if you used style-loader instead of MiniCssExtractPlugin.)
It will also generate .map
files if you've configured it to do this. These are source maps that help the browser reference lines of code in minified CSS and JavaScript files. You don't need to do anything with them, just leave them.
HTML
In your page files (located in your dist folder), you need to use the generated JavaScript and CSS files (also located in your dist folder) as usual:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Webpack Example</title>
<!-- loading javascript file: -->
<script src="main.js"></script>
<!-- loading css file: -->
<link rel="stylesheet" href="main.css" />
</head>
<body>
Hello World.
</body>
</html>
(As usual, you might have to use ../
, ../../
etc. depending on the folder depth of your page file.)
Done!
Everything should work now!
Having Problems?
Remember to either run npm webpack --watch
or npx webpack
. If you're using npx webpack
you need to run the command after every change you make in your javascript/css files.
Make sure you're loading the generated file (e.g. main.js
), not the source file (e.g. index.js
) in your html file.
If your html file is in a subfolder instead of directly in the dist
folder, make sure to adjust the path to the file as usual, e.g. <script src="../main.js"></script>
FAQ
"I can't see my changes!"
Make sure you run npx webpack
after every change, or run npx webpack --watch
so that webpack detects all changes automatically. Whenever you re-open your project you will need to run it again.
"How do I stop the currently running command?"
It depends on your terminal. In VSCode make sure you're focusing the terminal and press Ctrl+C (as if copying something). Then you might confirm by typing Y (for "Yes") and pressing enter.
Possible error messages: (Always check your terminal and your browser's console!)
"Module not found: Error: Can't resolve ..." / "Module build failed. Cannot find module ..."
If the module in the error message is an image/font/asset file that you're trying to use, make sure you don't have a typo in the file name and that the relative path is correct. The path should be relative to your src folder, not the file you're currently editing!
For example, if example.png is in src/img:
Correct: background-image: url(img/example.png);
Inorrect: background-image: url(../img/example.png);
"Module parse failed. [...] You may need an appropriate loader to handle this file type..."
This means exactly what it says. Make sure you have the necessary loader(s) for that file type installed and have the rules for this file type in your configuration file (webpack.config.js
).
"Uncaught SyntaxError: import declarations may only appear at top level of a module"
Exactly what is says: Imports should only be used on the very top of JavaScript files. Also make sure you are editing the javascript file in your src
folder, not the one in the dist
folder (that one you should never edit because it will be overwritten!)
If you have a problem or question that isn't here use the comment section below!
Links
Here are some links to official documentation & other helpful stuff.
You can find the files/codes for this entire example project HERE.
Please note that simply copying the contents of those files is not the same as installing webpack. You will still need to run the installation commands, for example.
If you want to look at real project that uses webpack, check out the one for this very website!
I spend hours of my free time writing these tutorials that I publish for free. If you'd like to say thanks, please share this tutorial with others and/or buy me a coffee!
Comments
Leave your questions, problems, suggestions, requests and thanks here!
To share very long code or error messages with me, please upload it to pastebin (or a similar site) and include the link. This is to make sure you don't hit the max character limit on comments.