Skip to content Go to Sitemap
You're 0% done reading!

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 your package.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 folder public if you prefer.)
    • The /dist/index.html file will be your homepage. You should create this one now.
  • 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).
  • 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 and package.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.js
    • filename: this will be the name of your generated JavaScript file
    • path: 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 your src folder (e.g. in src/img and src/fonts). All assets that you use in your CSS and JavaScript will be copied into the dist folder by Webpack. Remember not to change the generated files in the dist folder; Only manage your assets in the src 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!

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.

Comment Widget could not be loaded!