Understanding webpack and creating a React application with it

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.

Versão em português deste post / portuguese version of this post

What is webpack?

webpack is, today, a tool that is 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 bundle 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” option, which updates the “package.json” file with the development dependencies.

npm install webpack@2 --save-dev

de
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 to already prepare 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 entrym 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>
                <p>MainApp component</p>
            </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

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

2 comments on “Understanding webpack and creating a React application with it

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *