A Beginner's Guide To Webpack - SitePoint
A Beginner's Guide To Webpack - SitePoint
Ivaylo Gerchev
JavaScript Web
Nowadays, we’re forced to use many accessory tools to facilitate, speed up and optimize
our web development workflow. Often, though, such tools add an extra layer of
complexity into the stack. As a result, we need to utilize additional time and effort to learn,
understand and use these tools correctly. The same is true for webpack.
When using webpack for the first time, it can be difficult to understand how it works and
how it should be used. Although it has good documentation, it can be daunting for novices,
and it has a steep learning curve. However, webpack is worth learning and can save
considerable time and effort in the long run. In this tutorial, I’ll introduce all the core concepts
to help you get started.
Note: in this tutorial I’ve used webpack 5.9.0.
Key Takeaways
Webpack Basics: Webpack is a static module bundler that treats all files and assets as
modules, building a dependency graph to generate a single or multiple bundles for web
deployment.
Core Concepts Overview: Understand the key concepts such as entry, output, loaders,
plugins, and mode settings to effectively use Webpack in different environments
(development, production).
Webpack 5 Enhancements: Version 5 introduces features like Persistent Caching,
improved Tree Shaking, and removal of automatic Node.js polyfills, enhancing
performance and reducing bundle sizes.
Getting Started with Webpack: Initiate a project with Webpack by setting up a basic
configuration file, understanding default settings, and utilizing plugins like `html-
webpack-plugin` for dynamic HTML generation.
Advanced Usage: Learn to handle CSS with style and css-loaders, manage assets with
built-in modules replacing older loaders, and optimize your development process with
Webpack’s dev server for live reloading.
Best Practices for Production: Utilize Webpack’s capabilities to transpile modern
JavaScript, manage styles and assets, and speed up development with tools like
webpack-dev-server and optimizations for production builds.
What Is Webpack?
As its core, webpack is a static module bundler. In a particular project, webpack treats all
files and assets as modules. Under the hood, it relies on a dependency graph. A dependency
graph describes how modules relate to each other using the references (require and import
statements) between files. In this way, webpack statically traverses all modules to build the
graph, and uses it to generate a single bundle (or several bundles) — a JavaScript file
containing the code from all modules combined in the correct order. “Statically” means that,
when webpack builds its dependency graph, it doesn’t execute the source code but stitches
modules and their dependencies together into a bundle. This can then be included in your
HTML files.
Now, to expand the above cursory overview, let’s explore the main concepts webpack uses.
Getting Started
Note: you can find the files for our project in the GitHub repo.
Now that we have a solid theoretical foundation, let’s implement it in practice.
To start, we’ll create a new directory and switch to it. Then we’ll initialize a new project:
mkdir learn-webpack
cd learn-webpack
npm init -y
Next, we need to install webpack and webpack CLI (command line interface) locally:
Now, the content of the generated package.json should be similar to the following:
{
"name": "learn-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.9.0",
"webpack-cli": "^4.2.0"
}
}
Besides being a package manager, npm can be used as a simple task runner. We can create
webpack tasks by including the name of our task followed by its instructions in the
scripts section of the package.json file. Let’s try this now. Open package.json and
change the scripts object to the following:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
Within the scripts property, npm allows us to reference locally installed Node.js packages
by their names. We use that and the --mode flag to define dev and build tasks, which
will run webpack in development ( npm run dev ) and production ( npm run build ) mode
respectively.
Before we test the tasks we’ve just created, let’s create a src directory and put an
index.js file in it so that it contains console.log("Hello, Webpack!"); . Now we can
already run the dev task to start webpack in development mode:
$ npm run dev
As I mentioned before, webpack sets the default entry point to ./src/index.js and the
default output to ./dist/main.js . So what webpack does when we run the dev task is
to get the source code from index.js file and bundle the final code in a main.js file.
Great! It works as expected. But to verify that we get the correct output, we need to display
the result in the browser. To do that, let’s create an index.html file in the dist directory:
<!doctype html>
<html>
<head>
<title>Getting Started With Webpack</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
Now, if we open the file in the browser, we should see the Hello, Webpack! message in the
console.
Webpack Console Message Displayed
So far, so good. But writing our index.html file manually can be problematic in some
cases. For example, if we change the name of our entry point, the generated bundle will be
renamed, but our index.html file will still reference the old name. So, we’ll need to update
our HTML file manually every time we rename an entry point or add new one. Fortunately,
we can easily fix that with the html-webpack-plugin . Let’s install it now:
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Output",
}),
],
};
ADVERTISEMENT
As you can see, to activate a webpack plugin, we need to include it in the file and then add it
to the plugins array. If needed, we also pass options to the plugin. See the html-
webpack-plugin repo for all available options and the ability to write and use your own
templates.
Let’s run webpack now to see what will happen:
Let’s open the index.html . As we can see, the plugin automatically creates an updated
index.html file for us, which uses the title option from the configuration:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack Output</title>
<meta name="viewport" content="width=device-width, initial-scale=1"
<script defer src="main.js"></script>
</head>
<body>
</body>
</html>
Let’s now expand our project and specify custom names for the entry and output
properties. In webpack.config.js we add the following before the plugins property:
entry: {
main: path.resolve(__dirname, './src/app.js'),
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'deploy')
},
Here, we change the entry file to app.js and the output folder to deploy . We also tweak
the name of the generated bundle file slightly. Now it will start with the name of the entry
(“main”) followed by the word “bundle” and the .js file extension.
Now, we’ll create an src/component.js file:
element.innerHTML = text;
return element;
};
Next, we rename index.js to app.js to reflect our changes, and replace its content with
the following:
document.body.appendChild(component());
Let’s examine and clarify the information from the webpack output. After the “Compilation
finished” message you can see the files generated in the deploy directory
( main.bundle.js and index.html ). Below them, you can see the source files: the entry
module ( app.js ) and its dependency ( component.js ).
So now, in the deploy folder, we have the newly generated bundle file main.bundle.js .
If we open the index.html file in the browser, we should see Hello, Webpack! displayed on
the page.
Webpack Browser Message Displayed
Also, if we check the source of index.html , we’ll see that the value of the src property in
the script tag is updated to main.bundle.js .
At this point, we can delete the dist folder, which webpack generated initially, because we
won’t need it anymore.
Here, I run webpack with devtool option set to inline-source-map in order to render
the code more readable. This way I can demonstrate the code transpilation from ES6 to ES5
more clearly.
Next, let’s open main.bundle.js :
/***/ "./src/component.js":
/*!**************************!*\
!*** ./src/component.js ***!
\**************************/
/*! namespace exports */
/*! export default [provided] [no usage info] [missing usage info preve
/*! other exports [not provided] [no usage info] */
/*! runtime requirements: __webpack_exports__, __webpack_require__.r, _
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => __WEBPACK_DEFAULT_EXPORT__
/* harmony export */ });
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((text
const element = document.createElement("h1");
element.innerHTML = text;
return element;
});
/***/ })
As you can see, the modern ES6 features (the arrow function and the const declaration)
from component.js module are not transformed to ES5-compliant code by default. To
make our code work in older browsers, we must add the Babel loader:
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
]
},
When we define rules for a webpack loader, there are usually three main properties we need
to define:
test , which describes what kind of files should be transformed.
exclude , which defines the files that shouldn’t be processed from the loader(s), if we
have such.
use , which tells which loader(s) should be used against the matched modules. Here, we
can also set the loader options, as we’ve just done with the presets option.
Run the following command again:
npm run dev -- --devtool inline-source-map
/***/ "./src/component.js":
/*!**************************!*\
!*** ./src/component.js ***!
\**************************/
/*! namespace exports */
/*! export default [provided] [no usage info] [missing usage info preve
/*! other exports [not provided] [no usage info] */
/*! runtime requirements: __webpack_exports__, __webpack_require__.r, _
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => __WEBPACK_DEFAULT_EXPORT__
/* harmony export */ });
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (functi
var text = arguments.length > 0 && arguments[0] !== undefined ? argum
var element = document.createElement("h1");
element.innerHTML = text;
return element;
});
/***/ })
Perfect. Now we can use the modern JS features, and webpack will transform our code so it
can be executed by older browsers.
css-loader parses the CSS into JavaScript and resolves any dependencies
style-loader outputs our CSS into a <style> tag in the HTML document.
module: {
rules: [
...
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
]
},
Here, the order of loaders is important. They’re evaluated in reverse order — that is, from
right to left and from bottom to top. In our case, the css-loader is evaluated first, followed
by the style-loader .
Now, let’s create a file src/style.css :
ADVERTISEMENT
h1 {
color: red;
}
import './style.css';
When we run webpack ( npm run dev ) and then open the index.html , we should see the
Hello, Webpack! message in red color.
Webpack Browser Message With Style Applied
Asset Management
Most often your project will contain assets such as images, fonts, and so on. In webpack 4,
to work with assets, we had to install one or more of the following loaders: file-loader ,
raw-loader , and url-loader . In webpack 5, as we saw earlier, this is not needed
anymore, because the new version comes with the built-in asset modules.
Here, we’ll explore an example with images. Let’s add new rule in the webpack.config.js :
module: {
rules: [
...
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
]
},
Here, we import our image as a module and use it to create an <img/> tag. To make the
above code work, you need to download the image and then rename it to image.png and
put it in the src directory.
The next thing is to import our image component in app.js :
import './image-component';
And voila. Now, when we run webpack ( npm run dev ) and open the page, we should see
the image above the Hello, Webpack! message.
Webpack Image Component Displayed
If you take a look at the deploy folder right now, you’ll find three files generated in it:
a1af828b4e65d37668e1.png , main.bundle.js , and index.js . Here’s what webpack
does behind the scenes: the image is added to the deploy folder and assigned a unique
hash, followed by the image extension. The image is then included in the newly generated
main.bundle.js file as a module. Finally, an index.html file is generated with reference
to the main.bundle.js file.
Now let’s configure the server in webpack.config.js by adding the following property
after the output :
devServer: {
contentBase: './deploy',
open: true
},
This tells webpack-dev-server to serve the files from the deploy directory and to open
the entry page automatically.
Now, if we run webpack ( npm run dev ), we should see how the page is automatically
opened in the browser on http://localhost:8080.
Note: After running the webpack-dev-server you won’t find any files in the deploy folder (it
will be empty) because the server doesn’t write any output files after compiling. Instead, it
keeps bundle files in memory and serves them as if they were real files mounted at the
server’s root path. See the webpack development guide for more information. However,
when you run the build command, the deploy folder will be populated with the
generated files as expected.
If we now change any of the source files and save them, the web server will automatically
reload the page after the code has been compiled. Try to change the color property in our
CSS file to green, for example, and you should see how the color is updated appropriately in
the page.
Webpack Development Server In Action
In webpack.config.js :
...
plugins: [
...
new CleanWebpackPlugin()
],
Now, run webpack ( npm run build ) and inspect the deploy folder. You should now only
see the files generated from the build without old and unused files. To test it, create a simple
text file which is not used in the project and run the build script again. After the
compilation the file will be deleted.
Conclusion
Webpack is a useful and powerful tool. This tutorial introduces only the core concepts, but
webpack offers many more features, plugins, and different techniques to apply them, which
you can adopt as your knowledge grows. Here’s a list of resources I suggest for further
exploration of webpack’s capabilities:
Official webpack Documentation. The documentation offers you structured information
about webpack’s main concepts and configuration, as well as plugins and loaders you can
use in your project, and basic guides and API references.
Webpack 5: From Apprentice to Master. A complete manual which dives deeply into each
webpack aspect. Written by Juho Vepsäläinen, a core developer of webpack.
Webpack: The Core Concepts. A great introductory video course by Sean Larkin, one of
webpack’s maintainers.
Read Next
Accelerating the Cloud:
What to Expect When Going
Cloud Native
Accelerating the Cloud: Multiple Editors per Node in The Pros and Cons of
What to Expect When Going Drupal 7 Selling on ThemeForest
Cloud Native Daniel Sipos Henry Rise
Nicholas Cravotta
How to Use AMP with How to Write Modular Code How to Build Your Backend
WordPress with Angular UI-Router & with Hasura and
Jason Daszkewicz Named Views PostgreSQL
Thomas Greco Michael Wanyoike
Stuff we do Contact
Premium Contact us
Newsletters FAQ
Learning paths Publish your book with us
Library Write an article with us
Forums Advertise
About Connect
Our story RSS
Corporate memberships Facebook
Terms of use Instagram
Privacy policy Twitter (X)
This site is protected by reCAPTCHA and the Google Privacy
Policy and Terms of Service apply.
Back to top