# Setup React app from scratch - Part-1(Get your project running)
Tags: javascript, react, webpack, webdev
Published: 2022-03-22 14:03:06

## Part1.01 : Isn't create-react-app good enough?
[Create-react-app](https://create-react-app.dev/) is a great tool, it gets the project running in minutes. But it binds our hands with a lot of abstractions, according to the official react docs it is meant to be a tool to get anyone started with a good dev environment. 
It comes out of the box with a lot of good defaults(many of which we may never need) but it's very difficult to extend or bend it according to our needs. So in this series of articles we'll learn how to build our react setup from scratch.

## Part1.02: What is a module bundler
The name is self explanatory, it bundles modules. Usually in our projects we have multiple js libraries, bundlers them in a single javascript file which can be executed in the browser.
there are a lot of popular bundlers available : [esbuild](https://esbuild.github.io/), [parcel](https://parceljs.org/),  [vitejs](https://vitejs.dev/), [webpack](https://webpack.js.org/), [rollup](https://rollupjs.org/guide/en/) are the most popular once.
create-react-app uses webpack under the hood. We'll use webpack-5 for our setup.

## Part1.03: Project directory.
at the end of the project we'll end up with a folder structure like this.

```
📦my-app
 ┣ 📂public
 ┃ ┗ 📜index.html
 ┣ 📂src
 ┃ ┣ 📜App.jsx
 ┃ ┣ 📜app.module.css
 ┃ ┣ 📜index.js
 ┃ ┗ 📜logo.png
 ┣ 📜.babelrc.json
 ┣ 📜.browserslistrc
 ┣ 📜package-lock.json
 ┣ 📜package.json
 ┗ 📜webpack.config.js
```


## Part1.04: Initialize Project
first thing first create a repository.
```bash
mkdir my-app
cd my-app
```
initialize the project 
```bash
npm init --y   
```
at this time you should have a package.json file available in the directory

## part1.05: Run javascript ! Run!
run
```bash
npm i -D webpack webpack-cli
```
if you check the `node_modules/.bin` directory you will find a file named `webpack`. that is a binary file, that will run our webpack commands. 
now if you run 
```bash
./node_modules/.bin/webpack
```
you should get a promp saying 
```bash 
Can't resolve './src' ...
```
create src folder , by default webpack looks for a index.js file. 
crate a index.js file inside src folder

**src/index.js**
```js
console.log('hello world');
```

now if you run 
```
./node_modules/.bin/webpack
``` 
a dist folder will be created. however you'll be prompted with this error
```
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
```

to get rid of the error modify the command to 
```bash
./node_modules/.bin/webpack --mode development
```
now no errors will be prompted and and one `dist` folder will be created.

in the project we need to run webpack multiple times, writing the command each time can be tiresome , create a script in package.json to overcome this.

> PS: Package.json by default points to ./node_modules/.bin so we'll 
> just write webpack instead of ./node_modules/.bin/webpack

**package.json**
  ```json
  "scripts": {
	 ...,
    "start": "webpack --mode development",
    "build": "webpack --mode production",
    ...,
  }
  ```
  run 
  ```bash
  npm start
```
the output should be same as 
```bash
./node_modules/.bin/webpack --mode development
```

now we have a dist directory. 
inside of dist we have `main.js` file, to execute the file in browser we need to add it as a script in a html file.
Instead of doing it manually, we'll take the help of [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin).

```bash
npm i -D html-webpack-plugin
```
create `webpack.config.js` in root directory

**webpack.config.js**
```js 
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env, args) => {
  return {
    plugins: [new HtmlWebpackPlugin()],
  };
};
```
```bash
npm start
```
now dist will contain a `index.html` file as well, which points to the `main.js` file. 
if you open the index.html with browser and check console you'll see `hello world` in the console

**P.S:** env and args in the snippet can be used to receive environment variables and detect mode, we'll discuss them in details in some other chapter.

## Part 1.06: Setup the dev server
current setup doesn't provide us a developer friendly environment, we need to check the index.html file every time we make a change, 
to resolve this we'll use [webpack-dev-server](https://github.com/webpack/webpack-dev-server).
```bash
npm i -D webpack-dev-serve
```

**package.json**

>   "scripts": {
>   ...
> ~~"start": "webpack --mode development",~~
>     "start":  "webpack serve --mode development"
>     ...
>     },

```bash
npm start
```
open browser , go to `http://localhost:8081` and open console, you'll see  "hello world" .
To mock the behaviour of create-react-app and to run it on port 3000, make this changes
**webpack.config.js**
```js
module.exports = (env, args) => {
  return {
    devServer: {
      port: 3000,
      open: true,
    },
    plugins: [new HtmlWebpackPlugin()],
  };
};
```
```bash
npm start
```
the project will open browser and start on `http://localhost:3000`.

## Part 1.07: Set up HTML
create a public folder in root directory, create index.html

**public/index.html**

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
```
instead of using default `index.html` in the dist folder we want to use the html file from public folder.
why? because it has the id root and we will render all our react element using this root element .
To use the `public/index.html` modify the `HtmlWebpackPlugin`

**webpack.config.js**
```js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = (env, args) => {
  return {
    devServer: {
      port: 3000,
      open: true,
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: path.join(__dirname, 'public', 'index.html'),
      }),
    ],
  };
};
```
```bash
npm run build
```
check `dist/index.html`, the file should contain a div containing id root.

## Part 1.8: Setup react
```bash
npm i react react-dom
```
**src/App.jsx**
```jsx
import React from 'react';
const App = () => {
  return <div>App</div>;
};
export default App;
```

**src/index.js**
```js
import reactDom from 'react-dom';
import App from './App';
const root = document.getElementById('root');
reactDom.render(<App />, root);
```

```bash
npm start
```

you'll get an error 
```
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
```
from the error we get the idea that we need a loader to load jsx syntax

## Part 1.09: Enter babel
```bash
npm i -D babel-loader @babel/core   
```
modify webpack.config.js to load js and jsx files with babel
**webpack.config.js**
```js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = (env, args) => {
  return {
    devServer: {
    ...,
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          use: [{ loader: 'babel-loader' }],
        },
      ],
    },
    plugins: [...],
  };
};
```
```bash
npm start
```
you'll get an error
```
SyntaxError: /Users/fahadamin/workspace/test/src/index.js: Support for the experimental syntax 'jsx' isn't currently enabled 
```
though we are loading our js and jsx with babel, babel isn't configured yet to handle jsx files.
we'll use [@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react) to load jsx and @[babel/preset-env](https://babeljs.io/docs/en/babel-preset-env) to transpile modern js to es5 js for browser support.

create a `.babelrc.json` file in the root directory
**.babelrc.json**

```json 
{
  "presets": [
    ["@babel/preset-env"],
    ["@babel/preset-react", { "runtime": "automatic" }]
  ]
} 
```
create a `.browserslistrc` file for babel to transpile code for maximum brower support

**.browserslistrc**
> 1% # Browser usage over 1%
Last 2 versions # Or last two versions
IE 8 # Or IE 8

```bash
npm start
```
now your project can load jsx syntax

## Part 1.10: Loading CSS
our project can run jsx but it can't load any css yet, [css-loader](https://webpack.js.org/loaders/css-loader/) and [mini-css-extract-plugin](https://webpack.js.org/plugins/mini-css-extract-plugin/) to the resque.
we'll use css-loader to load css files and mini-css-extract-plugin to extract all the css in a single file.

```bash
npm i -D css-loader mini-css-extract-plugin    
 ```

**webpack.config.js**
```js
...,
const MiniCSSExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = (env, args) => {
  return {
	  ...,
      module: {
      rules: [
	     ...,
        {
          test: /\.(css)$/,
          use: [
            { loader: MiniCSSExtractPlugin.loader },
            { loader: 'css-loader', options: { modules: true } },
          ],
        },
      ],
    },
    plugins: [
	  ...,
      new MiniCSSExtractPlugin(),
    ],
  };
};
```

**ps:**  `options: {modules: true}` has been set for css-loader so that we can use module css files 

**src/app.module.css**
```css
.test {
  background-color: aquamarine;
}
```

**src/App.jsx**
```jsx
import React from 'react';
import style from './app.module.css';

const App = () => {
  return <div className={style.test}>App</div>;
};

export default App;
```
now your app will be able to load css.

## part 1.11: Loading assets
webpack 5 comes with asset handlers out of the box, we just need to treat some file type as assets.

**webpack.config.js**
```js
module.exports = (env, args) => {
  return {
	...,
    module: {
      rules: [
	    ...,
        { test: /\.(png|jpg|jpeg)$/, type: 'asset' },
      ],
    },
    ...
  };
};
```

add a logo in src folder

**src/App.jsx**
```jsx
import React from 'react';
import style from './app.module.css';
import logo from './logo.png';

const App = () => {
  return (
    <>
      <div className={style.test}>App</div>
      <img src={logo} alt='demo' />
    </>
  );
};

export default App;
```

now the project can load assets as well, our setup is ready for development.


## Finishing up
This is a very basic setup, we can enhance it with typescript and also optimize our app for production build with plugins. I'll try to keep adding to the series , at the end of the day hopefully we'll end up with a production ready optimize build.

all the code of this article is available [here](https://github.com/FahadAminShovon/custom-react-app/tree/part-1).