Categories
Frontend React Webpack

How to configure a React application with Webpack & Babel

When working with an old school React app. It is highly possible that Webpack is involved in the base setup.

In this guide I would like to teach you how to setup a basic React app using Webpack and Babel. But first we need to understand some concepts.

  • React
  • Webpack
  • JSX
  • Babel

If you already know all of this, please jump to the Steps section (click here)

React

React is a library that uses components for building UI. All you need to know about React is:

  • It uses Components, and every component normally represents a piece of UI in your app.
  • Components are Functions
<MyComponent /> = function MyComponent() { ... };
  • Those functions must return a React component (normally JSX).
function MyComponent() {
  return React.createElement("h1", null, "Hi");
}
  • JSX it’s an extension of React for writing React elements as HTML (But remember, it’s not HTML).
function MyComponent() {
  return <h1>I'm not HTML, I'm JSX</h1>;
}
  • You can pass properties (props) to components like you do with HTML tags.
<MyComponent name="Jorge" role="frontend" />
  • Components also have a life cycle, you can identify some callbacks when rendering a new component, like componentDidMount, componentWillUnmount, etc.
  • And other crazy things…

Webpack

Normally when creating a web application, you will have a main index.html and then you will import JS/CSS files.

A React app it’s quite similar, but consider that every file that you import needs to be imported it in a certain order for working correctly.

This how normally a web application structure would be:

Webpack will create a bundle of all the imported files that you are using in your app, including JavaScript libraries, CSS, etc.

Webpack is a little bit more complicated than that, you can package more than one bundle, and if you are using things like SCSS, TypeScript, or any other web technology. You can configure webpack to bundle those kind of files too.

Let’s start with some steps for configuring webpack.

Step 1: Basic Scaffold

We will start by generating our main folder directory. Let’s call it basic-react and then enter to this new directory.

mkdir basic-react
cd basic-react

Create a package.json file with the command npm init -y.

  • -y is a parameter that tells npm to create a package.json with the most basic possible information.

This is what we have right now:

basic-react
-- package.json

Step 2: Install dependencies

We will need to install a few dependencies for having everything setup correctly, install this libraries with the --save or --save-dev arguments for saving the references inside the package.json file.

npm install react --save
npm install react-dom --save
npm install path --save
npm install @babel/core --save-dev
npm install @babel/preset-react --save-dev
npm install babel-loader --save-dev
npm install webpack --save-dev
npm install webpack-cli --save-dev

Step 3: Create a script

Let’s take a look of the package.json file. See that we have a script already:

...
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
},
...

The scripts are custom scripts that we create in order to run custom commands inside our app.

You can run your scripts with npm run SCRIPT_NAME

Let’s create one script for generate the bundle we just talked above.

...
"scripts": {
  "build": "webpack"
},
...

We need to configure how webpack is going to bundle our app.

Step 4: Webpack configuration

Every webpack configuration needs three things:

This means that webpack.config.js needs to export those 3 keys:

module.exports = {
  mode: ...
  entry: ...
  output: ...
}
  • mode: webpack can do different stuff depending of the mode environment (production, development, etc). By default webpack uses development if we don’t tell a specific mode.
  • entry: This is the path where webpack will try to search for a root file to start chaining our bundle. By default webpack search into src/index.js
  • output: We need to tell webpack an absolute path of where our bundle is going to be created.

If we don’t specify any configuration to webpack, it will try to look for a an entry file in src/index.js and the final output will be placed in dist/main.js.

Since our basic-react project doesn’t have the src/index.js file, then it will throw the next message:

ERROR in Entry module not found: Error: Can't resolve './src' ...

Try to run npm run build in order to verify that.

In order to configure webpack ourself, we need to create a configuration file.

Let’s create a webpack.config.jsfile in the root directory.

touch webpack.config.js

We should have something like this:

basic-react
-- node_modules
-- package.json
-- webpack.config.js

Let’s create our configuration

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './public/index.js',
  output: {
     filename: 'bundle.js',
     path: path.resolve('dist/js')
  }
}

We are using path library for specifying an absolute path in the output section, this is required.

Let’s create our entry file in public/index.js

mkdir public
cd public
touch index.js
public cd ..

Our current three directory should look something like this:

basic-react
-- public
---- index.js
-- node_modules
-- package.json
-- webpack.config.js

Then run npm run build

> basic-react@1.0.0 build basic-react
> webpack

Hash: 7c0a5056798f58c491e3
Version: webpack 4.43.0
Time: 56ms
Built at: 2020-05-24 02:39:35
    Asset      Size  Chunks             Chunk Names
bundle.js  3.79 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./public/index.js] 0 bytes {main} [built]

You just created your first bundle, which if you see in your three directory you will now see a new dist/js/bundle.js file.

basic-react
-- dist
---- js
------ bundle.js
-- public
---- index.js
-- node_modules
-- package.json
-- webpack.config.js

This means that if we write something in our public/index.js file, and then run npm run build it will ends out in dist/js/bundle.js file.

Our configuration is not ready yet, but we can say the base is already done.

Now, let’s integrate @babel/core so we can write ECMA6 in all our JavaScript files with no problem.

Loaders

Loaders are some kind of modules that helps webpack to integrate new libraries in order to process every single file with some kind of compiler. We can represent it with the next image:

For example, if use ECMA6, we need to teach webpack how to transform ECMA6 into standard JavaScript, this is where the loaders are useful.

We need to teach webpack how to compile ECMA6 -> JavaScript.

const path = require('path');

module.exports = {
  mode: ...,
  entry: ...,
  output: ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}

module is an option to determine how the different types of modules within a project will be treated.

Inside module we have an array of Rules, every rule can have this three elements:

...
{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'babel-loader'
}
...
  • test: It’s just a regular expression for every processed file in out chain, in this case we are telling webpack “Hey, use babel-loader only in javascript files”.
  • exclude: We are just saying “Do not process any file inside node_modules folder, this is because this would take a lot of time, and normally node modules already have a dist version.
  • loader: The name of the loader we will use for the previous rules.

There are a lot of different configurations you can use inside every rule, but in this tutorial we will only use the basic stuff. Try to read the webpack documentation for reference.

Step 5: Create your HTML file

Now that we are processing all our JS files with @babel/core which transforms our ECMA6 into a more standard JavaScript, we need to insert our bundle in an actual HTML.

Let’s create an index.html file in the root directory

touch index.html

Currently you should have something like this:

basic-react
-- dist
---- js
------ bundle.js
-- public
---- index.js
-- node_modules
-- package.json
-- webpack.config.js
-- index.html

This file should only import the dist/js/bundle.js file for now

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Basic React</title>
</head>
<body>
  <div id='root'></div>
  <script src='dist/js/bundle.js'></script>
</body>
</html>

Notice that we have a <div id='root'></div> element, we will insert our whole app inside this div.

Let’s test everything we have done by doing a console.log(“hello world”) inside our public/index.js file, running our npm run build command and then opening our new index.html file. You should see “hello world” inside the console.

Perfect! We are almost there.

Step 6: Create your first component

Now, we can start with React. If you don’t know anything about how to write react components or how to insert them inside the DOM. I highly recommend you to read the Getting Started guide from the react documentation page.

Check the next code:

import React from 'react';
import ReactDOM from 'react-dom';

const container = document.getElementById('root');

const MyComponent = () => {
  return (
    <h1>This is my first component</h1>
  )
};

ReactDOM.render(<MyComponent />, container);

We are doing two things basically:

  • Creating a component
  • Inserting the component inside our <div id='root'></div> container.

Once you have this, run the build script and…

Dexter: Surprise Motherfucker - YouTube

A huge error telling you “Hey dude! I don’t understand what is that "HTML" inside the JavaScript file” This is completely normal since babel doesn’t know how to read JSX.

Step 7: Teach JSX to Babel

By default, Babel doesn’t know how to transform JSX into JavaScript, here is where our library @babel/preset-react takes place.

Let’s tweak a little bit our webpack configuration:

...
module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-react']
        }
      }
    }
  ]
}

With this, now we can run our build script this no problem, Babel will know how to transform JSX into JavaScript, and our components will render correctly.

Note: We are injecting @babel/preset-react inside the webpack configuration, but you could do it in your .babelrc file too.

Try to open your index.html file, it will render our h1 tag correctly.

We are pretty much done for this post, if you have any questions try to reach me through my twitter @jorgechavz.

Probably you noticed that you have to run npm run build every time you make a change in your public/index.js file. There is something we can do about it, we can use webpack-dev-server in order to run a server locally which will be handy when you want to develop fast, it will have some really cool features including hot reload so we can se our changes instantly.

In the next post you will learn how to configure webpack-dev-server.

Github repository: https://github.com/echavezNS/basic-react