Setting up a React app from scratch withWebpack, Babel and Eslint
New to JavaScript? Take 5 minutes from your valuable time and read this!! I assure you it will not go to waste,
We all know the magic command create-react-app .
However, if you are doing anything requiring SASS, Webpack configurations, or other custom configurations, CRA (create-react-app) will be a pain. Like, if you want to add custom build configs. One way to add custom configs is to eject the app.
If you are a naturally curious developer, you’d like to know how things work and which part does what, then let me help you with it.
Prerequisites
- Node
- Any IDE (recommended Webstorm or Visual Studio Code).
Let’s Start
Open up the command line or Git bash and create a new directory:
mkdir custom-react-boilerplate && cd custom-react-boilerplate
Initialize project by running:
npm init
It will prompt you for input for a few aspects of the project. fill that inputs as your own. Then you can see the package.json file.
Then we install react
npm install react react-dom
Setup Webpack
Webpack is a bundler. The core function of Webpack is that it takes a bunch of JavaScript files we write in our project and turns them into a single, minified file so that it will be quick to serve.
Let’s install the Webpack.
npm install --save-dev webpack webpack-dev-server webpack-cli html-loader html-webpack-plugin
- webpack — includes all core Webpack functionality.
- webpack-dev-server — this development server automatically re-runs Webpack when our file is changed.
- webpack-cli — enable running Webpack from the command line.
Next, we configure the package.json to run the Webpack.
"scripts": {
"start": "webpack serve --mode development --env=development",
},
- — mode means that configure the option to tells the webpack to run stage. For further information visit https://webpack.js.org/configuration/mode/
- — env means that we set the running environment. This parameter we use in below topic source map for better error logs.
The following string values are supported:
Then we create a index.html
file at the root of the project as,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom React Configuration</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
So two highlighted things, you have to create an <div id="root"></div>
element and an <script src="./dist/bundle.js"></script>
tag.
Add src directory then index.jsx and App.jsx file
mkdir src && cd src && touch index.jsx && touch App.jsx
Now let’s add some starter code,
In the App.jsx file,
import React from "react";
function App()
{
return (
<div className='App'>
<h1>Hello World !</h1>
</div>
);
}
export default App;
In the index.jsx file,
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
First, we create App.jsx
and import it to the index.jsx
. This approach isolates the complexity and understandable than the put all stuff together. So a couple of things have to clarify,
What is React.StrictMode ?
React’s StrictMode is sort of a helper component that will help you write better react components, you can wrap a set of components with the <StrictMode>
.
StrictMode
currently helps with:
- Identifying components with unsafe lifecycles
- Warning about legacy string ref API usage
- Warning about deprecated findDOMNode usage
- Detecting unexpected side effects
- Detecting legacy context API
for further information refer: https://reactjs.org/docs/strict-mode.html
Configuring Babel 7
Ok, what is Babel, and why we need it?
Babel is responsible to converting new language features to old.
While most popular browsers can support ECMAScript2015 ( or ES6), it’s always good practice to ensure that your code is compatible with older versions of JavaScript. Es6 have let
, const
, arrow functions
, classes
etc. To understand, below we have an input of an ES6 arrow function which is converted to a plain, old ES5 JavaScript function.
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);
// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
return n + 1;
});
Further reference: https://babeljs.io/docs/en/
Let’s install Babel into our project,
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
What we just installed:
- @babel/core: Main dependency that includes the babel transform script.
- @babel/preset-env: Transform ES6+ into valid ES5 code. Optionally configures browser polyfills automatically.
- @babel/preset-react: extends Babel support to JSX.
- babel-loader: Webpack loader that hooks Babel into webpack. We will run Babel from webpack with this package.
To configure Babel into our webpack, create a file in your root directory with the name webpack.config.js
file:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js'
},
resolve: {
alias: {
components: path.resolve(__dirname, 'src'),
},
extensions: ['.js', '.jsx'],
},
devServer: {
contentBase: "./build",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('./index.html'),
}),
]
};
Now we clarify what are the configurations that set in webpack.config.js
file.
entry
is our main JavaScript file. In React, this is the file where we use our renderer.
output
tells where to put our bundled code and what to name the file. Here, we configure Webpack to create a directory named /dist
in the root directory and create a file named bundle.js
.
output
tell the server to serve content from contentBase
config.
"module"
is where we tell Webpack to use all of those loaders we installed before.
Next, Babel presets, create a new .babelrc
file in the root of the project.
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
Now run npm run start
. You can see the Hello world !.
source map for better error logs
A source map provides the browser with a means of mapping back code within a compressed file back to its original position in a source file. This way, when an error occurs, you’re pointed to the exact file and line from which it emanates from. So if you’re using any tool that minifies, concatenates, or bundles up your code, you’ll definitely need to generate source maps.
So we modify the devtool
in the webpack.config.js
devtool : isProduction ?'source-map' : 'inline-source-map'
Now the webpack.config.js
looks like,
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env) => {
const isProduction = env === 'production';
return{
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js'
},
resolve: {
alias: {
components: path.resolve(__dirname, 'src'),
},
extensions: ['.js', '.jsx'],
},
devServer: {
contentBase: "./build",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('./index.html'),
}),
],
devtool : isProduction ?'source-map' : 'inline-source-map'
}
};
Setting up ESLint
A great project is made out of consistent code. The code should be indistinguishable from its writer: You should not be able to tell who authored which line of code in the project. Eslint helps developers to write better code by pointing out common errors and enforcing good programming patterns.
Linters highlights syntactical and stylistic problems in your source code, which often times helps you identify and correct subtle programming errors or unconventional coding practices that can lead to errors.
Let’s install ESLint into our project:
npm --save-dev install eslint eslint-loader babel-eslint eslint-config-react eslint-plugin-react
after that run eslint --init
in the terminal. Then please follow order,
We select Eslint to check syntax, problems and also enforce code style for code consistancy.
I personally prefer import/export statements. Choose your interested one.
Our application is a React-based application. So we need to select React.
We not going to use typescript here. So we select No.
Our React application is running on the browser. So we select Browser only.
I recommend using popular style guidelines.
I personally like Airbnb’s style guidelines. So I choose Airbnb and if you prefer something different. please take your own selection.
Here Eslint asks to create a configuration file as a JS, YAMLor JSON. I personally prefer JSON-type configuration files. SO I choose JSON.
Sometimes Eslint asks to install some plugins. So please select YES.
Now you can see theeslintc.json
file is created in the project root folder. This is the configuration file of the Eslint.
We have to modify the webpack.config.js
also.
use: ['babel-loader', 'eslint-loader'] // include eslint-loader
Now the file webpack.config.js
looks like,
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env) => {
const isProduction = env === 'production';
return{
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js'
},
resolve: {
alias: {
components: path.resolve(__dirname, 'src'),
},
extensions: ['.js', '.jsx'],
},
devServer: {
contentBase: "./build",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('./index.html'),
}),
],
devtool : isProduction ?'source-map' : 'inline-source-map'
}
};
for further reference: https://eslint.org/
To extend the eslint rules. I put some custom rules for better coding conventions. If your want to extend eslint rules, Goto theeslintc.json
file and put your custom rules inside the "rules":{}
.
If you interested in custom rules please refer: https://github.com/ShalithaCell/Eslint-Rules-ReactJs
So we again run the project npm run start
Oh.. a lot of errors are showing up in the console,
These errors are coming from the Eslint.
Errors! What do I do?
There are multiple ways to resolve Eslint errors and warnings.
- use — fix command
- setup IDE to check Eslint configurations.
1. Use — fix
Add the following line to package.json
the file.
"scripts": {
"lint": "eslint --fix --ext .js,.jsx ."
}
now script tag like,
"scripts": {
"start": "webpack serve --mode development",
"lint": "eslint --fix --ext .js,.jsx .",
"test": "echo \"Error: no test specified\" && exit 1"
},
Then run it with npm run lint
So we again run the project npm run start
and check errors are gone.
2. Setup IDE to check Eslint configurations.
Refer to the below link for configuring WebStrom and VS Code.
Setup LESS with React
Less is a backward-compatible language extension for CSS.
For further reference: https://lesscss.org/
Let's install the necessary packages,
npm install --save-dev less less-loader css-loader style-loader
Please check your webpack configuration module
property like below (we configure LESS properties earlier):
module : {
rules : [
{
test : /\.(js|jsx)$/,
exclude : /node_modules/,
use : [ 'babel-loader', 'eslint-loader' ],
},
{
test : /\.less$/,
use : [
'style-loader',
'css-loader',
'less-loader',
],
},
],
},
Finally your package package.json
file scripts look,
"scripts": {
"start": "webpack serve --mode development --env=development",
"lint": "eslint --fix --ext .js,.jsx .",
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production --env=production"
},
Conclusion
We just created our own React project boilerplate. fully functioning React app built without using create-react-app
. I hope this helps clarify at least some of what’s happening under the hood of the React apps you’ve already built.
Source code: https://github.com/ShalithaCell/React-Custom-BoilerPlate