React Ecosystem: Setting up Babel 7 and Webpack 5 for React
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
andindex.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-scratch
or 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 —
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 —
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 —
- @babel/core: The core functionality of babel is provided by this package.
- @babel/preset-env: enables you to write code in ES6.
- @babel/preset-react: preset for react. it helps babel in compiling JSX to browser understandable code.
- @babel/plugin-proposal-throw-expressions: lets you throw exceptions from expression context by using
throw
keyword. - @babel/plugin-proposal-export-default-from : used for default exports
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
insidewebpack
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 to
webpack.dev.config.js
file insidewebpack
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
.
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.