Creating a React Application from scratch (2021)

Muggle-born
7 min readMar 5, 2021

Preface

Exploring the intricacies of how: Babel, Webpack, or Hot Module Replacement work is out of scope for this article. I would recommend reading documentation available online if you are interested in learning more about how these tools work. Here are some links to get you started:

1 Getting Started

Let’s start by creating our folder structure and some files for our project. You can leave the files empty for now, we’ll update the content of the files throughout this article.

.
+-- react-app
+-- public
| +-- index.html
+-- src
| +-- App.css
| +-- App.jsx
| +-- index.js
+-- .babelrc
+-- webpack.config.js

Next, let’s initialize our project using npm and install some required development dependencies.

npm init -ynpm install -D babel-loader @babel/core @babel/cli @babel/preset-react @babel/preset-env style-loader css-loader webpack webpack-cli webpack-dev-server

There’s a lot we’re installing — and we’ll be installing more later — but let’s take a look at each of these packages real quick.

  • babel-loader: allows transpiling JavaScript files using babel and webpack.
  • @babel/core: main babel package required to do any transformations.
  • @babel/cli: allows you to compile files from the command line.
  • @babel/preset-react: allows you to use JSX syntax.
  • @babel/preset-env: allows you to use the latest JavaScript syntax without needing to micromanage which syntax transforms are needed by your target environment(s).
  • style-loader: allows you to inject CSS into the DOM. For example, import './styles.css' in your React component JavaScript files.
  • css-loader: interprets @import and url() like import/require() and will resolve them.
  • webpack: module bundler.
  • webpack-cli: allows you to run webpack on the command line.
  • webpack-dev-server: allows you to run a development server that facilitates live reloading.

2 Babel

Babel is a JavaScript compiler that allows you to write JSX, ES6+ syntax and ensures the code you write works across multiple browsers.

Out of the box Babel doesn’t do anything. It basically acts like const babel = code => code; by parsing the code and then generating the same code back out again. You will need to add plugins for Babel to do anything (which is what we installed in the step above).

2.1 Configure Babel

Update your .babelrc file to look like:

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

2.2 Validate Babel is working

Update your index.js file to look like:

const text = 'Babel';
console.log(`Hello ${text}`);

Run the following command npx babel src/index.js and observe the output in your terminal, which should look something like what’s shown below:

"use strict";var text = 'Babel';
console.log("Hello ".concat(text));

Notice how Babel compiled the code and used the presets we have specified in .babelrc to transform the syntax!

3 Webpack: Basic Configuration

3.1 Configure Webpack

Update your webpack.config.js file to tell webpack how to load and compile our modules. Your file should something look like:

const path = require('path');const REGEX_JS = /\.(js|jsx)$/;
module.exports = {
entry: './src/index.js',
mode: 'development',
module: {
rules: [
{
test: REGEX_JS,
exclude: /(node_modules)/,
loader: 'babel-loader',
}
]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
}
};

If you are interested in understanding how the configuration file works, you can read the available configuration options webpack has on their website.

3.2 Validate Webpack is working

Run the following command npx webpack and a file react-app/dist/bundle.js should have be created. The contents of the bundle.js file is a bit gross, but if the command ran without any problems then you’re following along perfectly and ready for the next step!

4 Adding React to the Mix

Up until now, we’ve written basic JavaScript. It’s time for React! Let’s start by installing some dependencies for React.

npm install react react-dom

4.1 Updating files

First, we need to update our static index.html file so React has an entry point to load all our components. This will generally be the only html file in the entire application since React is primarily written using JSX. Update your index.html file to look:

<!-- sourced from https://raw.githubusercontent.com/reactjs/reactjs.org/master/static/html/single-file-example.html --><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React Application</title>
</head>
<body>
<div id="root"></div>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script src="../dist/bundle.js"></script>
</body></html>

Next, we’ll update our index.js file to render our React application. There’s nothing fancy going on here and the code below should look familiar if you’ve used React before.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);

After updating index.js we need to update the contents of our App.jsx file to be a React component. We’ll also add a style to App.css to make sure that our css-loader is working in webpack. Update your App.css file by adding a style:

.App {
color: #303C6C;
}

Update your App.jsx file to be a basic functional component, which could look like:

import React from 'react';import './App.css';const App = () => (
<h1 className="App">
React!
</h1>
);
export default App;

Lastly, we need to update our webpack configuration to (1) tell webpack where to resolve our modules and (2) tell webpack how to load our css modules. If you currently try to run npx webpack you will encounter an error and be unable to bundle your application.

Update your webpack.config.js file to look like:

const path = require('path');const REGEX_JS = /\.(js|jsx)$/;
const REGEX_CSS = /\.css$/; // LINE ADDED
module.exports = {
entry: './src/index.js',
mode: 'development',
module: {
rules: [
{
test: REGEX_JS,
exclude: /(node_modules)/,
loader: 'babel-loader',
},
{ // LINE ADDED
test: REGEX_CSS, // LINE ADDED
use: ['style-loader', 'css-loader'], // LINE ADDED
} // LINE ADDED
]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
},
resolve: { // LINE ADDED
extensions: ['.js', '.jsx', '.css'] // LINE ADDED
} // LINE ADDED
};

Validate that everything is working up until this point by running the following command npx webpack. If the command ran without any problems then you’re following along perfectly!

4.2 Adding a Development Server with Webpack

We have added a bunch of configuration files and bundled our code several times using babel and webpack, but we have not actually seen what our web application looks like in the browser. Now we’re going to quickly setup our development server and run our web application.

Update your webpack.config.js file to look like:

const path = require('path');const REGEX_JS = /\.(js|jsx)$/;
const REGEX_CSS = /\.css$/;
module.exports = {
entry: './src/index.js',
mode: 'development',
module: {
rules: [
{
test: REGEX_JS,
exclude: /(node_modules)/,
loader: 'babel-loader',
},
{
test: REGEX_CSS,
use: ['style-loader', 'css-loader'],
}
]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
},
resolve: {
extensions: ['.js', '.jsx', '.css']
}
devServer: { // LINE ADDED
open: 'Google Chrome', // LINE ADDED
contentBase: path.join(__dirname, 'public/'), // LINE ADDED
port: 3000, // LINE ADDED
publicPath: 'http://localhost:3000/dist/' // LINE ADDED
} // LINE ADDED
};

We’ll also update our package.json file with a npm script command which will allow us to start the webpack development server. Add the following command to the “scripts” portion of your package.json file:

"scripts": {
"start:dev": "webpack serve"
}

Now we can run our React Application! Use the command npm run start:dev to tell Webpack to bundle your code and start a development server. After the code has been bundled Webpack should automatically open up a new Google Chrome tab/window and display your React Application!

You should also be able to update your App.css file or your App.jsx file then save the file and see live updates of your React application without having to use the npm run start:dev command again.

Congratulations to those who have made it this far! You just setup your own React project without using the create-react-app command.

4.3 Hot Module Replacement

Unfortunately, we’re not done yet. There’s one problem that we still need to solve which will require us to updating our Webpack configuration and install some more dependencies.

With our current configuration, we will lose all of our application state when a full reload is performed. Specifically, if we update our .jsx files. This may not seem like a problem worth solving, but using Hot Module Replacement can greatly increase your productivity while developing and save you a lot of time as your building your React application.

In this article we’ll be using React Hot Loader which is deprecated and expected to be replaced by React Fast Refresh, but at the time of writing this article React Fast Refresh for Webpack is in the experimental Stage and is not 100% stable.

Now let’s setup Hot Module Replacement! First, we need to install two new dependencies:

npm install react-hot-loader @hot-loader/react-dom

Second, we need to update our Babel and Webpack configuration. Update your webpack.config.js file to include an alias and update the devServer setting hot to true.

module.exports = {
// ...
resolve: {
alias: { // LINE ADDED
'react-dom': '@hot-loader/react-dom', // LINE ADDED
}, // LINE ADDED
extensions: ['.js', '.jsx', '.css']
},
devServer: {
open: 'Google Chrome',
contentBase: path.join(__dirname, 'public/'),
port: 3000,
publicPath: 'http://localhost:3000/dist/',
hot: true // LINE ADDED
}
};

Next, update .babelrc and add the react-hot-loader/babel plugin

{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": ["react-hot-loader/babel"] // LINE ADDED
}

Lastly, mark your root component as hot-exported. Make sure react-hot-loader is required before react and react-dom . Update your App.jsx file to look like:

import { hot } from 'react-hot-loader/root';       // LINE ADDED
import React from 'react';
import './App.css';const App = () => (
<h1 className="App">
React!
</h1>
);
export default hot(App); // LINE ADDED

All done :)

You can now add state to your React application, save your files, and in most cases you will not lose your application’s state. If you run into any problems setting up React Hot Loader please reference their documentation on Github.

--

--

Muggle-born

Hi, my name is Jeremiah. I build things on the web for fun. I enjoy figuring how and why things work the way they do, and I try teaching others along the way.