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 require
d 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:
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