Set up React Toolchain from the Ground up

setup.react.toolchain

This guide is designed for those who want to get their hands dirty to get up and running with a React application from scratch with no preconfigured environment, or hidden magic behind the scenes, or at least have some idea of what’s going on under the hood. To get the most out of this guide I highly encourage you to follow along step by step.

The puzzle

A React application build typically is a set of tools that is used to perform a complex tasks, and its consists of three main tools: Package manager Yarn or Npm, Bundler webpack or Parcel, and a Compiler such as Babel (new JavaScript features).

Prerequisites

  • Node version: >= 8.0.0
  • Package manager Yarn

📌 Snippets below uses $ to represent a shell prompt in a UNIX-like OS, though it may have been customized to appear differently.

Creating a project directory

$ mkdir app && cd $_
$ yarn init -y

The commands above is pretty straightforward as you can see; it does three things:

  • Creates a new “app” folder within the current directory,
    • then it changes the current working directory into our recently created project (second statement execution).
  • Initializing yarn as our package manager (interactively create a package.json file). The flag -y (or --yes) generates a package.json based on your defaults

Project directory structure

Before we dig-into installation, configuration and all the yucky things! let’s first structure our project directory to have the whole picture of our start and end points.

📌 for Windows OS users, you gotta use type null >> file instead of the touch command. The mkdir -p src/{...} command won’t work either, you might find this equivalent solution helpful or create these sub/directories separately.

$ mkdir -p src/components tests
$ touch src/index.html src/index.js
$ touch .babelrc .eslintrc.yml jest.config.js webpack.config.js

Above we executed commands that will result the following folder structure:

app/
├── src/
│ + components/
│ └── index.html
└── index.js
├── tests/
├── .babelrc
├── .eslintrc.yml
├── jest.config.js
└── webpack.config.js

💡 Tip: for Linux and Macintosh users, use ls -R command line to list all files & subdirectory contents

Dependencies

I prefer to use brace expansion (snippets below) for installing similarly named packages; its hackable, prettier, and time saver.

React: react, react-dom, react-hot-loader

$ yarn add react react-{dom,hot-loader}

Type checking for React props

As of React v15.5, using React.PropTypes is deprecated as mentioned in the official documentations and suggested to install and use the standalone `prop-types` library instead. Guess what? You don’t have to!

Two birds one stone; as of today React v16.0 uses prop-types as one of its dependencies (might be removed in future releases) and by installing react you should get prop-types out-of-the-box.

Babel: @babel/core@babel/preset-env@babel/preset-reactbabel-eslintbabel-jestbabel-loader

$ yarn add @babel/{core,preset-env,preset-react} -D
$ yarn add babel-{loader,eslint,jest} -D

The (@) prefix on node modules called “scoped packages”. Scopes are a way of grouping related packages together.

Webpack: webpack, webpack-cli, webpack-dev-serverhtml-webpack-plugin

$ yarn add webpack webpack-{cli,dev-server} -D
$ yarn add html-webpack-plugin -D

Linting (optional): eslint, eslint-plugin-react

$ yarn add eslint eslint-plugin-react -D

Testing (optional): jest, react-test-renderer

$ yarn add jest react-test-renderer -D
$ yarn add babel-core@7.0.0-bridge.0 -D

Configuration & Setup

Webpack: webpack.config.js

const HtmlWebPackPlugin = require('html-webpack-plugin');

const HtmlWebpackPluginConfig = new HtmlWebPackPlugin({
  template: './src/index.html',
  filename: './index.html'
});

module.exports = {
  module: {
   rules: [
      {
        test: /\.js|jsx$/,
        exclude: /node_modules/,
        use: { loader: 'babel-loader' }
      }
    ]
  },
  mode: 'development',
  devServer: {
    historyApiFallback: true,
    compress: true,
    port: 3030,
    open: true
  },
  plugins: [
    HtmlWebpackPluginConfig
  ]
};

Breaking down the webpack config:

  • rules: we attached babel as our loader, with rules which are matched to requests when modules are created
  • mode: webpack 4 introduces production and development mode, for defining webpack dev server and other stuff. We set the production mode later inline in package.json script
  • devServer: a set of basic options
  • plugins: used to serve our webpack bundles, and generates ./dist/index.html from template file under ./src
  • entry: As you might notice we have no entry point in our config, fortunately webpack 4 provides us with default entry point out-of-the-box ./src/index.js (you can override it)

Babel: .babelrc

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

Jest: jest.config.js

module.exports = {
  verbose: true,
  moduleDirectories: ['node_modules', 'src/components', 'tests']
};

I specified ’src/components’ as my module directory for tutorial’s purpose, but as your app grows you’ll have to override that setting based on your needs.

Eslint: .eslintrc.yml

parser: "babel-eslint"
env:
  browser: true
  node: true
  jest: true
  es6: true
extends:
  - eslint:recommended
  - plugin:react/recommended
settings:
  react:
    version: "16.0"
  • parser: specify the JavaScript syntax we need to support, in our case we set Babel (for modern JavaScript language use)
  • env: an environment defines global variables that are predefined
  • extends: a set of enabled rules from base configurations
  • settings: shared settings it will be supplied to every rule that will be executed.

I use YAML extension here for Configuration File to specify configuration information for an entire directory, but you’re absolutely free to use whatever you like, such as a JavaScript, or JSON.

Scripts: package.json

...
"scripts": {
  "start": "webpack-dev-server --hot",
  "lint": "eslint ./src",
  "test": "jest --colors -b -e --logHeapUsage",
  "build": "webpack --mode production"
},

React: src/index.html render container

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8”>
    <meta name="viewport" content="width=device-width">
    <title>App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>     

React: create src/components/App.js as our initial component

import React from 'react'
import PropTypes from 'prop-types';
import { hot } from 'react-hot-loader';

const App = ({message}) =><h1>Hello from {message}</h1>; 

App.propTypes = { 
  message: PropTypes.string 
}; 

export default hot(module)(App);

The App component is a stateless component that represents our application, and wrapped by hot a loader that tweaks in real time.

React: open and edit our entry point src/index.js

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

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

Run boy run

Before we keep moving on, we need to make sure that everything is working properly (scripts – package.json:)

$ yarn start

Expected behavior:

✓ A new tab should be opened in your default browser w/ localhost:3030 address
✓ DevTools verify that our Hot Module Replacement is enabled
✓ Webpack, Compiled successfully


Testing

We’re not going to deep dive into testing details w/ jest. We’ll create a minimal Snapshot Test, to make sure that our testing configurations is working correctly. Under tests/ directory create App.test.js file.

Jest: create tests/components/App.spec.js

import React from 'react';
import renderer from 'react-test-renderer';
import App from 'App';

describe('App Component Test Suite', () => {
  it('Should render properly to DOM', () => {
    const wrapper = renderer.create();
    expect(wrapper.toJSON()).toMatchSnapshot();
  });
});

…and then run test script:

$ yarn test

The first time this test is run, Jest creates a snapshot file.

Last but not least

We’re now ready to bundle our React application in production mode and optimizes the build for the best performance, all we need is run the following command:

$ yarn build

This will build the app for production to the dist/build folder.

Good job 🙌

Once you are done with that, and everything works as expected you now have taken care of the basic techniques to set up your own React application from scratch. You can take this even further by:

  • Extend webpack loaders, for example: Style Loader, CSS Loader, or Sass Loader to your webpack config
  • Add Git Hooks with Yarn/Npm scripts: precommit, prepush. For instance run linter before make a new commit
  • …and tons of more stuff you can play with

If you enjoyed this post, I’d be very grateful if you’d help it spread by emailing it to a friend, or sharing it on Social Media. Thank you!

Set up React Toolchain from the Ground up – Mahmoud Elmahdi