In this post we'll build a React web application using webpack, Babel, React, and React DOM without relying on create-react-app
❓What is create-react-app
create-react-app is a CLI provided by facebook to make it easier for users to get started building with React.
It is ideal for beginners to run a single command to get your environment ready to get started, but it has two major flaws
It restricts customization for build configuration. For example you might want to use SWC (Speedy Web Compiler) for your deployment pipeline instead of pre-installed Babel for compilation of JS/TS codebase.
It abstracts a lot of the inbuilt tools that makes React functional like processes of Transpilation, Minification, Bundling which one should know about while making Web applications
ℹ️ Some Terminologies
Transpiler - Converts one form of JS code to another form of JS code. Convert ES6(ECMA Script 2015) into code that browsers can understand. Example Babel
Module Bundler - Takes different files HTML,JS, Style Sheets(CSS). And bundles them into a single file. Helps managing dependencies relations (Including loading modules in correct order), To improve performance, To organize your code, To reduce the number of HTTP requests Example Webpack, Parcel, and Rollup.
Minification - The process of removing unnecessary characters from programming languages or markup languages without changing its functionality. It is also known as minimization. Minification can include: Removing whitespace, Removing comments, Removing semicolons, Using shorter variable and function names, and Removing unused code.
🛠️ Tools used
Babel - A transpilerthat converts JSX -> JS, JS(ES6) -> JS(ES5)
Webpack - A module bundler. Converts all assets to single file which is code optimized, minified.
This combination allows developers to write code using the latest language features and ensure it's efficiently packaged for deployment.
**
❓Why use so many tools required to run react locally
Before we start with the walkthrough there is one important question we need to answer
why do we need these specific tools such as vite or node(npm) to run React locally. Why can't we just use normal HTML, CSS and JS?
As we use JSX(Javascript XML) syntax very frequently which can't be understood by the browser. We need the presence of special build tools (Here Babel for Transpiling JSX into JS)
/*
Function returning an HTML tag giving an error.
For using this type of syntax inside react we need a
special build tool Babel (a Transpiler)
*/
function test(){
return(
<div>
<h1>Hello world!</h1>
</div>
)
}
Output -
For converting JS files using the syntax of JSX to the files that can be understood by the browser,
We need special setup like Vite etc (Includes Build tools which Transforms and optimizes code eg remove unnecessary whitespaces, Handles imports etc)
Note🗒️
You can skip the setup of having build tools by not using JSX syntax. You can use React without JSX if you don't want to set up compilation in your build environment. However Writing React without JSX can be more verbose than writing React with JSX.
We have built our base fundamentals and answered some cruicial questions. Now we are ready to start coding ⌨️
Lets start coding 🖮
Open your terminal and run the following commands in order as they appear.
npm init -y
Initiates the NPM project with default configs
npm i react
Install the React library (Ofcourse)
npm i react-dom
This package acts as a glue between React and the webpages DOM. DOM is just a programming Interface for webpages. To know more about on what is it and how its useful in web-dev click here MDN DOM
🧊 Install webpack and its dependencies
npm i webpack --save-dev
Install our module bundler webpack. Only needed as a dev dependency and not while our app is running on production.
npm i webpack-cli --save-dev
Provides a faster way to initialize and configure a webpack project through the cli. (Also watches for file changes).
npm i webpack-dev-server --save-dev
A utility complementing webpack. Provides a single line to setup a Live reload dev server using webpack.
npm i html-webpack-plugin --save-dev
Simplifies HTML file creation to serve our webpack bundles. Assists with adding our bundled files to index.html
file.
🅱️ Install Babel and its utilities
npm i @babel/core --save-dev
The main core babel transpiler
npm i babel-loader --save-dev
- Loader - Is what webpack uses to handle and process different file types. It dictates how certain file types are processed as they are imported into the project.
In this case transpile the js files from ES6 to ES5 before minifying them
As there are different files hence there are difference loaders
For example
babel-loader - For JS files. This package allows transpiling JavaScript files using Babel and webpack.
sass-loader - Transpiles SAAS files to css
style-loader - style-loader takes CSS you've imported in your JavaScript files, and injects them as Style tags into the DOM. It's particularly useful for inlining Critical CSS into the Head tag of your page.
npm i @babel/preset-react --save-dev
Preset is a set of plugins that support language features. By default Babel has the plugin ES2015 which add support for ES5 javascript
preset-react - Adds support for JSX(Javascript XML)
npm i @babel/preset-env --save-dev
preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!
Contents of package.json
After installing all modules.
{
"name": "create-react-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"babel-loader": "^9.1.3",
"html-webpack-plugin": "^5.6.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
}
}
Your installed versions might be different(higher) if you are following this at a later time.
After the above steps your directory structure should look like this.
└── root/
├── node_modules/
├── package.json
├── package-lock.json
Configure Webpack ⚒️
Create webpack.config.js
in the root directory and add the following content
const path = require("path");
// For injecting the generated minified bundle.js to the index.html
const HTMLWebPackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "/dist"),
filename: "bundle.js",
},
plugins: [
new HTMLWebPackPlugin({
template: "./src/index.html",
}),
],
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
},
],
},
};
entry- Essentially tell webpack which file to use as as the root to create a dependency graph. used to resolve modules which are dependent on one another and also building the modules in order (For example build those modules first which are required as a dependency by other modules)
output - As webpack is a module bundler. It takes in a group files and outputs a smaller minified group of files. We need to specify the final output javascript file name and its path that will be generated by webpack
module - Informs webpack what kind of loader we'll be using. It holds the information on how the different kind of modules will be treated.
rules - Informs webpack that we'll be using 'BABEL' to transpile our js files.
test - Which files would be covered under this rule. It's stated by the regex
/.js$/
i.e all files with extension js.exclude - Excludes
node_modules
from transpilation process. If we don't exclude those, Otherwise it Makes the process slower and also we don't need to transpile those as they are mostly already minified. Only under very specific conditions we want to transpile some of thenode_modules
but not in this case.use - Specifies the babel-loader' that would be used for transpiling.
options - Specifies the presets which would extend the loaders ability to intake JSX and ES6 syntax
👓 Current directory structure
└── root/
├── node_modules/
├── package.json
├── package-lock.json
├── webpack.config.js
✍️ Create the files required
- Create
index.js
(root for webpack to create a dependency graph) in thesrc
folder and add the contents.
import React from "react";
import ReactDOM from "react-dom";
// Get the component from the file to be rendered (Will be created later)
import App from './components/App'
// Get root id from index.html (will be created later)
ReactDOM.render(<App />, document.getElementById("root"));
- Create a folder
components
insidesrc
and a fileApp.js
to store the component that will be rendered inroot
div
import React, { Component } from "react";
/*
Extending the Component class gives us access to its life-cycle method one of which is render
*/
class App extends Component {
render() {
return(
<div>
<h1>Hello world!</h1>
</div>
)
}
}
export default App;
- Inside the
src
folder create 'index.html'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- Where are react component will be renderer -->
<div id="root"></div>
</body>
</html>
👓 Current directory structure
└── root/
├── node_modules/
├── package.json
├── package-lock.json
├── webpack.config.js
├── src/
│ ├── components/
│ | ├── App.js
| ├── index.html
| ├── index.js
➕ Add scripts to package.json
"start": "webpack-dev-server --mode development --open --hot"
we are using webpack-dev-server
for live reload. It provides a fast in-memory access to the webpack assets to accomplish this.
Flags
--mode development
- Enable production optimizations or development hints.--hot
- Enables Hot Module Replacement(HMR).It exchanges, adds, or removes modules while an application is running, without a full reload--open
- Open the application in a new tab
"build": "webpack --mode production"
It will create a production build for us to deploy. Bundle and minify all our js in bundle.js
located on a folder dist
.
The HTMLWebpackPlugin will inject our output bundle.js
in index.html
Final content for package.json
{
"name": "create-react-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --mode development --open --hot",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"babel-loader": "^9.1.3",
"html-webpack-plugin": "^5.6.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
}
}
♾ Run the scripts
To start a development live reload server
npm run start
Logs
Output
Try it on your own
Make an edit to index.html
and change its contents from "Hello World!" to "Hello Dev.to!". Save the file and see if the page gets updated as soon as you save your file. This is Hot Reload provided by the module webpack-dev-server
npm run build
It will create a dist
production build folder with minified bundle.js
generated by webpack and index.html
(Already injected with bundle.js
thanks to the module html-webpack-plugin
)
👓 Current directory structure
└── root/
├── dist/
| ├── index.html
| | ├── bundle.js
├── node_modules/
├── package.json
├── package-lock.json
├── webpack.config.js
├── src/
│ ├── components/
│ | ├── App.js
| ├── index.html
| ├── index.js
EXTRAS
- Why use require("path") which is a Node function inside frontend development?
If you try to run const fs = require("fs")
on browser it will throw an error. meaning its a Node enabled function.
Its a common practice in the development cycle. Whenever there is a need for local control(BUT ONLY DURING THE DEVELOPMENT PROCESS
) for handling local files, paths, creating servers locally to enable faster development. We use local Node runtime environment(Which also uses JS as coding language).
- Node version?
This tutorial was made on node version 20.12.2 LTS
Closing✌️
Thanks for reading it thus far. This is my first time publishing to dev.to. Got recommended this website by a fellow developer, Let's see how it goes.
Top comments (0)