Webpack is the leading module bundler for React and Redux apps. I think folks using Angular 2 and other frameworks are also using it a lot these days.

When I first saw a Webpack config file, it looked very alien-y :alien: and confusing :scream:. After playing around with it for some time, I now think that it is because Webpack just has a unique syntax and new philosophies that may cause confusion in the beginning. Incidentally, these philosophies are also responsible for making it so popular.

Since it’s confusing to get started, I thought I’ll write a few posts that’ll hopefully make it easy for others to get started and use it’s powerful features.Here is the 1st installment.

Webpack’s Core Philosophy

Two core philosophies of Webpack are:

Everything is a module — Just like JS files can be “modules”, everything else (CSS, Images, HTML) can also be modules. That is, you can require(“myJSfile.js”) or require(“myCSSfile.css”) . This mean we can split any artifact into smaller manageable pieces, reuse them and so on.

Load only “what” you need and “when” you need — Typically module bundlers take all the modules and generate a large single output “bundle.js” file. But in many real-world apps, this “bundle.js” could be 10MB-15MB and could take forever to load! So Webpack has various features to split your code and generate multiple “bundle” files, and also load parts of the app asynchronously so that you just load what you need and when you need it.

OK, Let’s take a look at various “confusing” parts.

1. Development Vs Production

First thing to be aware of is that Webpack has tonsof features . Some are for “Development-only”, some others are for “Production-only” and some are for both “Production-and-Development”.

Note: You can click on the pictures to zoom and read.

A sample dev v/s prod Webpack files

Typically most projects use so many features that they usually have two large Webpack config files.

And to create bundles you’ll write scripts in the package.json like so:

Webpack-dev-server (Good for Development Builds)

This is an Express node.js server that runs at port 8080 . This server internally calls Webpack. The benefit of this is that it provides additional capabilities like reloading the browser i.e. “ Live Reloading ” and/or replacing just the changed module i.e “ Hot Module Replacement” (HMR) .

Usage:

OPTION 1:

//Install it globallynpm install webpack-dev-server --save

//Use it at the terminal$ wepback-dev-server --inline --hot

OPTION 2:

// Add it to package.json's script

“scripts”: { “start”: “webpack-dev-server --inline --hot”, ... }

// Use it by running $ npm start

Open browser at:http://localhost:8080

Webpack Vs webpack-dev-server options

It’s worth noting that some of the options like “inline” and “hot” are webpack-dev-server only options. Where as some others like “hide-modules” are CLI only options.

webpack-dev-server CLI options Vs config options

The other thing to note is you can pass options to webpack-dev-server in two ways:

Through webpack.config.js’s “devserver” object.

Through CLI options.

//Via CLIwebpack-dev-server --hot --inline

//Via webpack.config.jsdevServer: { inline: true, hot:true }

I’ve found that sometimes the devServer config (hot:true and inline:true) doesn’t work! So I prefer just passing options as CLI options within package.json like so:

“hot” Vs “inline” webpack-dev-server options

“inline” option adds “Live reloading” of the entire page. “hot” option enables “Hot Module Reloading” that tries to reload just the component that’s changed (instead of the entire page). If we pass both options, then, when the source changes, the webpack-dev-server will try to HMR first. If that doesn’t work, then it will reload the entire page.

//When the source changes, all 3 options generates new bundle but,

//1. doesn't reload the browser page$ webpack-dev-server

//2. reloads the entire browser page$ webpack-dev-server --inline

//3. reloads just the module(HMR), or the entire page if HMR fails$ webpack-dev-server --inline --hot

3. “entry” — String Vs Array Vs Object

Entrytells the Webpack where the root module or the starting point is. This can be a String, an Array or an Object. This could confuse you but the different types are used for different purposes.

If you have a single starting point (like most apps), you can use any format and the result will be the same.

Different entry types but same output

entry — Array

But, if you want to append multiple files that are NOT dependent on each other , you can use the Array format.

For example: you may need “googleAnalytics.js” in your HTML. You can tell Webpack to append it to the end of the bundle.js like so:

entry — object

Now, let’s say you have true multi-page application, not a SPA w/ multi-views, but with multiple HTML files (index.html and profile.html). You can then tell Webpack to generate multiple bundles at once by using entry object.

The below config will generate two JS files: indexEntry.js and profileEntry.js that you can use in index.html and profile.html respectively.

Usage:

//profile.html<script src=”dist/profileEntry.js”></script>

//index.html<script src=”dist/indexEntry.js”></script>

Note: The name of the file comes from the “entry” object’s keys.

entry — combination

You can also use the Array type entries inside an entry object. For example the below config will generate 3 files: vendor.js that contains three vendor files, an index.js and a profile.js.

4. output — “path” Vs “publicPath”

outputtells the Webpack where and how to store the resulting files. It has two properties “path” and “publicPath” that could be confusing.

“path” simply tells the Webpack where it should store the result. Where as “publicPath” is used by several Webpack’s plugins to update the URLs inside CSS, HTML files when generating production builds.

publicPath in Development vs Production

For example, in your CSS, you may have a url to load ‘./test.png’ on your localhost. But in production, the ‘ test.png’ might actually be located at a CDN while your node.js server might be running on Heroku . So that means, you’ll have to manually update the URLs in all the files to point to the CDN when running in Production.

Instead, you can use Webpack’s publicPath and use bunch of plugins that are publicPath-aware to automatically update URLs when generating production builds.

Note: You can click on the pictures to zoom and read

publicPath Production example

// Development: Both Server and the image are on localhost.image { background-image: url(‘./test.png’); }

// Production: Server is on Heroku but the image is on a CDN.image { background-image: url(‘https://someCDN/test.png’); }

5. Loaders And Chaining Loaders

Loaders are additional node modules that help ‘load’ or ‘import’ files of various types into browser acceptable formats like JS, Stylesheets and so on. Further loaders also allow importing such files into JS via “require” or “import” in ES6.

For example: You can use babel-loader to convert JS written in ES6 to browser acceptable ES5 like so:

Here is how it works:

Webpack searches for CSS files dependencies inside the modules. That is Webpack checks to see if a JS file has “ require(myCssFile.css) ”. If it finds the dependency, then the Webpack gives that file first to the “ css-loader”

css-loader loads all the CSS and CSS’ own dependencies (i.e @import otherCSS) into JSON. Webpack then passes the result to “style-loader”.

style-loader to take the JSON and add it to a script tag — <script>CSS contents</script> and inserts the tag into the index.html file.

6. Loaders Themselves Can Be Configured

Loaders themselves can be configured to work differently by passing parameters.

In the example below, we are configuring url-loader to use URLs for images larger than 1024 bytes and embed images that are less than 1024 bytes. We can do this by passing “limit” parameter in the following two ways:

7. The .babelrc file

babel-loader uses “presets” configuration to know how to convert ES6 to ES5 and also how to parse React’s JSX to JS. We can pass the configuration via “query” parameter like below:

8. Plugins

Plugins are additional node modules that usually work on the resulting bundle.

For example, uglifyJSPlugin takes the bundle.js and minimizes and obfuscates the contents to decrease the file size.

Similarly extract-text-webpack-plugin internally uses css-loader and style-loader to gather all the CSS into one place and finally extracts the result into a separate external styles.css file and includes the link to style.css into index.html

9. Loaders Vs Plugins

As you might have realized, Loaderswork at the individual file level during or before bundle is generated.

Where as Pluginswork at bundle or chunk level and usually work at the end of the bundle generation process . And some Plugins like commonsChunksPlugins go even further and modify how the bundles themselves are created.

10. Resolving File Extensions

Many Webpack config files have a resolve extensions property that has an empty string like shown below. The empty string is there to help resolve imports without extensions like: require(“./myJSFile”) or import myJSFile from ‘./myJSFile’ without file extensions .