Cooking With Webpack Part 1 - Introduction and Basic Implementation

In part 1 of this series, we take a plunge into webpack, the webpack cli, the webpack configuration file, and creating our first bundle.

Cooking With Webpack Part 1 - Introduction and Basic Implementation

I’ll begin this article the way many a webpack article begins - with a simple, underwhelming, and slightly confusing description of what webpack is. From their docs:

Webpack is a module bundler. Webpack takes modules with dependencies and generates static assets representing those modules.

In this series of tutorials though, I promise to take you way beyond the basics, and into some of the more intricate parts of webpack.

In a most basic sense, webpack looks at a source file, examines its dependencies, and compiles an output bundle. So if our source file requires another modules (CommonJS of AMD), webpack will do some stuff under the hood in the build and bundle in that module, producing a file that’s ready for consumption in any JavaScript environment.

Something that confused me up front as well was the endless commingling of the command line interface as well as the configuration file. When webpack runs, it looks for both of these things, and often times, a lot of command line clunkiness can be moved into the webpack configuration file. In these guides, I’ll be primarily using the configuration file with occasional command line arguments.

Setting Up Our Project

First things first, let’s get a project set up by running:

npm init

Let’s also create a very basic folder structure, and add an empty JavaScript file to it:

mkdir src && touch src/index.js

Then, let’s add webpack as a development dependency:

npm install webpack --save-dev

We’re now able to run webpack scripts from our project as npm scripts. Let’s add a basic npm script to our package.json file:

  "scripts": {
    "build": "webpack"

Now, we can run npm run build. Naturally though, there are errors. Notably, it’s telling us that we don’t have an output filename configured, and pointing us to the cli docs.

Making Our First Bundle

At its core, webpack needs an entry point and some output configurations.We can achieve this via the updating our build task to this:

  "scripts": {
    "build": "webpack --entry ./src/index.js --output-filename ./dist/index.bundle.js"

Here, we’re using the --entry argument to specify the entry file, and the --output-filename argument to specify where our bundle should compile to. If you rerun npm run build, you’ll see that webpack successfully created the bundle at dist/index.bundle.js. It should contain some webpack boilerplate code, which we’ll get into later.

We’re not going to continue on with the cli though. Instead, we’re going to create a configuration file.

Setting Up The Configuration File

Webpack configuration files can be supplied via the --config argument in the command line:

  "scripts": {
    "build": "webpack --config ./config.js"

The above assumes that a webpack configuration file named config.js was found. However, by default, the webpack task looks for the webpack.config.js file in the same directory as it gets run from. So we can just revert back to this:

  "scripts": {
    "build": "webpack"

And now, we can create our webpack configuration file:

touch webpack.config.js

Let’s now move our command line script from above over to the webpack configuration file. A webpack configuration is just an exported object. For the most part, each of those cli arguments above map to some key-pair values. For example, --entry maps to {entry: ''}, and --output-filename maps to {output: {filename: ''}}. Here’s ours:

const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: path.join(__dirname, 'src/index.js'),
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'index.bundle.js'

Now, once again, if we run npm run build, we’ll see that our entry script gets bundled and compiled to the specified output. This time, we used two output options:

  • path - specifies the output path of our bundle
  • filename - specifies the filename of our bundle

These will demonstrate more importance in the future. For now, it’s just an easy way to split up our configuration into a more digestible format.

So…All That For What Exactly?

At this point, we’ve quite literally taken an empty project, added webpack as a dependency, and created some configuration setup that takes an empty JavaScript file and adds a bunch of weird code to it. Let’s write some actual code, populate our index.js file, require a module in it, and peek under the hood a bit.

First, let’s create a new file called hello.js. This will be a module that we can use in index.js.

touch src/hello.js

Our hello module will accept an argument, name, and return the most wonderful string of all time: “Hello name”. Out of the box, webpack can bundle CommonJS and AMD style modules. For starting purposes, we’ll use CommonJS. Let’s go ahead and write the code for hello.js:

// hello.js
module.exports = function(name) {
  if (name === undefined) {
    name = 'World';

  return 'Hello, ' + name + '!';

It’s pretty simple. Our function accepts a name argument, and if that’s undefined, it assigns the string World to it. It then returns the full string “Hello, name”. Back in our index.js file (which is our entry file for webpack), we can require this module and use it:

// index.js
var hello = require('./hello.js');

console.log(hello()); // should log "Hello, World!"
console.log(hello('Nick')); // should log "Hello, Nick!"

Now, if we run our build task again using npm run build, we’ll get some neat output. We can also verify that our code is working by running it simply with node:

node ./dist/index.bundle.js


Hello, World!
Hello, Nick!

What’s Inside The Compiled File?

If you’re curious like me, then you’re probably wondering what’s happening under the hood. I won’t get into too many details, because I’ve only glanced at the surface. There are a lot of generated comments though, so it’s pretty easy to follow. In a nutshell:

  1. webpack wraps everything in an IIFE, which gets invoked with all the modules (as an array)
  2. webpack creates a __webpack_require__ function, which internally requires modules when needed
  3. webpack kicks off by running the first module (which is the entry module, index.js in our case) using the __webpack_require__ function
  4. all previous require statements get replaced with an invocation of __webpack_require__ with the correct module index from the passed in array

If the above confuses you, then forget about it and don’t dwell too much on it. The good news is that webpack did some heavy lifting for you, and you’re now able to run this code in any JavaScript environment. To quickly see it working in the browser, open it up, go to the dev tools, and copy paste the entire index.bundle.js code into the console. You’ll see it execute perfectly!

Wrap Up

Here, we took a plunge into webpack, and skimmed the surface of its huge well of potential. We took a quick look at the webpack cli, and another look and implementation of the webpack configuration file. Then, we created our first bundle which was making use of CommonJS modules. Next time, we’ll look into loaders! Thanks again for reading, and if you have and questions, comments, or feedback, feel free to send me a tweet.

Next: Cooking With Webpack Part 2 - Understanding & Using Webpack Loaders
Previous: Multi-line Padded Text with the CSS box-decoration-break Property