Understanding webpack and creating a React application with it

March 29, 2017

In this post, we will talk about webpack. What is webpack, what is its use, how to configure it, and then we will apply all of this to create a React web app using some modern stuff, like Babel, to transpile Javascript, and styles with Sass.

What is webpack?

Webpack is a tool that is currently being used in lots of Javascript projects. Let’s answer our first question. What is webpack? Well, webpack is a code bundler. Ok, and what is a bundler? A bundler puts different modules together, with all of its required dependencies, in a single or in a few modules, creating a “dependency graph”.

You can then use “require” or “import” to use these different modules, static files (CSS files, images) and whatever else you need, where it is needed. Besides that, you can also process files to use, for example, Sass in your application styles, or Babel, to transpile Javascript and use features that are still not supported by all browsers.

Installing

I will consider that you already has “Node.js” installed, but if you don’t have it, you can check how to install it clicking here.

In first place, let’s use npm init to create the package.json file and start our project. This file contains general information about your project, such as name, version, dependencies. With it, you can install all the npm modules that your project requires just running the command npm install in the same folder as this file. Give a name to your project and hit Enter for everything else. In the end, type yes to confirm that everything is correct.

npm init

webpack installation is pretty simple. You can install it with npm install. Let’s use the --save-dev flag, which updates the package.json file with the development dependencies.

npm install webpack@2 --save-dev

We will use Version 2 of webpack. This is important, because the configuration options are quite different from Version 1 to 2.

Starting to use

To use webpack, we must create a file named webpack.config.js, in the root folder of your project (the same one where the package.json file is located). This file will define all the required configurations for webpack. In this file, we basically define an object, where each key is a webpack configuration option. We will start with the following code:

module.exports = {

}

Now, let’s start the fun part, which is how webpack really works. Basically, webpack is based on four main concepts. Let’s see them.

Concept 1: Entry

To create the “dependency graph” of your application, webpack needs an entry point. This entry point would be the file, or files, from which webpack will define what it must bundle. The entry key is an array, where each item is an entry file.

Let’s define in our configuration the entry file app.jsx (let’s use a JSX file so we can be ready for the creation of a React application), which will be inside the app folder, which we will create later:

module.exports = {
    entry: [
        './app/app.jsx'
    ],
}

Concept 2: Output

We defined the entry and now we must define the output. In the output key, we define where webpack should put the bundle that it will generate through the entry we defined previously. This definition is an object, with the two keys. One is path, which will be the folder where the bundle will be saved, and the other is filename, which, obviously, is the name of the file to be generated. Let’s set our bundle name to bundle.js and the path to the public folder, which will be relative to this configuration file that we are working on. For that, let’s import the path module through the require command, at the top of the file:

var path = require('path');

module.exports = {
    entry: [        
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    }
}

The output has a lot possible and more complex configurations, but these two are the most basic and simple.

Concept 3: Loaders

webpack treats each file of your application as a module, and it understands that all of your project assets should be his to handle, and not of the browser. With this concept, loaders are used to transform each file in modules, so that they can be imported and used through your project. Loaders configuration is used to:

  • Identify what files should be transformed by a certain
  • Process / Transform these files so that they can be added to the dependency graph

Loaders configuration is made through the rules key, which is inside the modules key. In this key, we define on the test property what should be transformed, and in the use property, what will be used to execute the transformation in these files. In our example, we will use babel-loader to transpile all .js and .jsx files in our project. Let’s use the Babel presets babel-preset-react, babel-preset-es2015 and babel-preset-stage-0. The babel-core package is also required. With this configuration, we will have access to new features of Javascript that are still not supported by all browsers, React specific syntax, and more. First, let’s install all of these packages with npm:

npm install --save-dev babel-loader babel-preset-react babel-preset-es2015 babel-preset-stage-0 babel-core

Now, let’s set everything up in our configuration file:

var path = require('path');

module.exports = {
    entry: [
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    },    
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/, 
                exclude: [/node_modules/],
                use: [{
                    loader: 'babel-loader',
                    options: { presets: ['react', 'es2015', 'stage-0'] }
                }],
            },            

        ]
    }
};

Note that we used the exclude property to indicate that the loader should not transform anything in the “node_modules” folder. This folder contains the modules installed through npm, and there is no need to transpile them.

Let’s understand what we just did. webpack uses the regex defined in the “test” property to define which files are going to be transformed, and then use “babel-loader”, with the options defined in the “options” property. In this specific case, the option we will define is the Babel presets (more about them here) that will be used to transform the files

In the same category, there is the style-loader, where we can set .scss files to be transformed, so that you can use Sass in your project. For that, we need the style-loader, css-loader and sass-loader packages. style-loader adds CSS on the DOM, css-loader interprets import and url() on your project and sass-loader compiles the CSS to be interpreted by the browser. sass-loader also requires the node-sass package, so let’s install it too:

npm install --save-dev style-loader css-loader sass-loader node-sass

Now, let’s add the second rule to our “webpack.config.js” file:

module.exports = {
    entry: [
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    },    
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/, 
                exclude: [/node_modules/],
                use: [{
                    loader: 'babel-loader',
                    options: { presets: ['react', 'es2015', 'stage-0'] }
                }],
            },
            {
                test: /\.scss$/,
                exclude: [/node_modules/],
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "sass-loader"
                }]
            }
        ]
    }
};

In this case, as you can see, we used the loader three times. It is important that they are applied in this order. Now, you can require a “.scss” file and use Sass in your project. We will show this later, in our React example application.

Concept 4: Plugins

While loaders work in a per-file basis, plugins are normally used to transform code chunks that are already bundled. One example, from the webpack docs itself, is the webpack.optimize.UglifyJsPlugin() plugin. This plugin, minify / uglify your bundled code. Plugins are defined in the plugins key. To access webpack plugins you must import webpack through the require command. Let’s see all of this in our webpack.config.js file:

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

module.exports = {
    entry: [
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    },    
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/, 
                exclude: [/node_modules/],
                use: [{
                    loader: 'babel-loader',
                    options: { presets: ['react', 'es2015', 'stage-0'] }
                }],
            },
            {
                test: /\.scss$/,
                exclude: [/node_modules/],
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "sass-loader"
                }]
            }
        ]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin()
    ]
};

To a more complete list of webpack plugins, check here.

Now that we have everything configured, let’s set our React application up.

webpack with React

First, let’s use npm to install React and React-DOM, which are the basic packages to work with React. In this case, we will use the --save option, to update our package.json with production dependencies.

npm install --save react react-dom

Let’s also install http-server repository link) to serve our files and test our project locally. In this case, we can use the -g option to install it globally, since you can use it to test any other project.

npm install -g http-server

Now, let’s create the structure of our React project. I will use the structure that I normally use on my projects. Let’s create a folder called app. This folder will contain our entry file app.jsx, as we specified in the webpack configuration file. In this file, there will be some basic code to import the MainApp component, which we will create on the next step, to render in your application. We will also include a require to our stylesheet, app.scss, which we will also create later. This stylesheet will import other files, so that we do not need to go back to this file to add more .scss files, one by one.

import React from 'react';
import ReactDOM from 'react-dom';

import MainApp from './components/MainApp.jsx';

// App Styles
require('./styles/app.scss')

ReactDOM.render(
  <MainApp />,
  document.getElementById('app')
);

In the app folder, we will also create the components folder. In this folder, we will save our components files. The one we will use in this example will be called MainApp.jsx, with the basic code for a React component:

import React, { Component } from 'react';

class MainApp extends Component {
    render() {
        return (
            <div>
                MainApp component
            </div>
        );
    }
}

export default MainApp;

Now, still inside the app folder, we will create the styles folder. This folder will hold the .scss files, responsible for the style of your application. There, we can create a app.scss file. This will be our main stylesheet, and is the one we required in our app.jsx file. Let’s define a background-color to the body and a color to the p element, to check if the styles are loaded correctly. I included the p selector nested inside the body, to show that we are using Sass, since this nesting is not allowed in traditional pure CSS. I’ll also import a specific stylesheet for the MainApp component, which we will create soon:

@import "components/MainApp";

body {
  background-color: red;
  p {
    color: white;
  }
}

Inside the styles folder, we will create the components folder. In this folder, we can create a specific .css file for each component. This structure allows for a good separation and organization of your code. In this example, let’s create the _MainApp.scss file. Note the underscore in the beginning of the file name, required for the correct import as defined on the app.scss file that we just created. I’ll define just a style for the paragraph, so that we can confirm that the import is correct:

p {
  text-decoration: underline;
}

To finish, we need to create the public folder. This folder will contain the files that will be server to http-server, and is the folder where webpack will save the bundle with your code. Let’s create the index.html file, a simple HTML importing the bundle.js file, which will be in the same folder:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8"/>
</head>

<body>
    <div id="app"></div>

    <script src="bundle.js"></script>
</body>

</html>

Now, let’s run webpack. All you need to do is run webpack on your project’s root folder. webpack will take a few seconds and then will print some notices. If everything went right, you can check in the public folder that the bundle.js file was created. You need to run this command again for every change you make on your code for this project. To avoid that, you can run the command webpack -w. This command will watch (w, for watch) for changes, and will update the bundle.js file when any change is made to the project code. Finally, let’s run http-server and check if everything is alright. http-server default port is 8080, so, access http://localhost:8080/; if everything went correctly, you should see a page like this:

Print of the React application working correctly
Print of the React application working correctly

The color scheme is ugly as hell, but as you can see, the MainApp component was correctly rendered. The styles were applied, both the ones defined in the app.scss file (paragraph and body colors) and the one defined in the _MainApp.scss file (underline paragraph).

This means that everything went right. Now you have a React application, with your code bundled by webpack and using modern Javascript features and Sass for your styles. To summarize, our structure of files and folders stayed like this:

├── app
│   ├── app.jsx
│   ├── components
│   │   └── MainApp.jsx
│   └── styles
│       ├── app.scss
│       └── components
│           └── _MainApp.scss
├── package.json
├── public
│   ├── bundle.js
│   └── index.html
├── understanding-webpack-tree.txt
└── webpack.config.js

Note that there are many structures and organization styles that work. This is just one kind of organization that works for me, so, don’t assume this as some kind of “correct answer”.

I hope this was helpful, and I’m always open to corrections and suggestions.

The code in this post can be found on the following Github repository: https://github.com/felipegalvao/understanding-webpack

See ya