Learn Webpack by dumping create-react-app for once.
Tags: webpack, react, javascript, todayilearned
Published: 2020-08-27 10:37:45

So I have been reading [Learning React by Alex Banks and Eve Porcello](https://learning.oreilly.com/library/view/learning-react-2nd/9781492051718/) lately and in a chapter they went through the whole process of setting up a React project without using create-react-app, and I felt it is a great thing to know about how stuff works under the hood.

For everyone who doesn't have access to the book I thought it will be a good idea to write on the topic as articles are highly accessible.

> **P.S.** The book is worth the money nonetheless.

Also, I am assuming that you all must be knowing how to use the node package manager (npm) that comes with nodejs.

Now let's see how can we create our own React application
## 1. Project Setup
Start with initializing the package.json file with npm init so we can install modules. We will install the initial dependencies we need to get started.
```
npm init -y
npm install --save react react-dom serve
```
Now let's start working on setting up a skeleton (more like a folder structure). I recommend following along if you are a beginner or if you have never used webpack before.
```
> node_modules
> package.json
> package-lock.json
> /src (folder)
    > index.js
    > /components (folder)
      > App.js
      > Banner.js
      > Header.js
```
This is our project folder, it has a very basic setup consisting of a src folder which has the index.js file (this will be our entry point) and a components folder containing App, Banner and Header components.

There are many ways you can structure your app, this is just a very basic, easy to understand structure.

## 2. Making the Project
Our project will have a header and banner. So let's start by making the **Header component**.
```js
// ./src/components/Header.js
import React from 'react';

export default function Header() {
  return (
    <header>
      <h2>Checkout these lovely Unicorns</h2>
    </header>
  );
}
```
and now the **Banner component**
```js
// ./src/components/Banner.js
import React from 'react';

export default function Banner() {
  return (
    <section>
      🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄Unicorns For SALE🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄
    </section>
  );
}
```
Add these components to **App.js** and then render App component through index.js using ReactDOM.render().
```js
// ./src/components/App.js
import React from 'react';
import Header from './Header';
import Banner from './Banner';

export default function App() {
  return (
    <>
      <Header />
      <Banner />
    </>
  );
}
```
```js
// ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

ReactDOM.render(<App />, document.getElementById('root'));
```
So now we have completed writing our React components, let's move on to the next step.

## 3. Setting up Webpack
As our components are split into different modules we will be using webpack which is a **module bundler**. We cannot write everything directly into one file as that makes development messy so we use a module bundler that bundles everything into one big file that we can use for production.

Let's start by installing webpack and webpack-cli as dev dependencies.
```
npm install webpack webpack-cli --save-dev
```
So understanding webpack is a topic of it's own but I'll try to give a high level overview of it. You can see we have import statements all over the place, so webpack will start looking from the entry point we will define in the webpack.cofig file (index.js) and start building a dependency graph as soon it hits any import statements. In a casual language webpack is saying like "*Oh, this module needs that module and hey that module needs another module*". So it'll bundle everything into one big file.

Now let's build our very basic **webpack.config.js** file
```js
// ./webpack.config.js
var path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  }
};
```
This config file has an entry point and an output path, we are telling webpack to **start looking from index.js** and bundle everything (on the basis of dependency graph) and put the output in a folder named dist in a file named bundle.js, we have also set the mode to development so it will not do any minification and production stuff as of now.

So now webpack is ready to bundle our files but as we are writing JSX and using modern JavaScript we would want a loader which will covert these things into a syntax that every browser (new or old) can understand. We will be using the **babel loader** here.
```
npm install babel-loader @babel/core --save-dev
```
and now we will update the webpack config file to use this loader.
```js
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  module: {
    rules: [{ test: /\.js$/, exclude: /node_modules/, use: "babel-loader" }]
  }
};
```
The rules property is the place where you will be adding all the loaders (which are JavaScript objects) for various purposes (we will also add sass loader in the end).
Here we are only adding the babel loader which has a test property telling if you see any .js file use the babel loader on it except if the file is a part of the node_modules folder.

Webpack is all set now. The last thing will be to make a **.babelrc** file specifying the presets telling Babel which transforms to perform. As here we need to transform ESNext (modern JS) syntax to a syntax understandable by all browsers and also transform JSX too. We can do that by the following.
```js
// ./.babelrc
{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}
```

## 4. Running webpack
As we installed the webpack-cli you can run webpack from the terminal like this
```
npx webpack
```
This will make a dist directory and create the bundle.js in development mode as we stated in the config file. Change the mode to production when you are ready to ship or a common practice is to make scripts in package.json.
```js
// ./package.json
...
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode production"
 },
...
```
Do `npm run build` in this case.

## 5. Making the HTML file
Here we will be making the **index.html** which will link to the bundle.js file. I'm making it in the dist folder.
```js
// ./dist/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Unicorn Stable</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./bundle.js"></script>
  </body>
</html>
```

 The final folder structure would look like this
```
> node_modules
> package.json
> package-lock.json
> /dist (folder)
    > bundle.js
    > index.html
> /src (folder)
    > index.js
    > /components (folder)
      > App.js
      > Banner.js
      > Header.js
```
Now, open the HTML file in the browser and you will see that if everything went well we have the Unicorn sale ongoing 🦄.

## 6. Adding sass
Let's add some styles to our components. We will be using a single sass file but you are free to use separate sass files for each component as we just have to import it and webpack will bundle it for us using appropriate loaders.
```css
/* ./src/styles.scss */
h2 {
  background-color: #a0c3f0;
  font-size: 200%;
  text-align: center;
}

section {
  border: 2px dotted #ac307c;
  font-size: 150%;
  text-align: center;
  padding: 1em;
}
```
and then we import them into our App component.
```js
import React from 'react';
import Header from './Header';
import Banner from './Banner';
import '../styles.scss';
....
```
Let's now install the necessary loaders, we will need **style-loader, css-loader, sass-loader (which depends on node-sass)**.
```
npm install --save-dev style-loader css-loader sass-loader node-sass
```
and then we update the webpack config file to check for .scss files and apply the loaders in a set order.
```js
var path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
    ],
  },
};
```
The **order is to be kept the same** as sass-loader needs to be applied first then css-loader and style-loader at the end (the last one in applied first).

Let's bundle our code one last time with `npx webpack` and check the browser. All the styles would have been applied :)