Webpack and Babel for React from scratch
- Create the project
- Development Server
- Styled Components
- Code Quality
- Production Build
There are various ways to write a React app without the need to deal with tooling and configuration. Create React App, Next.js and Gatsby are all excellent and can be used to make a complete, finished app (depending on your needs).
This guide will explain how to configure a React application from scratch. You may need to do this to meet the needs of your project, or you might just be curious to learn how your React source ends up in a format that can be understood by browsers.
This guide assumes a few things that will not be covered:
- You have Node.js and NPM installed. At the time of writing, the latest versions are
- You’re comfortable enough on the command line to navigate the file system and run commands.
Create a new folder for your project. Let’s call it
react-project for now.
We’ll be using NPM to handle all of our dependencies, so we need to initialise it and create a
$ npm init -y (the
-y here will accept all default values for each property, which is fine to get started).
If you’re going to be using git, now is a good time to ignore some files. Create a
.gitignore file and add a few entires:
The Babel documentation is very good and explains everything in much more detail.
$ npm i --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
Create a config file for Babel named
.babelrc and add the presets we just downloaded:
Part of the process of creating bundles is running Babel, but it can automate many other tasks too. In this case, Webpack will:
- Copy files between our source and destination folders at build time.
- Start a development server that will automatically refresh as we work.
- Create an optimised build ready for production.
Install Webpack and the Webpack CLI as dev dependencies:
$ npm i --save-dev webpack webpack-cli.
Create a new webpack config named
webpack.config.js and add the below configuration to get started:
With this configuration, running Webpack will look for all files with a
To define what browsers we want to support, add a new property in
package.json whose value describes the target:
Webpack uses browserslist for this, and details on how to specify the browsers you’re interested in can be found in their documentation. For now, the last 2 versions of each browser will do.
We need an HTML for React to render to, so let’s create one and add to our Webpack config so the file is copied from our source folder to the destination folder.
Install the html loader and the html webpack plugin:
$ npm i --save-dev html-webpack-plugin html-loader.
Create a new folder named
src for our source files at the root of the project and create a new HTML file named
index.html. Add the below markup, noting the div with an ID of
TIP: If you’re using emmet you can create this very quickly using:
Add to your webpack config to use the HTML loader:
Now we have some tooling taken care of, we can install React and create the most basic App.
Install React and ReactDOM as dependencies:
$ npm i --save react react-dom.
That’s it for now - we’re just directly rendering one stateless functional component to the div with an ID of ‘root’ that we added to our HTML file earlier.
We’ve yet to see anything in a browser yet. In order to do that we’re going to configure Webpack to use Webpack Dev Server and Browsersync together.
The Webpack Dev Server is very quick and easy to set up and provides excellent features such as Hot Module Replacement. Browsersync allows you to use multiple browsers and devices at the same time, with scrolling and clicking synced everywhere.
Install the Webpack Development Server and Browsersync:
$ npm i --save-dev webpack-dev-server browser-sync browser-sync-webpack-plugin, and add the configuration to
We’re also adding
devserver properties which:
devtoolensures that sourcemaps are created and available when debugging in the browser.
devserverensures that any 404 errors fallback to
index.html, which is essential if using something like React Router for client side routing.
This config is:
- Telling Browsersync to run on port 3000 on localhost.
- Telling BS to proxy port 8080, which is where WDS is running. By doing this, we get the features of both tools at the minor expense of using two ports on localhost.
reloadto false stops Browsersync from reloading as we save - WDS has that covered.
package.json, we can create a new script entry for starting this development server (you can remove the
test script already defined):
$ npm start will bundle our code, write sourcemaps, start the dev server in production mode with HMR, strat Browsersync proxying WDS and open your browser on the correct url and port. You’ll also get a summary in your terminal:
The External URL can be used by any device on the same network to view your Dev Server. The UI URL is a dashboard for Browsersync where you can adjust some settings.
Visiting http://localhost:3000 will display your amazing React App.
Tip: React Developer Tools is an essential extension available for Chrome and Firefox that makes debugging React much easier, as well as looking under the hood of third party apps built in React.
Install styled components as a dependency:
$ npm i --save styled-components.
There is also a Babel plugin that provides better debugging:
$ npm i --save-dev babel-plugin-styled-components. Add this plugin to
We can now use Styled Components in our existing React app:
Prettier and ESLint
Prettier and ESLint are tools that help ensure that your code adheres to standards and is correctly formatted. There is a little bit of overlap in what these tools do, so by configuring them together we can get the benefits of both.
We’re going to use the popular Airbnb ESLint config to set some sensible defaults, including a config and plugin that takes prettier into account.
The Airbnb config expects various peer dependencies, and these can all be installed together:
$ npx install-peerdeps --dev eslint-config-airbnb.
We also need to install the babel parser for ESLint, Prettier and the Prettier configs and plugins so it’ll work nicely with ESLint:
$ npm i --save-dev babel-eslint prettier eslint-config-prettier eslint-plugin-prettier.
.eslintrc config file, extending each config,using the prettier plugin and using the babel-eslint parser:
We’re adjusting the Airbnb rules here slightly. The rules state that all files that contain JSX must use a
.jsx file extension. Some people prefer to just use a
.js extension, and this rule will allow either.
.jsx files, we’ll need to add a property to our webpack config that will ensure JSX files are resolved:
With this added, you could go back and rename
src/index.jsx if you prefer.
.prettierrc config with some personal preferences. The default config for Prettier is really sensible and well thought out, so if you like it you only need to include an empty object.
There are a few rules I like to override:
- Set quotes to single, as the default is double.
- Ensure trailing commas are used. I like this for source control, as it means a line won’t appear to have been authored by a developer who only added a comma at the end.
- Set a longer line length.
$ npm i --save-dev stylelint stylelint-processor-styled-components stylelint-config-styled-components stylelint-config-recommended.
.stylelintrc with this config, which extends the configs we just downloaded. I have also added some rules I like, but you should take the time to read the available rules as I find the recommended rules a bit too lenient.
If using Visual Studio Code, the Prettier, ESLint and stylelint plugins will add features to the editor. Issues will be underlined in code, and Prettier rules will be applied on save so you never need to think about things like indentation.
To enable the format on save feature, add to your VS Code
prettier.requireConfig setting will prevent VS Code from applying prettier formats on save in projects that do not have a prettier config. This is especially useful if you’re in an older codebase with established formatting that deviates from Prettier.
Add scripts to
Each of these scripts will look inside our source folder and all folder contained within, and check that the linting rules pass for all js and jsx files.
When running the CSS linter, you’ll notice it flags an issue. We have used the
red colour keyword in our Styled Component which our rules forbid. To solve this, go back to
src/index.js and set the colour in hex:
Rerunning the linter should now show no output at all, as all rules have been adhered to.
Linting on save with Webpack
Webpack can be configured to run the linters as we work, as part of our existing development server configuration.
First, we need eslint and stylelint loaders:
$ npm i --save-dev eslint-loader stylelint-custom-processor-loader, and add to our existing rule for js/jsx files:
This extends the existing rule for js and jsx files, and adds the ESLint and stylelint loaders. The order of the loaders in the
use array is important - we need to lint our source files before Babel touches them, so ensure each linting loader is after the Babel loader in the array.
mode parameter we already used for our development server.
Add a new
build script to
--progress option here will print the build progress to the console when run. It’s not essential, it just provides a little more information which can be helpful if your build is large.
npm run build will create a new
dist folder containing our bundles, and will copy the HTML file we created earlier.
We can add some optimisation to the bundle in our Webpack config. Here, we’re going to strip all comments which the built in build doesn’t do. Install the uglify JS plugin as a dev dependency:
$ npm i --save-dev uglifyjs-webpack-plugin, and add to our Webpack config.
This will be our final Webpack config.
We should now have a marginally smaller bundle. Not by much, but it all helps.