Before starting, the final repo can be found here on Github.

For the creation of my new website I wanted to skip using one of the many great boilerplate repos out there (for example React Boilerplate or Create React App) and instead setting everything up myself. The main reason was that I wanted to dive deeper and get a better understanding of all the parts needed. Learning how frameworks and libraries works and being able to put them in a larger context is something that I really enjoy and is one of the things that gets me motivated and makes me grow as a developer.

There are many different choices of what techniques to use when setting up an app like this, and its therefore also a large number of different personal preferences of what to use out there. For that reason I will show how to setup a bare-boned base using React, webpack, Babel, ESLint and Prettier. From there it is easy to add the libraries you want to work with. But enough with the mumbo jumbo, lets jump into the steps to setup a React app from scratch!

Prerequisites: You need to install yarn and node on your machine in order to follow along this post. You will also need a code editor (for example VSCode).

Let there be light

The first thing we need to do is to create a directory for your project. Open your terminal and change directory to where you want your project to reside. Then create your project directory (changing “react-from-scratch” to the name of your project) and initialize a new package.json :

mkdir react-from-scratch && cd react-from-scratch && yarn init -y

Linting and code formatting

Linting and code formatting is a great way to improve the quality of the code you write, keep a common code style throughout the project and to prevent unnecessary bugs. It is a good idea to add linting and code formatting the first thing you do, so that everything created afterwards is styled and formatted correctly right away. We will use ESLint, Prettier and EditorConfig to aid us with this. ESLint is the most popular linter for javascript while Prettier is an opinionated code formatter. Prettier helps us with code style while ESLint will mainly help with errors in the code (but can also check for code style). Since both Prettier and ESLint can check for code styles it is important to be aware that it is possible to configure them with conflicting rules. Lastly, the EditorConfig file will define configuration for the editor itself. Here is a great post on StackOverflow explaining the differences between ESLint, Prettier and EditorConfig and how they work together.

Start by creating an .editorconfig file in the root folder:

touch .editorconfig

Note that some of the settings in this file will be used by Prettier and will take precedence over Prettier’s own config, for example indent_style and indent_size . Add the configurations that you want to your .editorconfig file using the docs. My config ended up like this:

Continue by installing ESLint, Prettier and some useful plugins / configs (like Airbnb’s ESLint rules) as dev dependencies to the project:

yarn add eslint prettier eslint-config-airbnb eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react babel-eslint --dev

Next we will add a prettier config file:

touch prettier.config.js

Add the options that you want by looking at the prettier docs. Export the options as an object in the prettier.config.js file. I used these settings:

Continue by adding an ESLint config file:

touch .eslintrc.js

This file will define all the linting rules in the project. The config file will also define that we will let ESLint run Prettier for us. Prettier’s website contains great information about integrating with ESLint. The important thing here is to add the Prettier plugin, import the prettier config created above and pass it to the rules section of the config. It is also a great idea to extend the eslint-prettier-config in order to disable conflicting rules between Prettier and ESLint. Continue by extending the Airbnb and React configs, add the React and jsx-a11y plugins, and add any other ESLint rules you want to use. I created my .eslintrc.js file by basing it of react-boilerplate’s eslint config. It ended up like this:

Tip: If you are using VSCode and are having troubles getting it setup then this is a great video to help you get it working properly.

Bundling with webpack

I chose to use webpack for bundling my React app, mainly because it is one of the most widely used out there. Start by installing the following dev dependencies:

yarn add webpack webpack-cli --dev

Side note: For a smaller web app a great alternative to webpack is Parcel. It has gained a lot of attention for its zero-configuration, something that webpack later adopted in webpack 4.

To test that bundling works create a folder called src/ and add a file called index.js . This file will serve as the entry point for the app.

mkdir src && touch ./src/index.js

Add a simple console.log statement to index.js and save the file. If you run one of the following commands webpack will now bundle your app using its default configuration for either development or production mode:

webpack --mode development

webpack --mode production

Go to the generated dist/ folder in the root of your project to take a look at the result of the bundling. Neat, we have now setup basic bundling with webpack!

Add React

Lets add React and React Dom as dependencies to your project:

yarn add react react-dom --save

In the index.js created in the last section remove the console.log statement and create a minimal React component that looks something like this:

Continue by creating an index.html file in the src/ folder.

touch ./src/index.html

Add the following markup to the index.html file:

Great you have created an entry point for your app that will render a super simple React component! However, before we can test the component we will need to setup Babel and use it together with webpack.

Setup Babel

Babel is used to transpile your javascript to syntax the target browsers will understand. For this setup we will use the new. Lets add the following dev dependencies:

yarn add @babel/core babel-loader @babel/preset-env @babel/preset-react --dev

…and babel-polyfill as a regular dependency :

yarn add @babel/polyfill --save

@babel/core : Is doing the actual transformation of the code.

: Is doing the actual transformation of the code. babel-loader : Transpiling javascript files using babel-core and webpack.

: Transpiling javascript files using babel-core and webpack. @babel/preset-env : A preset that lets you only include the polyfills and transforms needed for the browsers your app will support.

: A preset that lets you only include the polyfills and transforms needed for the browsers your app will support. @babel/preset-react : A preset for React plugins that for example transform JSX to function calls.

: A preset for React plugins that for example transform JSX to function calls. @babel/polyfill: A polyfill that can be used to emulate a full ES2015+ environment.

If you are interested checkout this for further information about Babel plugins / presets.

Next we will create a configuration file for Babel. The configuration file will include all presets and plugins that we want to use, as well as options for the presets and plugins. Note that Babel 7 adds support for using javascript for the configuration file instead of JSON.

touch babel.config.js

Add the following to the configuration file:

The call to api.cache.invalidate tells Babel how we want to cache the configuration and will recreate plugins and discard the previous instance whenever something changes. We then specify the presets that we want to use (preset-env and preset-react). For preset-env we set useBuiltIns to usage , which means that Babel will polyfill features (using babel-polyfill ) if they are used in the code and if they are not supported by the target environments (see below).

We also want to specify the browsers that we want to support. This can be done in the babel.config.js file, but it is recommended to use a .browserlistrc since it could be used by other packages. Lets create it:

touch .browserlistrc

Then add the following query to the file:

The query says that we want to support browser with a global usage percent greater than 0.25% and that are not dead. Here dead means that a browser has been without official support or updates for at least 24 months. Browserl.ist is a great resources for figuring out queries for your target browsers.

Custom webpack config

Even though webpack 4 does not require a config by default it gives one much more control over what is going on, like defining that we want to use Babel together with webpack. Start by installing the dev dependency html-webpack-plugin, which will be used to inject our javascript bundles to our index.html template created before.

yarn add html-webpack-plugin --dev

Lets continue my actually making the config files. In the root of your project, create a folder called internals and then a subfolder called webpack.

mkdir internals && cd internals && mkdir webpack && cd webpack

We will then create 3 files: webpack.base.js , webpack.dev.js and webpack.prod.js .

touch webpack.base.js webpack.dev.js webpack.prod.js

The reason behind creating 3 different configs is that we want to create different configurations for production and development, but also share some configuration that are common between the two ( webpack.base.js ). First of, the base config will look like this:

The config exports a function that takes options as a parameter (here we will later pass our development or production config) and returns a webpack config object. Here is an explanation of the different configuration options:

mode : Tells webpack to optimize for development or production (default).

: Tells webpack to optimize for development or production (default). entry : The entry point of the application.

: The entry point of the application. module : Defines how different modules in the project will be treated. Here we specify that we want to run and transform all javascript files in the project, excluding files in node modules, through Babel.

: Defines how different modules in the project will be treated. Here we specify that we want to run and transform all javascript files in the project, excluding files in node modules, through Babel. devServer : Options used by webpack-dev-server.

: Options used by webpack-dev-server. plugins: List of plugins that customize the webpack build process. Here we add html-webpack-plugin, which will inject a script tag referencing the main bundle to index.html .

Continuing, the webpack.dev.js will look like this:

Here we use the base config file and specify mode to development, pass options to the dev server (see below) that we want Hot Module Replacement (HMR) and add the actual plugin for hot module replacement. HMR exchanges, adds and removes modules while an application is running without a full reload, which speeds up development vastly. More info on HMR can be found here.

The initial setup for the production config looks like this:

Here we change mode to production , remove HMR from the dev server and add dist/ as the content base for the dev server, which means that the server will serve the static files from there. When defining content base like this a recompile will trigger when files in the src/ folders are modified and saved. At this point the advantage of the split between the development and production configs might not be crystal clear, but it will become useful while the project grows and we want to add new webpack plugins to the configs.

It is now possible to use the configs when bundling by running the following commands (from the root of the project):

webpack --config internals/webpack/webpack.dev.js

webpack --config internals/webpack/webpack.prod.js

Server

We need to setup a development server when developing the app. webpack provides a development server called webpack-dev-server that provides live reloading. Lets install it:

yarn add webpack-dev-server --dev

It is now possible to run the app on port 3000 with HMR (specified in the webpack.dev.js ) by issuing the following command:

webpack-dev-server --config internals/webpack/webpack.dev.js

Or run the app in production mode:

webpack-dev-server --config internals/webpack/webpack.prod.js

Add scripts to package.json

It is a good idea to add the commands mentioned above to the script tag in your package.json . This makes it possible to run them using yarn <commmand> , eg. run yarn start to run the app in development mode. I added scripts for start , start:prod and build to my package.json .

Where to go from here

Hurray! 🎉 We have successfully created a very basic setup for a React app using webpack, Babel, Prettier and ESLint. This is of course a very bare-boned setup, but this is a good base to start from. Below I will provide some suggestion on where to go from here.

Styling

The most straight forward way of adding styling to your app is to install css-loader and style-loader as dev dependencies and add them to your webpack base config. I did left this out since I prefer using Styled Components for styling, which is a popular CSS in JS framework.

Routing

I would suggest using react-router for routing in your app. Its using a concept called “Dynamic Routing” instead of the more classic “Static Routing”. More info can be found here.

State management

When it comes to a global state container Redux is one of the most solid choices. My suggestion is to wait using Redux until you actually need it instead of throwing it in your app right away.

Unit testing

Jest is a great choice for unit testing. It is developed by Facebook so it is working well together with React. Here is a guide on how to set it up in a React app.

Like mentioned in the beginning of this post, the full project can be found here on Github.