From scratch — address issues and confusing bits — as an introduction — with React and Babel

Webpack. We’ve all heard talk about it and we may even know some of the things it does — like bundles up files for us! But what exactly is it, how can we use it and when should we use it?
Webpack has many capabilities:
- bundles up files for us
- minimizes file sizes
- code splitting
- manipulate and transform code via loaders and plugins
Because of all these significant capabilities making our applications fast and saving us time coding and doing repetitive tasks, Webpack has climbed to stardom over the years!
Bundling files, minimizing file sizes, and code splitting are all methods of creating fast applications — if your users need to download less when visiting and uses your app, then your material and content is delivered faster and they will be happy and because they’re happy, you’re happy!
But how can webpack be used to manipulate and transform our code?
- You can take SASS / SCSS / LESS files and convert them into CSS saving you time and reaping the useful features with respective loaders
- You can ensure your JS will work across various versions and use the syntax you prefer via Babel
- You can automatically add browser compatibility reducing your worries
Webpack

Dependencies
To explain this I’ll refer to a previous project, BetterReads, for a reference to a small React structure. We have the following structure, but what is the highest level of our application hierarchy? If you typically use the create-react-app CLI then you’ll probably know it’s index.js in /src.
/src
- index.js
- index.css
- App.js
- App.css/src/components
- Books.js
- Book.js
- Menu.js
- Shelves.js
- Shelf.js
- Search.js/public
- index.html
Let’s take a look at what is in index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter } from 'react-router-dom';ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);registerServiceWorker();
We’re importing several dependencies if you will, and from here the program will go and fetch all relevant information prior to processing or executing the following code — and that’s why it fails to compile or execute if it is missing. But what happens when App.js also has its own dependencies? Then the program will go and fetch its own dependencies too!
This is what Webpack does too! You give it a starting point, index.js, and it will take all of the dependencies and bundle it up for you.
Entry
With that said, here’s what Webpack’s documentation states:
An entry point indicates which module webpack should use to begin building out its internal dependency graph. webpack will figure out which other modules and libraries that entry point depends on (directly and indirectly).
By default its value is
./src/index.js
, but you can specify a different (or multiple entry points) by configuring the entry property in the webpack configuration.
This in the webpack.config.js file at the app’s root directory looks something like:
// webpack.config.jsmodule.exports = { entry: "./src/index.js"}
Output
You give an entry point for webpack as an input and webpack will process and give you an output — your bundle(s)! This is where you specify where you want to store those files — /public and /dist are both very common directories for where these live.
// webpack.config.jsconst path = require('path');module.exports = { entry: "./src/index.js", output: { path: path.join(__dirname, "/dist"),
filename: "[name].[hash].js"
}}
*Notice how we are requiring the path module/package — this is because the path for the output location requires an absolute path and it ensures we get exactly that without any fuss
You may see some people use resolve instead of join (bolded above), but join worked best for me. When I had it as resolve, sometimes I couldn’t locate the /dist after running the build command and someone on the net said it’s probably in memory which didn’t help at all — use join if you want to always be able to locate it!
With that said, if you are using a dynamic and unique name like we are here, then you may end up with many files as you continue to build. To fix that we can use a plugin to delete the /dist directory and re-create it at build time.
Loaders
The configuration for loaders goes in the module section after entry and output. There’s a lot going on and we’ll break it up into chunks (get it? :D).
test is where a RegEx detailing the type of files for the loader. The first is searching for JS files with the extension of .js. Some specifically use the JSX extension too — you’d want to include it here.
We don’t want to change anything in our node modules so we exclude it from the babel-loader.
You can configure babel here, on the CLI and also in its own .babelrc file — the latter is recommended by Babel, and I have this plugin here but the presets in the separate config file.
transform-class-properties includes a lot of the newer JS syntax and features including arrow function support and the object rest spread operator too.

// webpack.config.js...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== "production";module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
plugins: ["transform-class-properties"]
}
}
]
}, {
test: /\.(sa|sc|c)ss$/,
use: [
{ loader: devMode ? "style-loader" : MiniCssExtractPlugin.loader },
{ loader: "css-loader", options: { minimize: true } },
{ loader: "postcss-loader",
options: {
ident: "postcss",
plugins: [require("autoprefixer")()]
}
},
{ loader: "sass-loader" }
]
}
]
}
}
You can use multiple loaders, but note that the loaders are executed from last to first, or bottom to top. So for the case with CSS:
sass-loader --> postcss-loader --> css-loader --> style-loader
So, basically:
- The sass-loader takes SASS/SCSS and converts it to CSS
- postcss-loader uses the autoprefixer and adds cross-browser support with webkit and all
- css-loader will collect the CSS
- style-loader takes the CSS and adds a style tag onto the HTML file
Plugins
They also serve the purpose of doing anything else that a loader cannot do.
Plugins can serve as task-runners and help with the minification and optimization of your bundles to serve up! Some common and essential ones are the following:
This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
This helps create HTML files for your bundles which can change every time if you include a hash in the filename, but also provide a template for the bundle to use. I created a standard HTML file in /public and used it as the template for each bundle.
If you’re including hashes in the filenames for the bundle, then you’ll end up with many files upon each build — if this is undesirable, then this plugin is for you! It will find the output directory if it exists and remove it, then recreate it with the newly bundled files in it keeping it ‘clean’. This also resolves the issue with using join in the entry declaration.
This plugin will minify your JavaScript keeping it condensed and ensuring a faster application load as there’s less to download each time.
// webpack.config.js...
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');module.exports = {
...,
plugins: [ new HtmlWebpackPlugin({ template: "./public/index.html" }), new MiniCssExtractPlugin({
filename: devMode ? "[name].css" : "[name].[hash].css",
chunkFilename: devMode ? "[id].css" : "[id].[hash].css"
}), new CleanWebpackPlugin(["dist"])
], optimization: { splitChunks: {
chunks: "all",
minChunks: 2
}, minimizer: [ new UglifyJsPlugin({
uglifyOptions: {
compress: {
unused: true,
dead_code: true,
warnings: false
}
},
sourceMap: true
})
]
}, performance: { hints: false }};
React

When you use the create-react-app CLI, it will generate a boilerplate for React apps and will include various things including:
- React packages
- hot-reloading
- PWA setup
- testing
- webpack
- babel
But if you’re looking to create a more minimal React application, then you can create your own from scratch!
Start with creating a project folder and initialize it with NPM
// terminalmkdir react-app-from-scratch && cd react-app-from-scratchnpm init -y
// the -y option is for a standard setup to avoid filling the fields out in package.json
Install and setup what we really need for a React app
following the basic create-react-app structure
npm install react react-dommkdir src public && touch index.js App.js public/index.html// index.jsimport React from 'react';
import ReactDOM from 'react-dom';
import App from './App';ReactDOM.render(<App />, document.getElementById("root"));// App.jsimport React from 'react';class App extends React.Component { render() { return (
<div className='App'>Hello World</div>
)
}
}// public/index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React-App-From-Scratch</title>
</head><body>
<div id="root"></div>
</body></html>
Then we should include babel for backwards compatibility and usage of updated JavaScript syntax.
We will install them as dev dependencies because they’re not necessary for production.
npm install --save-dev @babel/core @babel/present-env @babel/preset-react babel-loader babel-plugin-transform-class-propertiestouch .babelrc
// in the root of the project directory// .babelrc{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
*The @’s are significant in indicating the proper packages for babel 7.1.0
*Include babel-plugin-transform-class-properties if you want to be able to use arrow functions and the object rest spread operator
*Note this will likely change, but always refer to their documentation, otherwise you’ll — like me — get trapped and confused as to what is what and how to work with so many versions and packages. Notice the version — at the moment — 7.1.0 at the top-left of the page; you can click on it and refer to other versions as well. It’s not exactly the best UX and as intuitive as you’d think, but that’s just my opinion.
And of course let’s use webpack to bundle our files together
use the same webpack.config.js file we broke down above and have it at the root of the project directory
npm install --save-dev autoprefixer clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin node-sass postcss-loader sass-loader style-loader uglifyjs-webpack-plugin webpack webpack-cli webpack-dev-servertouch webpack.config.js
Update our package.json for useful scripts to run and build our files
// package.json{
...
"scripts": {
"start": "webpack-dev-server --mode development --open --hot",
"build": "webpack --mode production"
},
...
}// now you can run 'npm start' and 'npm run build' scripts without worrying about much
// you can also include the modes in the webpack.config.js configuration
With this you’ll be able to code up a React application without all the bells and whistles, but with the necessary bare bones!