How to set up Webpack and Babel for a simple ReactJS app?

Sélom
7 min readMay 6, 2018

When JavaScript was first released in 1995 by Brendan Eich, no-one thought this programming language could become as popular as it is today.

One of the reasons for such popularity is the fact that JavaScript can be used for both frontend and backend app development.
In fact, JavaScript is mostly used for frontend development to build Single-page applications.

Simply put, a single-page application is a web application that loads a single HTML file which is dynamically updated as the user interacts with the app.

There are quite a number of frameworks or libraries used in building a single-page app. The most popular are:
Angular.js
ReactJS
Vue.js
Ember.js
Meteor.js

In this blog post, we will use ReactJS to create a simple single-page application. I will explain later in this post, what Babel and Webpack are and why they are so important for our project. Also, to manage our packages, we have the choice between Yarn and NPM. Yet we will be using NPM for this. I will be writing another article to explain why I chose NPM over Yarn.

Let us get started, shall we?

First thing first…

First off, let us create an empty directory and name it todo-app . Yup, you guessed right, we are building a todo app. I couldn’t find any simpler idea 😄.

mkdir todo-app

CD into the todo-app` directory and initialize NPM by running the code below in your terminal:

npm init -y

npm init -y basically creates a package.json file which will be used to keep track of the modules that our app will need in order to work.

Now that we’ve created our package.json file, let us install our modules.

  • dependencies
npm i react react-dom --save
  • devDependencies
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react babel-core@7.0.0-bridge.0 webpack webpack-cli @babel/plugin-proposal-class-properties --save-dev

Note that we have two types of modules: dependencies and devDependencies . Simply put, devDependencies are used only during development while dependencies are required during runtime. As you can imagine, modules listed under devDependencies don’t get into production, while those listed under dependencies do.

Now that that’s out of the way, what the hell is npm i react react-dom --save ? Well, we are basically telling NPM to install the react and react-dom packages and to keep track of them in the package.json file. We did the same to install other packages under devDependencies . The only difference is that that we used --save for dependencies and --save-dev devDependencies. As you can imagine, --save just tells NPM to install the package under dependencies and --save-dev also tells NPM to install the package under devDependencies. Take a look at the package.json file after installing the modules. You should have something similar to:

{
"name": "todo-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Lom Se <shellom2005@gmail.com>",
"license": "ISC",
"dependencies": {
"react": "16.5.2",
"react-dom": "16.5.2"
},
"devDependencies": {
"@babel/core": "7.1.2",
"@babel/plugin-proposal-class-properties": "7.1.0",
"babel-loader": "^7.1.4",
"@babel/preset-env": "7.1.0",
"@babel/preset-react": "7.0.0",
"webpack": "4.20.2",
"webpack-cli": "3.1.2"
}
}

Note that the content of the package.json file might change during our journey since we might be adding more packages to our dependencies.

Here comes Babel…

Simply put, Babel is a transpiler which is best known for allowing codes written in ES6 to run in browsers which do not support ES6 at the moment. I’m sure you are asking why we are using Babel. Well, our code will be written in ES6 (which is currently not supported by most of the browsers out there) and we need Babel to compile it into a code that our browser can understand. Cool?

Or you are still asking why we are using ES6 instead of ES5 which most of the browsers understand? ES6 is the 6th edition of ECMAScript which has tons of new features to make a developer’s (you and I) life easier. Check out this nice article which talks about some of the new features available only in ES6. At the time this post is written, ES8 has already been released. Check this article for more information

Now let us configure Babel for our ReactJS app. Create a file named .babelrc and add the following code:

{
"presets": [
"@babel/preset-react",
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}

Basically, we are telling Babel to transpile our code using the @babel/preset-env and @babel/preset-react. We are also using the @babel/plugin-proposal-class-properties plugin to add support for experimental features like arrow functions in our code. That’s it!

In the context of Babel, a preset is just a set of plugins used to support a language feature. So the react preset adds support for JSX . env preset on the other hand, apart from compiling down to a minimum of ES5, can also take a browser or runtime version and determine which plugins are needed for that specific environment. For the sake of simplicity, we will keep our babel configuration as it is now.

Note that we will be adding more configuration to the .babelrc file whenever it is needed. The next config we will need is webpack.

Webpack! Hmmm

Webpack is a module bundler and a task runner. We will use it to bundle our React component into one file which will be used by the HTML file in our project.

At first the configurations in Webpack file look very intimidating, at least, that’s how I felt :D. But the good news is that it is quite easy to understand when you know why are using it.

Create a file and name it webpack.config.js and add the following code:

'use strict'const path = require('path')const config = {
// We will be adding our configurations here
}
module.exports = config

Note that the code in the webpack.config.js file is written in ES5. Wait a minute! Did we say that we were going to write our todo-app using ES6? hmm what is going on here?

Well, the reason why we are writing the webpack.config.js file in ES5 is because it is meant for NodeJS which does not support ES6 modules yet. In other words, we do not need to transpile the webpack.config.js file. So it is totally fine to write your webpack.config.js using ES5. Now let us add more configurations to our webpack.config.js file:

context: __dirname

context simply tells Webpack to always run from the root directory of your project no matter where the Webpack command is issued. Also __dirname is just a NodeJS global variable which refers to the root directory of your project.

entry: './src/App.jsx'

The entryproperty simply tells Webpack about the entry point of your app. Sort of the front door to your project. Since App.jsx file is located in the src folder, let us create a src folder in our root directory.

devtool: 'cheap-eval-source-map'

cheap-eval-source-map simply tells Webpack to add all your source-maps into your bundled code. This allows you to easily debug your application even after the code has been transpiled. Note that this option is going to make your bundled code bigger but the good news is that this is only used in development and not in production.

output: {
path: path.join(__dirname, 'public/js'),
filename: 'bundle.js'
}

The output object has two properties: path and filename . There are others properties for output . One of them is publicPath which will not be covered here. As you can imagine, path is just the path for our bundled file which is called bundle.js in this case. You can call the filename whatever you want.

By using path.join(__dirname, 'public/js) we are guaranteed that Webpack will always create our bundled file (bundle.js ) in the public/js folder no matter where the Webpack command is issued. Now from the root folder, let us create the public folder and inside the public folder, let us create the js folder.

resolve: {
extensions: ['.jsx', '.js', '.json']
}

The resolve.extensions property simply tells Webpack the order in which it should look for a particular file. Consider the code below:

import add from './add`

Webpack will first look for a file named add. If the file add is not found, it is not going to look for the file add.jsx. If add.jsx is not found, it will now look for the file add.js and if add.js is not found, Webpack will now look for the file add.json. In other words, this is just the order of resolution of those extensions.

module: {
rules: [
{ test: /\.jsx?$/, loader: 'babel-loader' }
]
}

This is an array of rules that Webpack is going to use to apply different loaders to your code. A loader is simply a tool that Webpack uses on your code in some fashion. By using babel-loader, we are asking Webpack to use Babel on any file that has the extension js or jsx . The question mark in /\.jsx?$/ simply means that we are looking for files with the extension js or jsx .

In case we need to apply more than one loader, we can specify an array ofloaders instead of justloader . In this case, we will have something like:

module: {
rules: [
{ test: /\.jsx?$/, loaders: ['babel-loader', 'another-loader', 'and-so-on']}
]
}

Note that the configuration for webpack might change since we might need Webpack to do more than just bundling our code.

Full code for webpack.config.js

You can find the full code here.

In my next post, we will install and configure Eslint, Prettier and setup a server with Express. Stay tuned!

Related blog posts:

Todo app with React.js — Part 1 (Eslint, Prettier and Express server)

Todo app with React.js — Part 2 (Styled Components)

Todo app with React.js — Part 3 (Snapshot testing with Jest and Enzyme)

Todo app with React.js — Part 4(Unit testing with Jest)

--

--

Sélom

Software Engineer | Fullstack Web Dev | Node.js ~ Laravel ~ React.js