DEV Community

Cover image for ⚛️How to create a React Web Application without create-react-app CLI
Bhavuk kalra
Bhavuk kalra

Posted on

⚛️How to create a React Web Application without create-react-app CLI

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

  1. 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

  2. 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.

  3. 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

  1. Babel - A transpilerthat converts JSX -> JS, JS(ES6) -> JS(ES5)

  2. 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>
    )   
}
Enter fullscreen mode Exit fullscreen mode

Output -

Output of above code on Web page console

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
Enter fullscreen mode Exit fullscreen mode

Initiates the NPM project with default configs

npm i react
Enter fullscreen mode Exit fullscreen mode

Install the React library (Ofcourse)

npm i react-dom
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

A utility complementing webpack. Provides a single line to setup a Live reload dev server using webpack.

npm i html-webpack-plugin --save-dev
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The main core babel transpiler

npm i babel-loader --save-dev
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"],
          },
        },
      },
    ],
  },
};

Enter fullscreen mode Exit fullscreen mode
  • 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 the node_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
Enter fullscreen mode Exit fullscreen mode

✍️ Create the files required

  • Create index.js(root for webpack to create a dependency graph) in the src 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"));
Enter fullscreen mode Exit fullscreen mode
  • Create a folder components inside src and a file App.js to store the component that will be rendered in root 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;
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode

👓 Current directory structure

└── root/
    ├── node_modules/
    ├── package.json
    ├── package-lock.json
    ├── webpack.config.js
    ├── src/
    │   ├── components/
    │   |    ├── App.js
    |   ├── index.html
    |   ├── index.js
Enter fullscreen mode Exit fullscreen mode

➕ Add scripts to package.json

"start": "webpack-dev-server --mode development --open --hot"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

♾ Run the scripts

To start a development live reload server

npm run start
Enter fullscreen mode Exit fullscreen mode

Logs

sadasdasd

Output

Logs of npm run start

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

About me

Socials 🆔

Top comments (0)