Setup React and Webpack manually
Tags: webpack, javascript
Published: 2022-01-02 20:38:38

If you are using React, you are probably also using **create-react-app**, which is a great way for setting up a new React project in a fast and easy manner. It hides away all the build process config, so you can focus on writing code immediately. This way you don't have to worry about configuring Webpack, Babel, and the other build tools. But doing this manually can be very beneficial for learning purposes, so let's see a simple way to set up your project manually.

## Project initialization

Let's create a directory and initialize npm and git.

```
mkdir react-app
cd react-app
npm init
git init .
``` 
Our folder structure will look like this:

```
REACT-APP
└───src
│   └───App.js
│   └───index.js
|   └───index.html
└───package-lock.json
└───package.json
└───webpack.config.json
``` 
So we will create all the needed files and directories.

```
mkdir src
cd src
touch App.js
touch index.html
touch index.js
``` 
Then, we need to install React runtime dependencies.

```
npm install react react-dom
``` 

## React application setup

We will add content to the files in the `src` folder, so we have a working React application.

**index.html**

{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=index.html %}

**index.js**

{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=index.js %} 

**App.js**

{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=app.js %}

If you open`index.html` in the browser, it will be blank. Reason for this is that in `App.js` file we are using JSX when we write: ` return <h1>Hello World</h1>;`. The browser does not understand this syntax, so it needs to be transformed from JSX code into regular JavaScript. For this purpose, we use the Babel compiler.

## Babel setup

First, we will install Babel core and CLI packages locally.

```
npm install --save-dev @babel/core @babel/cli
``` 

### Use React preset

We also need to install and configure Babel to use presets, which will enable transforms for React. Let's install the required preset.

```
npm install @babel/preset-react --save-dev
``` 
To configure Babel we will create a `babel.config.json` configuration file in the project root.

```
touch babel.config.json
``` 

Inside the config file, we will define which presets we want to use.

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

### Testing the compiled code

After we run `babel src -d dist` compiled code will be located inside the `dist` folder. To use the compiled code, we need to reference compiled `index.js` file in `index.html` file.  To do this we will add `<script src="../dist/index.js" />`.  If we examine the code compiled by Babel, we will see that JSX syntax is compiled to valid JavaScript code. 

One thing worth noting is that we are using ES modules. Since modern browsers have started to support module functionality natively, our application should work out of the box. But if we open `index.html` in the browser, the first problem that we will encounter is that the browser does not recognize `index.js` as a module, therefore we get an error saying `Uncaught SyntaxError: Cannot use import statement outside a module`. To fix this we need to include `type="module"` in the `<script>` element, to declare this script as a module. Our script element will look like this:

```
<script type="module" src="../dist/index.js" />
``` 
This should help, right? Not really. If we try to run the page again, we will encounter the second problem: the browser is complaining that the React module relative reference is not valid. This is because the browser accepts only one kind of module specifier in an import statement: a URL, which must be either fully-qualified or a path starting with `/`, `./` or `../`.  One possible solution would be to use the relative path to React module located in `node_modules` folder. But again, we face another problem. If you check the `react` folder you can see that React currently only supports **UMD** and **CommonJS** modules. At this point, we would like to find some solution that would allow us not to worry about module formats of the dependencies and how to import them. Let's see what Webpack brings to the table and what problems it is trying to solve.

## Webpack setup

Webapck bundles all the required imports into one JavaScript file to be used on the client side. This is why we call it a bundler.  Since all modules are contained in one namespace, it resolves all dependency and module format problems for us. Other important features, that are worth mentioning are:

- **Tree shaking mechanism** - it can eliminate code that’s not used and imported by any other module.  
-  **Code-Splitting** - it can create multiple bundles that can be dynamically loaded at runtime.

To start using Webpack, we first need to install the required packages:

```
npm install webpack webpack-cli --save-dev
```
We are installing 2 packages:  main Webpack package and **webpack-cli** for running Webpack commands.

Next up, let's add Webpack configuration file:

```
touch webpack.config.js
``` 

We will start with the basic configuration:

{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=webpack.config.js %}

What's happening here? First, we are defining an entry point of an application. This is the point from which Webpack starts the bundling process and builds the dependency tree. In our case, the entry point will be `index.js` file. Also, we are defining the output path for the bundled file. We will use `dist` folder as the output path. 

Since we have the basic configuration set up, we can build the application with Webpack CLI. We can use `webpack build` command, but since this is the default command, we can use only `webpack`. But if we try to run this command Webpack will output something like this:

```
Module parse failed: Unexpected token (5:16)
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
| import App from "./App";
| 
> ReactDOM.render(<App />, document.getElementById("app"));
| 
``` 
Webpack is telling us that it does not recognize JSX syntax and that it needs something called loader to handle it properly. So, let's see how to do this.

### Babel loader setup

Out of the box, Webpack only understands JavaScript and JSON files. Loaders allow Webpack to understand other file types. For JSX files we will use Babel loader. We have already installed and used Babel core package and presets. Now we need to install the loader.

```
npm install babel-loader --save-dev
``` 
Then we can modify Webpack configuration to start using Babel loader. The configuration file will look like this:

{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=webpack.config.v2.js %}

Since **production** mode minifies the code by default, we will use **development** mode because of output readability. These are the explanations for some of the other properties that are used:

1. `test` identifies which file or files should be transformed
2. `exclude` identifies which modules should be excluded
3. `use` indicates which loader should be used to do the transforming
4. `presets` is a list of presets that should be used

Webpack should be satisfied now and will run the build command successfully. If we take look at the output bundle, we can see that Webpack packaged our app modules and React modules in one file. Now we can use this bundle in `index.html` by adding the script tag:

```
<script src="../dist/main.js" />
``` 
If you open the `index.html` file in the browser now, you will see that **Hello World ** message is displayed. This means our application is up and running. That's sweet 😌 . Let's see some ways that we can optimize the build process.

### HtmlWebpackPlugin setup

Right now, we are including the bundle in the `index.html` file manually. This is enough for our app to run. But in real world applications, we could use code splitting that would produce multiple bundles, or we could even hash bundle file names for caching purposes. It would be a tedious process to include them manually in our `index.html` every time the bundles are produced. So we will automate this process by using **HtmlWebpackPlugin**. Plugins are third party packages that can be used with Webpack to extend its functionality. In this case, we are using HtmlWebpackPlugin. First, let's install it:

```
npm install html-webpack-plugin --save-dev
``` 
And then modify the configuration file:


{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=webpack.config.v3.js %}

After running the build command, you will notice that now there is also `index.html` file included in `dist` folder. And the most important thing, `main.js` script tag is automatically injected. This means that we can remove the `<script>` tag from `src/index.html`.

### Development server setup

Currently, we are manually building the bundle after every change and opening the `index.html` to see the effects in the browser. This of course is not the ideal solution for the development environment and it would be best if we could automatize these steps. Webpack offers a special package called **webpack-dev-server** that acts as a development server and supports live reloading. This way, we will be able to host our bundle and any change in the code will cause reload of our application in the browser.

The important thing to mention here is that the devserver is creating a separate JavaScript bundle and output in the memory. It will monitor the dependencies of the entry point defined in the Webpack configuration, and re-create the output when changes are detected. We will be using this output when serving the application in the development environment, and not the output that was created by Webpack CLI. First, let's install the required package: 

```
npm install webpack-dev-server --save-dev
``` 
Next, we need to configure dev-server in the Webpack configuration file: 


{% gist https://gist.github.com/sdanko/a0b25916922fa4f76f3a47cfcb35416c file=webpack.config.v4.js %}

This is the basic configuration that will allow us to host the application locally. First, we define the `port` on which the server will run. After that, we set `open` property to `true`, which means that dev server will open the application in the default browser after the server had been started. We start the browser with `webpack serve` command. 
The application will be opened in the browser and any changes in the code will appear automagically and instantly in the browser. Remember, the dev server is serving in-memory output, so even if you clear the contents of the output folder, the page will still run.

## Conclusion

In this article, we have covered the basics of the Webpack ecosystem. We have seen how to initialize a React project from scratch and how to use Babel transpiler. Also, we have learned about Webpack loaders, plugins, and how to use Webpack dev server. Of course, these are jut the basics, and there are a lot more concepts to learn about Webpack ecosystem. I will cover some of them in the next posts.

You can check out the example repo  [here](https://github.com/sdanko/react-app).