Navigate back to the homepage

React From Scratch: React 16.7 + Webpack 4 + Babel 7 Tutorial

Kyrell Dixon
February 4th, 2019 · 4 min read

Facebook made it pretty easy to get started making a project in React with create-react-app. In fact they explicitly say:

You don’t need to install or configure tools like Webpack or Babel.

But what if you want to learn how to configure Webpack and Babel? This tutorial aims to demistify Webpack 4 and Babel 7 to give you the flexibility to configure these tools exactly how you want. We’ll go step-by-step from creating the initial index.js to having a complete working setup for React.

Table of Contents

Basic Webpack Setup

To get started, create the project folder and initialize the project.

1mkdir react-from-scratch; cd $_
2yarn init # or npm init -y
3touch README.md
4echo "# React From Scratch" >> README.md

After that initial setup, you want to have a package.json that looks pretty similar to:

1{
2 "name": "react-from-scratch",
3 "version": "1.0.0",
4 "author": "Kyrell Dixon <github@kaicodes.com>",
5 "license": "MIT",
6 "private": true,
7 "main": "index.js"
8}

Create a Basic Project Structure

Here we’re putting the index.js inside the src folder since that’s where Webpack will initially be looking for it. First, create the folder and index.js file with:

1mkdir src
2touch src/index.js

index.js

1console.log("The index file is working");

Bringing in Webpack

Getting Webpack setup is as straightforward as looking through the docs. The Webpack getting started page shows you the exact what modules you need to install. But to make it clear it’s as simple as:

1yarn add -D webpack webpack-cli # or npm i --save-dev webpack

After adding Webpack to the devDependencies section, there are a couple more setup steps. First, add a scripts object to the package.json file.

1"scripts": {
2 "build": "webpack"
3}

We also want to do some clean up and remove the main: index.js field in the package.json file. Your final output should look like:

1{
2 "name": "react-from-scratch",
3 "version": "1.0.0",
4 "author": "Kyrell Dixon <github@kaicodes.com>",
5 "license": "MIT",
6 "private": true,
7 "scripts": {
8 "build": "webpack"
9 },
10 "devDependencies": {
11 "webpack": "^4.29.0",
12 "webpack-cli": "^3.2.1"
13 }
14}

Basic Webpack Config Setup

If you’re working extensively with Webpack, typically you’ll use a webpack.config.js file to manage your configuration details. To get started, create a new config file using:

1touch webpack.config.js

From there, add some basic config details:

webpack.config.js

1var path = require('path');
2
3module.exports = {
4 mode: 'development',
5 entry: './src/index.js',
6 output: {
7 path: path.resolve(__dirname, 'dist'),
8 filename: 'main.bundle.js'
9 }
10};

For this config file, there are a few basic pieces to understand:

  • The mode determines what Webpack will optimize for. It defaults to production, but it can also be development or none
  • The entry point is the filepath (or paths) that Webpack starts bundling from
  • output is exactly what it sounds like. Here it is an object containing a filepath for where the final bundle will end up, and also the filename for the actual bundle.

To test it out, run yarn build or npm run build to build your final bundle.

React, Babel, and Webpack Setup

In this section, we’ll create a simple React application and configure Webpack and Babel until we get it to build.

Setup Basic Babel Config

Babel is a library that gives developers access to new JavaScript features that browsers may not support. It does this by transpiling later versions of JavaScript (ES6-7+) to CommonJS, a version of JavaScript that almost all browsers have supported for years.

To get started, add a few libraries:

1yarn add -D babel-loader @babel/core @babel/preset-env
  • babel-loader is what Webpack uses to transpile JavaScript at build-time
  • @babel/core is the Babel 7 library that does most of the compilation heavy-lifting
  • @babel/preset-env is a preset that determines the appropriate transforms to apply

From there a .babelrc file can be used to handle the Babel configurations. Create the file using:

1touch .babelrc

This is where @babel/preset-env is included:

.babelrc

1{
2 "presets": ["@babel/preset-env"]
3}

We also need to update webpack.config.js to use babel-loader

1module: {
2 rules: [
3 {
4 test: /\.(jsx?)$/,
5 exclude: /node_modules/,
6 use: {
7 loader: "babel-loader"
8 }
9 }
10 ]
11}

Setup Babel for React

We’re almost ready to start building with React! First we’ll have to install the React libraries react and react-dom. Then we can add in @babel/preset-react to make sure React can transpile the way it needs to.

1yarn add react react-dom # or npm i react react-dom
2yarn add -D @babel/preset-react # or npm i --save-dev @babel/preset-react

Add the @babel/preset-react preset to the .babelrc file

.babelrc

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

Creating a Simple React App

Now that all the setup necessary to get a React project up and running has been taken care of, we can go ahead and add the rest of the files for a simple project:

1mkdir public
2touch src/App.js public/index.html

I’m adding index.html to the public folder since that is a common convention that tells other developers this folder won’t be modified by Webpack. Inside of the file I added some basic HTML5 boilerplate.

index.html

1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 <title>React from Scratch</title>
8</head>
9<body>
10 <div id="root"></div>
11 <script src="../dist/main.bundle.js"></script>
12</body>
13</html>

Inside the body is a <div id="root"></div> that React will use to mount the file. I also added a hardcoded link to ../dist/main.bundle.js so that the actual JavaScript is included when we open index.html.

App.js

1import React from 'react';
2
3const App = () => (
4 <div>Inside the App component</div>
5);
6
7export default App;

index.js

1import React from 'react';
2import { render } from 'react-dom';
3import App from './App';
4
5render(
6 <App />,
7 document.getElementById('root')
8);

That’s it! Run yarn build or npm run build to make sure you’re not getting any errors. If everything is working fine, open index.html in your browser and you should see something like:

Adding html-webpack-plugin for Script Injection

You may have realized that hardcoding a script tag into the app isn’t the best way to connect it to the index.html file. If we change the name or the path of the compiled bundle from the build step, or if you decide to add in more build files, you will constantly have to update the script tag. To solve this issue, html-webpack-plugin dynamically injects output bundle into index.html. It also has a ton of other cool features you should check out in the docs.

To install the library use:

1yarn add --D html-webpack-plugin # or npm i --save-dev html-webpack-plugin

The Github page for the library has an example that I took and modified so that we can inject our bundle into an existing index.html file.

1plugins: [
2 new HtmlWebpackPlugin({
3 title: 'Custom template',
4 // Load a custom template (lodash by default)
5 template: 'index.html'
6 })
7]

webpack.config.js

1var path = require('path');
2
3module.exports = {
4 mode: 'development',
5 entry: './src/index.js',
6 output: {
7 path: path.resolve(__dirname, 'dist'),
8 filename: 'main.bundle.js'
9 },
10 plugins: [
11 new HtmlWebpackPlugin({
12 title: 'React from Scratch',
13 // Load a custom template (lodash by default)
14 template: 'public/index.html'
15 })
16 ],
17 module: {
18 rules: [{
19 test: /\.(jsx?)$/,
20 exclude: /node_modules/,
21 use: {
22 loader: "babel-loader"
23 }
24 }]
25 }
26};

index.html

1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 <title><%= htmlWebpackPlugin.options.title %></title>
8</head>
9<body>
10 <div id="root"></div>
11</body>
12</html>

Enabling Hot Module Reloading

Currently, in order to see any changes you add to your JavaScript reflected in the index.html, you have to rebuild the app after every change. This leads to a slower development cycles which make you less productive. Hot module reloading was created to solve this issue. Basically, it handles updating your html file whenever there is a change so that you don’t have to continually rebuild to see changes reflected.

There are several ways to enable hot module reloading with Webpack, but one of the most common ways is through the use of webpack-dev-server. This library is packed with tons of features like hot module reloading, more descriptive error messages, and a network accessible server that allows you to test your apps on any device on the same network.

To get it set up, first install the library using:

1yarn add -D webpack-dev-server

We can then add a start script to the package.json so we can run yarn start or npm run start to get everything up and running with:

1"start": "webpack-dev-server --open"

The --open flag automatically opens the page in your browser. From here you can run yarn start or npm run start to start the server based on the default configuration. If you want to modify it you can add the devServer object to your webpack.config.js. This will allow you to fine tune the server to behave in a way that’s best for you. An example object is:

1devServer: {
2 contentBase: path.join(__dirname, 'dist'),
3 compress: true,
4 port: 3000
5}

My final config looks like:

webpack.config.js

1var path = require('path');
2var HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports = {
5 mode: 'development',
6 entry: './src/index.js',
7 output: {
8 path: path.resolve(__dirname, 'dist'),
9 filename: 'main.bundle.js'
10 },
11 devServer: {
12 contentBase: path.join(__dirname, 'dist'),
13 compress: true,
14 port: 3000
15 },
16 plugins: [
17 new HtmlWebpackPlugin({
18 title: 'React from Scratch',
19 // Load a custom template (lodash by default)
20 template: 'public/index.html'
21 })
22 ],
23 module: {
24 rules: [{
25 test: /\.(jsx?)$/,
26 exclude: /node_modules/,
27 use: {
28 loader: "babel-loader"
29 }
30 }]
31 }
32};

Conclusion

This tutorial was meant to introduce you to some of what’s possible to get going in webpack. There is a ton of opportunity to extend the base configuration to include styling and more custom optimization. I’d encourage you to play around with some of the links mentioned earlier in the documentation to see just how flexible webpack can be.

Best,

—Kai

Join our email list and get notified about new content

Be the first to receive our latest content with the ability to opt-out at anytime. We promise to not spam your inbox or share your email with any third parties.

More articles from Kyrell Dixon

Git Life Hacks—The Hidden Features of Git You Never Knew Existed

Here's a series of Git features that have the potential to completely change your workflow for the better.

January 10th, 2019 · 3 min read

Creating a Product Hunt Clone with Vue.js and Tailwind CSS - Part 1

I've been getting a lot of questions on how to build a full stack Vue.js application using Vuex, Vue Router, and Firebase complete with authentication, so I decided to make one.

September 15th, 2019 · 11 min read
© 2019 Kyrell Dixon
Link to $https://twitter.com/kyrelldixonLink to $https://github.com/kyrelldixonLink to $https://instagram.com/kyrell.dixonLink to $https://www.linkedin.com/in/kyrell-dixon/