React Ecosystem: Setting up Babel 7 and Webpack 5 for React

Photo by Christopher Gower on Unsplash

Have you ever wondered, How your React JSX code is running in a browser that only understands javascript? How your code is minified and bundled and shipped to the browser? Well If the answer to the above questions is YES, then you have come to the right place.

This is the first part of the article series where we will be setting up a production-grade React application from scratch. Check-out this article for more information.

Table of Content

  • Set up the folder structure.
  • Installing Webpack & babel dependencies.
  • Setting up babel configurations.
  • Adding index.html and index.js
  • Setting up webpack configurations.
  • Adding React to the project.

Before jumping into the code, if you are someone who learns more via going through the code, feel free to check out the repo for this project. I will be using the same repo to setup the whole React EcoSystem.

Step 1 - Set up the folder structure

Alright, let’s create a folder named react-from-scratchor anything you want and open your terminal and navigate inside react-from-scratch.

Then run the following command in terminal —

npm init -y

The above command will create apackage.json and -y flag skips some simple questions. After running this command your package.json will look like this —

React from scratch: package.json
React from scratch: package.json

While we are at it, let’s create some more files and folders to properly manage our codebase by running the following commands in your terminal -

mkdir webpack public src

This command will create 3 folders, I will explain why we need these but first, let’s put some files in these folders as well by running the following command

touch webpack.config.js babel.config.js public/index.html webpack/webpack.dev.config.js webpack/webpack.prod.config.js webpack/webpack.base.config.js src/index.js

Remember, You can always create these files manually as well, After creating the files your folder structure should look something like this —

React from scratch: folder structure
React from scratch: folder structure

Looks cool, right? Now before we start putting some configuration in these files let’s install some dependencies.

In following steps 2, we will be talking about Webpack & babel setup in depth, and will focus on why we are using a certain dependency. if you like to learn from code, you can skip this and refere the package.json mentioned in step 3 directly.

Step 2 — Installing Webpack and Babel

Before installing let’s understand what is Webpack and babel.

Webpack is an open-source JavaScript module bundler. It is made primarily for JavaScript, but it can transform front-end assets such as HTML, CSS, and images if the corresponding loaders are included.

Babel is a transcompiler which is mainly used to compiles ES6 code to backward compatible code that can run on older Javascript engines.

Enough with the definition :P, let's install the dependencies by adding the following to your devDependencies inside your package.json and run command npm install from your terminal.

"devDependencies": {
"webpack": "5.37.1",
"webpack-cli": "4.7.0",
"webpack-dev-server": "3.11.2",
"webpack-merge": "5.7.3"
"terser-webpack-plugin": "5.1.2",
"optimize-css-assets-webpack-plugin": "6.0.0",
"html-webpack-plugin": "5.3.1",
"compression-webpack-plugin": "8.0.0",
"mini-css-extract-plugin": "1.6.0",
"copy-webpack-plugin": "9.0.0",
"babel-loader": "8.2.2",
"css-loader": "5.2.6",
"style-loader": "2.0.0",
"@babel/core": "7.14.3",
"@babel/preset-env": "7.14.4",
"@babel/preset-react": "7.13.13",
"@babel/plugin-proposal-export-default-from": "7.12.13",
"@babel/plugin-proposal-throw-expressions": "7.12.13",
}

To avoid any braking issue due to version change in the future, I have locked the dependencies version here.

Okay I know what you are thinking, that’s a lot of dependencies right? Well hold your horses and roll up your sleeves because you are about to get bamboozled :P.

Because we are gonna talk about these one by one-

First, we have some basic webpack dependencies -

  • webpack: a module bundler for js application, which compiles our code based on some rules/config. we will talk about these rules/config later.
  • webpack-cli: enables the command line support for webpack.
  • webpack-dev-server: provides a server where the application can run locally. This helps in developing the application, because of this we don’t have to create a Nodejs server just to run my code locally.

After that, we have some plugins to support webpack -

  • webpack-merge: a plugin that helps in merging two webpack configurations.
  • terser-webpack-plugin: used for minifying the js code.
  • optimize-css-assets-webpack-plugin: used for minifying the CSS code.
  • html-webpack-plugin: generates an HTML file, also it adds .css and .js files to that HTML dynamically.
  • compression-webpack-plugin: the plugin which helps in compressing your code.
  • mini-css-extract-plugin: This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps, Thus enhancing the performance.
  • copy-webpack-plugin: Copies individual files or entire directories, which already exist, to the build directory.

Up next, we have some loaders to help Webpack in compiling the code-

  • babel-loader: This package allows transpiling JavaScript files using Babel and webpack.
  • css-loader: This would help webpack to collect CSS from all the CSS files referenced in your application and put them into a string.
  • style-loader: it takes the output string generated by the above css-loader and puts it inside the <style> tags in the index.html file.

And last but not the least, we have some babel dependencies —

Before we jump into setting up configurations, I wanna talk about the revolutionary breaking changes that babel introduced with v7-

Before v7 , babel had stage presets like stage-0, es2015 etc. so even if you were using only one feature from a preset, you still had to add the complete preset, which was making your codebase heavy and your application slow.

With v7, Babel introduced some breaking changes and has decided to move away from the above approach and started shipping each feature separately. Interesting right?

Step 3— Setting up babel config

Well let's add some configuration to those files which we created, but before that, a quick check, at this point your package.json should look like below-

If you have skipped the step 2, you can simply copy this package.json and run npm install from your terminal.

{
"name": "react-from-scratch",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/plugin-proposal-export-default-from": "^7.12.13",
"@babel/plugin-proposal-throw-expressions": "^7.12.13",
"@babel/preset-env": "^7.14.4",
"@babel/preset-react": "^7.13.13",
"babel-loader": "8.2.2",
"compression-webpack-plugin": "8.0.0",
"copy-webpack-plugin": "9.0.0",
"css-loader": "5.2.6",
"html-webpack-plugin": "5.3.1",
"mini-css-extract-plugin": "1.6.0",
"optimize-css-assets-webpack-plugin": "6.0.0",
"style-loader": "2.0.0",
"terser-webpack-plugin": "5.1.2",
"webpack": "5.37.1",
"webpack-cli": "4.7.0",
"webpack-dev-server": "3.11.2",
"webpack-merge": "5.7.3"
}
}

Alright , let’s add some babel configuration by adding the following code inside babel.config.js -

module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react'
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-proposal-throw-expressions',
'@babel/plugin-proposal-export-default-from',
],
};

As mentioned in step 2, here we have added presets and plugins that help in compiling our codebase which the browser can understand.

Step 4 — Adding index.html and index.js

let's add some HTML code to see something on our browser. Add the following in ourindex.html inside public folder.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React App From Scratch</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

Please add the following dummy code in our index.js inside src folder

console.log('Hello World, Setting up react from Scratch');

Step 5 — Setting up webpack configuration

Well to configure Webpack properly we will be using the following files —

  • webpack.config.js to pick configuration based on environment.
  • webpack.base.config.js to write a common configuration that will be used in both prod and dev mode.
  • webpack.dev.config.js to write development config.
  • webpack.prod.config.js for production config.

Alright, let’s add the config one by one, with the explanation of course :)

Add the following code to webpack.config.js at root level-

module.exports =
process.env.NODE_ENV === 'development'
? require('./webpack/webpack.dev.config')
: require('./webpack/webpack.prod.config');

here we are picking up Webpack configuration based on the environment.

Add the following code for common config to file webpack.base.config.js inside webpack folder.

const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = () => {
return merge([
{
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
process.env.NODE_ENV === 'production'
? MiniCssExtractPlugin.loader
: 'style-loader',
'css-loader'
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: './index.html',
}),
],
},
]);
};

Here in this config, we have two things, module and plugins We use module to write rules for a different set of files. Also, we are using babel-loader to transpile every js file and css-loader and style-loader to transpile and injecting the CSS.

Next, inside plugins, we have HtmlWebpackPlugin for creating the HTML and dynamically injecting CSS and js, and MiniCssExtractPlugin for on-demand CSS loading, you can read more about MiniCssExtractPlugin here.

Notice here we are using MiniCssExtractPlugin.loader for production only and style-loader for development mode, also we are using webpack.merge to merge out configurations.

Up next let’s setup the development config by adding the following code towebpack.dev.config.js file inside webpack folder.

const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackBaseConfig = require('./webpack.base.config');
const developmentConfig = () => {
return merge([
{
mode: 'development',
plugins: [
new webpack.DefinePlugin({
isDevelopment: true,
'process.env': {
NODE_ENV: JSON.stringify('development'),
},
}),
],
},
]);
};
module.exports = () => merge(webpackBaseConfig(), developmentConfig());

Here we are using webpack.DefinePlugin is used to set up some variables which can be referred to on UI. Also, in the last line, we are loading the base config and merging it with our development config using webpack.merge plugin.

Last but not the least lets add the following to webpack.prod.config.js

const webpack = require('webpack');
const { merge } = require('webpack-merge');
// plugins
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const webpackBaseConfig = require('./webpack.base.config');const prodConfig = () => {
return merge([
{
mode: 'production',
optimization: {
minimize: true,
runtimeChunk: 'single',
minimizer: [new TerserPlugin()],
},
plugins: [
new MiniCssExtractPlugin(),
new OptimizeCssAssetsPlugin(),
new webpack.DefinePlugin({
isDevelopment: false,
'process.env': {
NODE_ENV: JSON.stringify('production'),
},
}),
new CompressionPlugin(),
],
},
]);
};
module.exports = () => merge(webpackBaseConfig(), prodConfig());

Since v4, Webpack can handle the code optimization based on the mode but you can still set them manually. here we are using TerserPlugin for minification for our code inside optimization .

mode:’production’ sets the predefined optimization for production. You can also set it to development and none . check out this link for more info on this.

Up next, We have added the plugins MiniCssExtractPlugin and OptimizeCssAssetsPlugin to compress and load-on-demand CSS, CompressionPlugin for code compression.

Congratulations, We have finally set up the Webpack and babel, our 90% job is done. Now in order to write code in React, we will need one last setup.

Step 5 — Adding React to the project

To install React, run the following command in your terminal

npm i react react-dom

This will add two new dependencies inside your package.json.

React Eco-system: React from Scratch

Finally, we are done with the setup, let’s add some React code to our index.js inside src folder.

import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
render() {
return <h1>React Ecosystem: setting up React from Scratch</h1>;
}
}
ReactDOM.render(<App />, document.getElementById('root'));

Before we run this code, let’s just put this final piece to the puzzle but adding a script to run our code in development, Add the following start command inside scripts in your package.json

"start": "set NODE_ENV=development && webpack serve --open"

Perfect, now let’s run our code by running the following command in your terminal -

npm run start

this command will trigger the above-mentioned script and will open a new tab on your browser, which will look something like this-

YEEESSSSS !!!!! We finally did it. Now you can write and test your code in the browser. We are still a long way from a production-grade react application but let’s celebrate this little milestone for now.

Up Next in this series, We will be setting up Code linting and code formatting, check out this article series for more info.

Photo by Jen Theodore on Unsplash

A Passionate front-end developer