React + webpack4 + babel 7 (Jest included)

Ever wondered how facebook’s create-react-app handles the bundling and bootstrapping behind the abstraction of yarn build? Let’s try to bootstrap our own react application using webpack 4 and babel. 😸

I’ll be using yarn through this article but please if you use npm, well, that’s fine too. 😁

What is webpack and babel?

Essentially, converting any es5+ code to its browser compatible version is a two step process; Transpiling and bundling.

Transpiling is the process of converting modern javascript code to a form that is understood by even older browsers. Babel takes care of this. Babel also has various presets which allow for transpiling different versions. For example, the preset-react helps convert JSX to browser compatible format.

Bundling is the process of combining all the code across your project and putting it in a single file. Webpack does this for you. typically, this file is called bundle.js

Let’s get into it

Having understood the purpose of webpack and babel, let’s put them to use in bootstrapping our first react app with them. We’ll go through code organisation, and the config for webpack. Cool? cool.

File structure

If you haven’t already created your project directory, now would be a good time. Run the following on your terminal to create it.

mkdir react-boilerplate
cd react-boilerplate
npm init
git init

Also, pick a .gitignore file you usually use on JS projects. I’m not going to show that here.

We’ll organise all our public code(index.html and bundle.js) in the ./public directory. I’ve seen some projects where there is a separate ./build or ./dist folder that contains the bundled js file but it’s just a matter of preference. Let’s also create a index.html in the public folder.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React Boilerplate!</title>
</head>
<body>
<div id="root">This is where you'r entire app will be rendered</div>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script src="./js/bundle.js"></script>
</body>
</html>

Your directory structure should look something like this now:

Code and config

Now, there are a few dependencies we’ll have to install to get started with webpack.

yarn add --dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli webpack-dev-serveryarn add react react-dom

At the root of the project, create an index.js file. This will serve as the entry point for webpack.

// ./index.jsimport React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<div>
Welcome to the app!
</div>, document.getElementById('root'));

Along with this, we also need config files for webpack and babel. Let’s create ./webpack/webpack.config.js

// webpack/webpack.config.jsconst path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './index.js', // The file to start bundling from
output: {
path: path.join(__dirname, '/public/js'), // output dir
publicPath: '/js/', // path to serve file on
filename: 'bundle.js' // file name
},
devServer: {
contentBase: './public/', // dev server file location
},
plugins: [
new webpack.HotModuleReplacementPlugin({}), // HMR plugin
],
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader' // Loader to use for js and jsx files
}
}]
},
};

and ./.babelrc:

// ./.babelrc{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

To see what we have in action, we can add a start and a build script to our package.json.

//package.json...
"scripts": {
"start": "webpack-dev-server --mode development --config webpack/webpack.config.js --open 'Google Chrome'",
"build": "webpack --mode production --config webpack/webpack.config.js"
},
...
...

Now, running yarn start should fire up this page in your browser:

First app build

Adding Styles

All right; so we have our basic app up and running. Cool as it may be, it’s not enough. We need to make the site more beautiful and add some CSS.

PS: You can use SASS or LESS as preprocessors too. there are babel loaders for them too. For the sake of this tutorial, I’ll stick to CSS

For this, we’ll need to add two new dependencies:

yarn add --dev css-loader style-loader

We also need to use the installed packages in the webpack config for css files. All the following to the module.rules object:

// webpack/webpack.config.js...
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
...
...

To test it out, let’s create a ./index.styles.css:

// ./index.styles.css.app {
background: aqua;
color: red;
}

and import a class from that into our index.js:

import React from "react";
import ReactDOM from "react-dom";
import styles from './index.styles.css';
ReactDOM.render(<div className={styles.app}>
Welcome to the app!
</div>, document.getElementById('root'));

The result should look like this:

App build after adding styles

Testing with jest

Any production grade app isn’t complete if it is not covered with testcases. Most people working with react use jest as the testing library. Owing to the same popularity, let’s configure the jest testing framework in the boilerplate.

We will require a babel loader for jest called, babel-jest and let’s use react-test-renderer as the library for snapshots.

yarn add --dev babel-jest jest react-test-renderer

This article will not cover testing with jest. That calls for a new blog post entirely. Also, this could be a good time to go ahead and add enzyme to your application. In the interest of keeping this tutorial simple and elementary, I’m going to skip it.

Just one more thing before we start writing tests and wrap this up; Jest, or rather the babel-jest loader, won’t be able to transpile css. Hence, for the purpose of testing, we need to map all the stylesheets to a mock module. With jest, this step is rather simple.

we need to use the moduleMapper property in the jest object in package.json or create a separate jest.config.js in the root of the project.

// package.json...
"jest": {
"moduleNameMapper": {
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
}
}
...
...

We also need to write the styleMock.js

// ./__mocks__/styleMock.jsmodule.exports = {}

Aaaaaaand, that’s it. We can start testing the application with jest. As part of the boilerplate, I am going to separate out the component from index.js and put it in a new src directory where all code should be.

// ./src/App.component.jsimport React from "react";
import './App.styles.css';
const App = () => {
return (
<div className={styles.app}>
Welcome to the app!
</div>
)
};
export default App;

We’ll use this app.js in the index file. Much cleaner now. The styles from index.styles.css can now move to App.styles.css.

And that’s it! You can now go ahead and build that crazy app you were going to in the first place! 😁

I’ve hosted this boilerplate on github. You can find the link to the same below 🙂

Cheers! ☕️

@szanwar22 on twitter :D