Creating A React App Without create-react-app

featured-image
Image By Antonio Batinić

In this article, we will create a React app from scratch without create-react-app library in order to understand how it works in essence. We will use Webpack as the module-bundler for this project. I will explain everything at the time I implement it in the project.

Before we start, clone the github repo that I created, which contains the src folder with minimum starter files. Once done, open the folder in your code editor, and make sure you're in starter-files branch.

Link to this heading1. Initialize NPM

Run npm init -y in the root of the project folder. It should create a package.json file that looks like this-

C:\Users\sapin\Desktop\react-webpack-starter>npm init -y
Wrote to C:\Users\sapin\Desktop\react-webpack-starter\package.json:{
“name”: “react-webpack-starter”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1"
},
“keywords”: [],
“author”: “”,
“license”: “ISC”
}

Link to this headingThe Need For Webpack

The very first thing we need is not the react library, but a module-bundler that will scan through our project files and bundle them together. But before bundling the code together, Webpack uses the tools we provide to understand the stuff present inside the files. Don't worry! We'll discuss it later in details.

Webpack will start its job by picking up src/index.js file, because this is what we specify as the entry-point when configuring it up. So let's jump right into it-

We need the following libraries from Webpack for anything to work with it-

  • webpack: It's the core library for Webpack bundler.

  • webpack-cli: If you take a look inside package.json when using create-react-app, you'll find the following scripts-

"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
}

react-scripts is a custom library that uses webpack's command line interface to run any command, such as- npm start. But in this setup, we're going to run commands from raw webpack-cli.

"scripts": {
"start": "webpack-dev-server --mode development",
"build": "webpack --mode production",
}
  • webpack-dev-server: You already saw the use of this library in the start script above. It is used in development mode to serve the bundled files in a browser; e.g. localhost:3000.

Link to this heading2. Setting Up Webpack

First, run the following command in the console. The --save-dev or -D flag states that we want to install them as dev-dependencies.

npm install --save-dev webpack webpack-cli webpack-dev-server

Now add the above-mentioned scripts inside package.json file.

"scripts": {
"start": "webpack-dev-server --mode development --open --hot",
"build": "webpack --mode production",
}

The two flags --open and --hot serve the following roles respectively:

  • automatically open the app in browser after the server starts
  • automatically reload the app in browser whenever there's a change in the server code

So far, we have the following stuff in our project folder-

starter files

Now let's configure webpack by creating a file webpack.config.js at the root of the folder. Add the following stuff into it-

module.exports = {
entry: {
index: './src/index.js',
},
output: {
path: __dirname + '/build',
filename: 'index.bundle.js',
},
};

Everything inside module.exports is exported as configuration for webpack.

  1. entry: Here we specify the file(s) that webpack should look for to start the bundling process.

  2. output: The directory where the bundled file(s) will be placed. In our case, we have just a single entry-point named index, and so the output should generate a single bundled file only. Let's see its properties-

    • path: Path to the output directory. __dirname, which is provided by node, holds the path of current working directory; i.e. the root of our project folder. Thus, __dirname + '/build' evaluates to /build folder in the root.
    • filename: Name of the bundled file. (index.bundle.js)

Now install html-webpack-plugin, which will pick up src/index.html, and create an html file inside /build folder, placing the same content as in src/index.html.

npm install --save-dev html-webpack-plugin

Update webpack.config.js with the following content-

const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: __dirname + '/build',
filename: 'index.bundle.js'
},
plugins: [
new HTMLWebpackPlugin({
template: './src/index.html'
})
]
}

Link to this headingAll it does is-

  1. require the html-webpack-plugin
  2. add, to plugins array, a new plugin named HTMLWebpackPlugin, and pass to it the source file that will be used as a template to create new html file in the /build directory.

And now we're ready to tackle Babel and React/JSX.

Link to this headingIntroducing Loaders

The fact of the matter is that excluding html & js file-types, Webpack actually knows nothing about what files there are in /src folder, and what those files do.

And here comes the role of a loader. A loader acts as a pre-processor for specific file-types. When Webpack encounters any new file-type, it looks for a loader in its configuration, and handles the file to that loader. The loader configured for that file-type would perform its magic, and return the transpiled version of that code to Webpack which then continues the bundling process.

Link to this headingThe Need for Babel

Webpack doesn't understand the modern JavaScript code that we're used to write. It only understands code prior to ES2015. But we can use babel for this job. Babel is a JavaScript compiler that transforms modern JavaScript code into backward compatible version of it. It can also transform JSX and React. And that's what we actually need here! 😄

Here are the dependencies/libraries that we need to implement babel-

  • @babel/core: It's the core library for Babel.
  • @babel/preset-env: This preset is a collection of babel plugins to transform modern JavaScript code, depending on the environment/target-browser we specify in the configuration.
  • @babel/preset-react: This preset is the one that transforms JSX syntax into actual JavaScript code we need.
  • babel-loader: This one should've been mentioned before rest of the dependencies above. If we want to use any of the three things above, we must have babel-loader so that it can pre-process the specific file-types (in this case, js/jsx) before Webpack can work further.

Link to this heading3. Setting Up Babel

Run the following command with --save-dev flag as earlier-

npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react

Add module property to module.exports object in webpack.config.js-

module: {
rules: [{
test: /\.js|\.jsx$/,
exclude: /node_modules/,
use: 'babel-loader'
}]
},

Each object in rules array specifies rules for different types of files. Let's inspect the rule properties-

  • test: It takes a regular expression for the file-type(s) to look for.
  • exclude: As you may guess, it restricts the loader for corresponding file-types to not enter specified directory/directories.
  • use: This property tells Webpack to use babel-loader for file-types mentioned above.

Link to this headingAll You Should Understand So Far-

  • Webpack looks for the entry-point (/src/index.js) when we hit npm run start (npm start for short).
  • When it encounters a file with js/jsx extension, it looks for any corresponding loader in its configuration. If it finds one, it handles the file to that loader; else throws an error.

After Webpack will hand index.js over to babel-loader, the loader will look for its own configuration. So let's have one!

Create a new .babelrc file in the root directory, and add the presets that we're using for babel-

{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

Now that our Webpack settings are able to interpret and transform JSX code, let's actually install react and react-dom libraries!

npm install react react-dom

Notice that I didn't use --save-dev because these dependencies are used in both development and production modes.

Now you can import react and react-dom in your project files, and start using JSX! And if you run npm start, it should start webpack-dev-server, and automatically open the app in your browser after Webpack completes the bundling process.

Link to this headingBINGO!!! But Wait…

Despite all of the stuff we've done, we are still limited to not using any file-types other than js/jsx in our project because we have to specify loader(s) for each file to tell Webpack what to do with it.

Some commonly used loaders are: css-loader, sass-loader, url-loader, file-loader, etc. You should know how to use some of the loaders listed here.

In the complete branch of my repository, I have added support for a lot more things like sass, eslint, image-files, postcss-loader with autoprefixer, file-loader, url-loader, and many more.

By now, you should have a pretty good understanding of how React and Webpack work together.

And, if you like my writing style, you can subscribe to my mailing list in order to never miss any of my future posts!


Sapinder Singh © 2024. All Rights Reserved.