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
mkdir react-from-scratch && cd react-from-scratch && yarn init -y
Linting and code formatting
Start by creating an
.editorconfig file in the root folder:
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_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:
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:
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!
Lets add React and React Dom as dependencies to your project:
yarn add react react-dom --save
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
Add the following markup to the
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.
yarn add @babel/core babel-loader @babel/preset-env @babel/preset-react --dev
babel-polyfill as a regular dependency :
yarn add @babel/polyfill --save
- @babel/core: Is doing the actual transformation of the code.
- @babel/preset-env: 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.
- @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.
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
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:
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
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:
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).
- entry: The entry point of the application.
- devServer: 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
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
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
build to my
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.
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.
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.
Like mentioned in the beginning of this post, the full project can be found here on Github.
That’s it! Slap that clap button if you found this post useful. Thanks for reading ❤