DEV Community

Cover image for A Basic Introduction to Webpack
skaytech
skaytech

Posted on • Originally published at blog.skay.dev

A Basic Introduction to Webpack

Introduction

In this article, I will introduce the core concepts with Webpack in a practical easy to follow manner. I will explain setting up webpack configuration file from scratch and what each of the configuration such as entry point, css, loaders, plugins mean.

What many people may not realise is that Webpack is what runs under the hood when you are bootstraping an Angular or a React project with an angular-cli or create-react-app.

I have written this article as a follow-along. You could get the entire code for the webpack starter described in the article from the Github repository.

What is Webpack?

Webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every module your project needs and generates one or more bundles.

The illustration shown below can explain it in a much simpler way as to what Webpack actually does.

https://cdn.hashnode.com/res/hashnode/image/upload/v1599234539688/5qLIgwl0R.png

How does Webpack help?

Let us take an example of any web application. It typically comprises of an index.html and within it referencing many script tags as shown below.

<body>

  ...

  <script src='src/blog.js'></script>
  <script src='src/about.js'></script>
  <script src='src/contact.js'></script>
  <script src='src/index.js'></script>
</body>

Enter fullscreen mode Exit fullscreen mode

While the above way of including script tags within a HTML page works, it also presents a couple of challenges such as:

  • The script tags need to be included in a certain order. This is necessary, so that, the script referencing a function inside another script is loaded before itself. In the above example, about.js, contact.js and blog.js must be loaded into the browser before index.js, since index.js is most likely to refer a function within each of the other scripts.
  • In addition, the above method is error prone to typos.

Webpack precisely solves this problem and by using a bundler, you do not have to worry about including every script tag within your index.html and certainly not worry about the order.

<body>

  ...

  <script src='dist/index_bundle.js'></script>
</body>

Enter fullscreen mode Exit fullscreen mode

Module bundling is just one aspect which Webpack solves. But, certainly it is much much more powerful in terms of having the power to apply transformations to your HTML, CSS and JS files before including them in the bundler. Let us jump right into how to install and setup Webpack.

Installing Webpack

To start with, you'll need to install the following two packages to use Webpack.

//Create a new folder
$ mkdir webpack-example

//Initialize a new NPM projects (Creates a package.json with default values)
> webpack-example$npm init -y

//Include the packages webpack and webpack-cli as dev dependencies
> webpack-example$npm install webpack webpack-cli --save-dev

Enter fullscreen mode Exit fullscreen mode

Things to Note:

  • If you wish to follow along, please create a new folder. Open your terminal, cd into the new folder and run the above set of command.
  • The option '—save-dev' adds the packages into your package.json as a dev dependency. What this means is that these packages will not be included with your final production build.

The Configuration File

The file webpack.config.js is the main place where most of the action happens. It is where you will provide a set of instructions to the 'Webpack' to let it know what to do with your project files and how to bundle them in a way you would like to consume.

Note: As per Webpack official docs, as of Webpack 4.0.0, you don't need a configuration file to setup the bundler. However, the configuration file is probably the most important part of Webpack that you'll need to customize as per your project needs.

We will cover the following core concepts of the Config file:

  • Entry
  • Loaders
  • Output
  • Plugins
  • Mode

Before, we begin looking at the concepts, first create the webpack.config.js at the root structure of your project.

> webpack-example$ touch webpack.config.js

Enter fullscreen mode Exit fullscreen mode

Webpack does the following in the specified order:

  • How do I located the files that I need to bundle? Or optionally apply transformations on?
  • What do I need to do once I access those files? Do I need to apply any specific transformations?
  • Where do I need to output (save) the bundle generated by me?

The Entry Point

The single file that kicks off everything is typically the entry point for your Webpack. It is generally an 'index.js' or an 'app.js'.

You can visualise the import structure shown below, as sort of how the Webpack creates the dependency graph.

index.js
  imports about.js
  imports contact.js
  imports blog.js
    imports util.js
    imports api.js

Enter fullscreen mode Exit fullscreen mode

Let us create the 'index.js' within the app folder.

> webpack-example$ mkdir app

> webpack-example$ cd app

> webpack-example$ touch index.js

Enter fullscreen mode Exit fullscreen mode

Let us add the entry point to the webpack.config.js

module.exports {
    entry: './app/index.js'
}

Enter fullscreen mode Exit fullscreen mode

Loaders

Now that we've setup the entry point, the next thing to tell our Webpack is what should it do with the files within our project. In other words, what kind of transformations that need to be applied to our files.

To do that, we have something called as the 'loaders'. By default, Webpack looks at all the JSON and JS files to build the dependency graph as shown above.

import contact from './app/config' // 👍
import config from './utils/config.json' // 👍
import './css/app.css' // ❌

Enter fullscreen mode Exit fullscreen mode

In the above, the CSS import will be ignored by Webpack and Loaders is precisely what we need here to assist Webpack to help process files other than JS and JSON.

Let us look at how to the following steps to add a CSS Loader.

> webpack-example$ npm install css-loader --save-dev

Enter fullscreen mode Exit fullscreen mode

We will need to add the loaders within the property 'modules.rules' array. Webpack will look the rules array, to determine the loaders setup and the associated rules for each file types.

module.exports = {
  entry: './app/index.js',
  module: {
    rules: []
  }
}

Enter fullscreen mode Exit fullscreen mode

We will need to specify the loader properties within the rules array. Every loader has 2 attributes that need to be define:

  • use - The name of the loader.
  • test - The regex to match the file path.
module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\\.css$/, use: 'css-loader' }
    ]
  }
}

Enter fullscreen mode Exit fullscreen mode

Now, if we use a CSS anywhere within our project, Webpack will recognize it through the help of loaders and import it within our project. Basically, the above CSS import statement which had a ❌ will now have a 👍.

import contact from './app/config' // 👍
import config from './utils/config.json' // 👍
import './css/app.css' // 👍

Enter fullscreen mode Exit fullscreen mode

DOM Injection

While we have succeeded importing a CSS file using webpack configuration, there's one more thing that needs to be done. We will need to inject the style elements into the DOM.

In simple words, the CSS style needs to be included as a 'style' tag within the HTML.

To help us achieve that, we will need to use the 'style-loader'.

> webpack-example$ npm install style-loader --save-dev

Enter fullscreen mode Exit fullscreen mode

And modify the webpack.config.js to add the 'style-loader' to modules.rule array.

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\\.css$/, use: [ 'style-loader', 'css-loader' ] }
    ]
  }
}

Enter fullscreen mode Exit fullscreen mode

Things to Note:

  • Since there were 2 variables to be used within the 'use' property, we changed it to an array.
  • The order of mentioning the loader is important, since, webpack will process them in the reverse order. So, the 'css-loader' will first interpret the import command and then the 'style-loader' will inject the CSS into the DOM.

There are a lot more things a loader can do and a very popular example is the use of Babel. Babel is used to transform every JavaScript file to the appropriate ES syntax based on the configuration specified in the webpack.config.js.

You can check the full list of loaders over here.

Output

As the name suggests, this configuration parameter simply tells the Webpack where to output the bundle that it creates.

//Import 'path' to resolve the file path
const path = require('path');

//Add this configuration after module.rules config
output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
}

Enter fullscreen mode Exit fullscreen mode

Things to Note:

  • We are importing the 'path' package to help with resolving the file path.
  • The output configuration requires the path parameter to inform webpack as to where to save the bundle file. In addition, using the filename parameter, you can specify the 'name' of the bundle that is generated.

Quick Recap

So until this point, we have seen the entry, loader and output configurations. Combining all the configurations the webpack.config.js looks like this:

//Import 'path' to resolve the file path
const path = require('path')

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\\.css$/, use: [ 'style-loader', 'css-loader' ] }
    ]
  },
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  }
}

Enter fullscreen mode Exit fullscreen mode

A Quick Recap:

  1. Webpack finds out the entry point located at ./app/index.js.
  2. It examines all the import and require statements within the project and creates a dependency graph.
  3. Then it starts creating a bundle, whenever it comes across a path we have a loader for, it transforms the code according to that loader then adds it to the bundle.
  4. Finally, It bundle and outputs it the location mentioned in the configuration which is at dist/index_bundle.js.

Hope you are able to follow up-to this point. We have two more configurations 'plugins' and 'mode' to cover. So, just hang tight, we are almost there.

Plugins

While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimisation, asset management and injection of environment variables.

Plugins allow you to execute certain tasks after the bundle has been created. Because of this, these tasks can be on the bundle itself or simply applied to the source code base.

Let us look at two examples of such Plugins:

HtmlWebpackPlugin

Earlier we saw that the main benefit of webpack was that it would generate a single bundle for us that we could then use to reference inside of our main index.html page.

What HtmlWebpackPlugin does is that, it will generate the index.html page for us, stick it inside of the same directory where our bundle is put, and automatically include a <script> tag which references the newly generated bundle.

As always, the first step involves downloading the plugin from npm.

> webpack-example$ npm install html-webpack-plugin --save-dev

Enter fullscreen mode Exit fullscreen mode

Next, we add a plugins property which is an array to our webpack config.

//Import the HtmlWebpackPlugin
const HtmlWebpackPlugin = require('html-webpack-plugin')

//Add this to the module.exports configuration
plugins: [
    new HtmlWebpackPlugin()
]

Enter fullscreen mode Exit fullscreen mode

Now your entire webpack.config.js will look like this:

//Import 'path' to resolve the file path
const path = require('path')

//Import the HtmlWebpackPlugin
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\\.css$/, use: [ 'style-loader', 'css-loader' ] }
    ]
  },
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  },
    plugins: [
    new HtmlWebpackPlugin()
    ]
}

Enter fullscreen mode Exit fullscreen mode

HtmlWebpackPlugin is one of the most basic plugins available, you can check out the rest of plugins offered by Webpack over here.

MiniCssExtractPlugin

MiniCssExtractPlugin is the second example of plugin that we will look at. If you remember from the earlier example, we had used a Style-loader, but using this plugin we can completely separate the CSS file instead of inserting only the style elements of the CSS.

As usual, we will start with installing the dependency as the first step:

> webpack-example$ npm install --save-dev mini-css-extract-plugin

Enter fullscreen mode Exit fullscreen mode

Add the following to webpack.config.js file:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

plugins: [
  new MiniCssExtractPlugin(),
],

Enter fullscreen mode Exit fullscreen mode

And last but not the least, we will replace the style-loader with MiniCssExtractPlugin.loader:

{
  test: /\\.css$/,
  use: [
    MiniCssExtractPlugin.loader, // instead of style-loader
    'css-loader'
  ]
}

Enter fullscreen mode Exit fullscreen mode

Now when you run webpack, it will output main.css file in the dist folder and will be referenced from the index.html file using the link header.

Mode

Using the mode parameter you can enable webpack's built-in optimizations based on the value 'development', 'production' or 'none'. The default value is 'production'.

mode: 'production' //Other values include 'development' or 'none'

Enter fullscreen mode Exit fullscreen mode

An example of build optimization run by Webpack when the mode is set to Production is that it'll minify the code and strip out warnings. For a detailed list of optimizations run behind the scenes, you can read it over here.

Running Webpack

If you've followed along, your webpack.config.js at this point should look like this:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [{ test: /\\.css$/, use: ['style-loader', 'css-loader'] }],
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js',
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'production',
};

Enter fullscreen mode Exit fullscreen mode

Let us now proceed to run Webpack. First, we'll need to add the following configuration to 'package.json'.

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

Enter fullscreen mode Exit fullscreen mode

Now run the following command from your command terminal.

> webpack-example$ npm run build

Enter fullscreen mode Exit fullscreen mode

Webpack will execute and create an optimized bundle named index_bundle.js and put it inside of the dist directory.

You should see the final index.html created within the 'dist' folder.

Webpack DevServer

Webpack DevServer is a development server for webpack, that'll keep track of your files in memory and serve them via a local server instead of generating a dist directory.

But the best part is that, it supports live reloading. What that means is whenever you make a change in your code, webpack-dev-server will quickly recompile your code and reload the browser with those changes.

We'll need to install the package from npm.

> webpack-example$ npm install webpack-dev-server --save-dev

Enter fullscreen mode Exit fullscreen mode

Then we will need to update "scripts" attribute in the package.json to run webpack-dev-server.

"scripts": {
  "start": "webpack-dev-server"
}

Enter fullscreen mode Exit fullscreen mode

Also, we'll make a small change to our 'mode' parameter in the webpack.config.js to default to 'development' if 'production' is not specified in the environment variable.

mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'

//If you’re on Windows, then the command is as follows:
"SET NODE_ENV='production' && webpack"

Enter fullscreen mode Exit fullscreen mode

That's it! If you run 'npm start' from the terminal then your project will be served at http://localhost:8080.

> webpack-example$ npm start

Enter fullscreen mode Exit fullscreen mode

The complete project with customization is available in the following Github Repository.

Conclusion

While this turned out to be a long article, to be truthful, we have barely touched the surface of Webpack and the endless possibilities of the configuration it supports.

To summarize, we covered the following in this article:

  • What is Webpack and What benefit it provides?
  • The core components of Webpack:
    • Entry Point
    • Loaders
    • Output
    • Plugins
    • Mode
  • Running a Webpack - Local & Production Mode

I hope you found the article useful. If you find my articles interesting, please do not forget to subscribe to my newsletter.

You may also be interested in:

Top comments (4)

Collapse
 
quyethn2 profile image
quyethn2

i have been read more article about webpack, but this is good article :D

Collapse
 
skaytech profile image
skaytech

Thank you 🙏 Means a lotm Please do share for better reach.

Collapse
 
kumarakh profile image
akhilesh kumar ojha

Thanks for sharing very good article about webpack..

Collapse
 
skaytech profile image
skaytech

Thank you. I'm thinking I'll probably write another article with Webpack and Babel and provide with a starter template for the same. Please feel free to share for a larger reach.