***Nov 2021 update – dependency updates, minor updates required due to dependency updates***

The Facebook team has made many improvements to getting up and running quickly with React using “create-react-app”.  There is also a lot going on under the hood of the app that gets created using “create-react-app”.  So, even though that is the quickest and simplest way to get started I feel it is more difficult in the long run when you want to start customizing the development process and the build process.  So, instead of going through that which you can simply do here.  I’ll first go over a simple basic react setup with webpack and babel and explain why you would also need these tools (the “create-react-app” also uses these under the hood).

Let’s dive right in from scratch

First, create a directory and initialize it with yarn or npm.  Throughout this blog you will see both yarn and npm commands, don’t run both, simple chose which you like and use one or the other.

$ mkdir react-starter 
$ cd react-starter
$ yarn init
$ npm init

Accept the defaults from the init command or enter what you’d like. 

Webpack – install and configure

Webpack basically takes your js files and any other dependencies and bundles them into one static js file. It can also generate other static assets as well.  It also supports “loaders” and is extremely configurable.

Again using yarn let’s add webpack

$ yarn add --dev webpack webpack-cli
$ npm install --dev-save webpack webpack-cli

This will add webpack and webpack-cli to your package.json file in the dependency section and update your yarn.lock file.  Once this completes we can create the webpack configuration file:

As of version 4, webpack doesn’t require any configuration, but most projects will need a more complex setup, which is why webpack supports a configuration file. This is much more efficient than having to manually type in a lot of commands in the terminal, so let’s create one

$ touch webpack.config.js 

Update the config file with the following:

const path = require('path');

const config = {
  mode: "development",
  entry: `${path.resolve(__dirname, 'src')}/index.jsx`,
  output: {
    path: path.resolve(__dirname, "dist"),
  },
};

module.exports = config;

This is the least required in the config file, it simple needs an entry point and on output location.  The entry holds the React application code base and the output is the directory where the built main.js file will be placed.

Lastly we need to tell webpack to copy our index.html file over to the dist directory.  First is to install a webpack plugin

$ yarn add --dev copy-webpack-plugin
$ npm install --dev-save copy-webpack-plugin

Then we need to import that in, at the top of your webpack.config.js file add this:

const CopyWebpackPlugin = require('copy-webpack-plugin');

Then in the config object add the following after the output property:

plugins: [
  new CopyWebpackPlugin({ patterns: [{ from: './src/index.html' }] }),
],

Now that webpack is all set we need that index.jsx file we are referring to in the config file.  Let’s create that

$ mkdir src
$ touch ./src/index.jsx

And add the following:

console.log('I just created an App with Webpack!');

Let’s add it to an html file so we can run it in the browser.  Create an index.html file in the src directory and add the following:

<!DOCTYPE html> 
<html lang="en-us"> 
    <head> 
        <meta charset="utf-8"> 
        <meta name="viewport" content="width=device-width, initial-scale=1"> 
        <title>React.js using Yarn, Babel6 and Webpack</title> 
    </head> 
    <body> 
        <div id="app" /> 
        <script src="./main.js" type="text/javascript"></script> 
    </body> 
</html>

Now type this command in the terminal

$ webpack --mode development

That runs webpack in development mode and generates the bundle.js file in the output directory “dist” and copies our index.html file. 

Now open the index.html file in the browser and look at the console.  You will see what we logged out:

I just created an App with Webpack!

Babel – install and configure

React with JSX and ES2015 make life much easier.  Sure you can write React code without JSX and ES2015 but let’s be real…why would you ever want to do that?  So, why babel you ask, ES2015 is still not fully supported in all browsers.  Also, with babel you can use the latest ES features from 2015 and beyond, not just ES2015.  Babel can be used alone without webpack however, to keep the build process simple we can use a babel loader for webpack.  Babel is used to transpile the ES20XX code to what browsers support.  Let’s add the babel packages that are required to make this happen:

$ yarn add --dev @babel/core @babel/preset-env @babel/preset-react babel-loader
$ npm install --dev-save @babel/core @babel/preset-env @babel/preset-react babel-loader

Babel-loader is used by webpack and babel-loader uses babel-preset-env and babel-preset-react to transpile our JSX and ES2015 code.  We need a little configuration for babel as well:

$ touch .babelrc.js

Then add this to the file:

module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
};

Now we need to tell webpack to use the babel-loader library.  Open webpack.config.js and add the follow to the bottom of the config object:

module: {
    rules: [{
        test: /\.jsx?/,
        include: `${path.resolve(__dirname, 'src')}`,
        use: {
            loader: 'babel-loader'
        }
    }]
}

Here webpack is using the babel loader, and looking in the src directory for files with an extension .jsx.  These are the files babel will transpile.

Phew!  All set with webpack and babel, now for the good stuff, React!  Use yarn to add react and react-dom

$ yarn add react react-dom
$ npm install --save react react-dom

Now replace the existing console.log statement in index.jsx with the following:

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

class App extends React.Component {
    render() {
        return(
            <p>Hello React!</p>
        );
    }
}

ReactDOM.render(
    <App />, 
    document.getElementById('app')
);

Then save and run the webpack command again:

./node_modules/.bin/webpack

Now reload the index.html file and you should see the following in the browser:

Hello React!

You can also split out the App class code to a separate file App.jsx like this(which you’ll want for when we add  a a Jest test:

import React from 'react';

export default class App extends React.Component {
    render() {
      return (
        <p>Hello React!</p>
      );
    }
}

Which then leaves only this in index.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
    <App />,
    document.getElementById('app')
);

You also need to add this to your webpack file so it knows to look for imports that are .jsx files along with others in the future, add it to the top right above output:

resolve: {
    extensions: ['.js', '.jsx', '.json', '.css', '.scss'],
},

And that’s it!  You now have React setup with babel and webpack.  Next up we’ll look into adding hot reloading and setup some scripts in our package.json file.  Adding these will enhance the development environment.  Also, if you haven’t already I highly recommend using Chrome and getting the React Dev tools for chrome.  This will allow you to see the details regarding your react components in Chrome.