webpack By Example: Part 2

This article is part of a series (starting with webpack By Example: Part 1) of articles that, through a number of progressively more complicated examples, explores the purpose and functionality of webpack. The examples’ configuration files and source code is available as a GitHub repository.

The examples in this article focus on incorporating CSS and assets (e.g., images) into our projects.

require

Before we explore including CSS files in our project, we will split our JavaScript code into two files and use module.exports and require to export / import functionality.

src/index.js

var secondary = require('./secondary');
window.console.log('hello world');
secondary();

src/secondary.js

module.exports = function() {
window.console.log('hello secondary');
};

By choosing No on all the questions during the execution of webpack-cli init (from the command line in the project folder) the we end up a minimal webpack.config.js file (same as in all previous examples).

Executing the command ./node_modules/.bin/webpack from the command line in the project folder will create a main.bundle.js file in the dist folder. 
To use the bundled file, create the index.html file into the dist folder and open it a browser; the developer console will display hello world followed by hello secondary.

dist/index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>webpack Patterns</title>
</head>
<body>
<script src="main.bundle.js"></script>
</body>
</html>

css

In this example, the second file is a css stylesheet and we import it using require.

src/index.js

require('./index.css');
window.console.log('hello world');

src/index.css

body {
background-color: yellow;
}
#root {
height: 400px;
background-color: purple;
}

By choosing No on all the questions, except selecting CSS for the CSS solutions question, during the execution of webpack-cli init (from the command line in the project folder) the we end up with a new block in the webpack.config.js file.

note: As of version 1.3.1, webpack-cli produces a non-functional configuration in this case (I manually corrected). By the time you read this, however, the fix is likely to have been incorporated into the next release.

To correct, I ran npm install --save-dev style-loader and updated a portion of the configuration file.

webpack.config.js

...
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
}
]
}
]
},
...

In this example, we using we are using two webpack loaders.

Loaders allow you to preprocess files as you require() or “load” them. Loaders are kind of like “tasks” in other build tools, and provide a powerful way to handle frontend build steps. Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs. Loaders even allow you to do things like require() css files right in your JavaScript!

— webpack

The way to interpret the single rule in the webpack.config.js is as follows:

  • Apply this rule to any required file that ends in .css.
  • When applied, first run the css-loader on the file and then the style-loader on its output.
  • css-loader first converts the css file into a JavaScript module (a module form of the css).
  • style-loader accepts the generated JavaScript module and creates a new JavaScript module that, when run, will inject a style tag with the css into the DOM at the end of the head element.
  • The JavaScript module of the last loader, style-loader in this case, is included in the generated bundle.

Executing the command ./node_modules/.bin/webpack from the command line in the project folder will create a main.bundle.js file in the dist folder. 
To use the bundled file, create the index.html file into the dist folder and open it a browser; the developer console will display hello world (as before) but the expected yellow and purple styling has been applied.

dist/index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>webpack Patterns</title>
</head>
<body>
<div id="root"></div>
<script src="main.bundle.js"></script>
</body>
</html>

css-url

In this example, the we update index.css from the url example to use a background image using url syntax; the file cute.jpg is placed in the src folder.

src/index.css

body {
background-color: yellow;
}
#root {
height: 400px;
background-image: url('cute.jpg');
background-size: cover;
background-position: center;
color: white;
}

While css-loader will interpret the url syntax, additional loaders need to be added to support the image (a file).

To support files we run npm install --save-dev file-loader and npm install --save-dev url-loader. Continuing from the last example, we then add the following rule to webpack.config.js :

webpack.config.js

...
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
loader: 'url-loader',
options: {
limit: 10000
}
}
...

The way to interpret this rule in the webpack.config.js is as follows:

  • First, css-loader interprets the url syntax and requires the image file.
  • The new rule is applied on any matching assets, including the image.
  • When applied, the rule runs url-loader on the file.
  • url-loader, as configured, will convert the file into a data URL if less than 10kB and export it (to be used by css-loader); otherwise it uses file-loader on the file.
  • file-loader will copy the file (renaming with a hash) to the distribution folder and export the relative URL of the file.
  • In either case, url-loader or file-loader, css-loader uses the exported value to fill in the url syntax.

Executing the command ./node_modules/.bin/webpack from the command line in the project folder will create a main.bundle.js file in the dist folder as well as the image file named with a hash, e.g., d09dbe3a95308bb4abd216885e7d1c34.jpg.

To use the bundled file, open the index.html file in a browser; the developer console will display hello world (as before) but the purple block is replace with an image of a cute cat.

In this example the injected style tag looks like this.

<style type="text/css">
body {
background-color: yellow;
}
#root {
height: 400px;
background-image: url(d09dbe3a95308bb4abd216885e7d1c34.jpg);
background-size: cover;
background-position: center;
color: white;
}
</style>

image

In the last example, we triggered url-loader (and subsequently file-loader) indirectly through css-loader. We can also more directly import images from JavaScript and insert it into the DOM.

note: Typically one is using a front-end framework, e.g., React, that provides a simple way of interacting with the DOM. As I wanted this example to be about webpack, not a front-end framework, I used vanilla JavaScript.

Using the same webpack.config.js as the previous example, we update index.js and index.css.

src/index.js

require('./index.css');
var cute = require('./cute.jpg');
var rootEl = document.getElementById('root');
var imageEl = document.createElement('img');
imageEl.src = cute;
rootEl.appendChild(imageEl);

src/index.css

body {
background-color: yellow;
}

Executing the command ./node_modules/.bin/webpack from the command line in the project folder will create a main.bundle.js file in the dist folder as well as the image file named with a hash (as before).

To use the bundled file, open the index.html file in a browser; instead of being a background image the cute cat image is directly inserted in the DOM.

The Next Part

In the next part, webpack By Example: Part 3, we will streamline the workflow and revisit making production-ready distributions.

Like what you read? Give John Tucker a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.