Breaking The Shackles of create-react-app
The path to becoming considered a "frontend developer" is somewhat vaguely defined in 2020, however, it still begins with basic HTML, CSS, and JavaScript. From there, you'll learn about NPM, and Yarn and add CSS preprocessors like SASS and LESS. From that, most people jump straight into frameworks, and it's all downhill from there.
For a lot of people, the road to React is paved with a large number of "create-react-app" projects. This isn't necessarily a bad thing, it exists for a reason, and the wonderful people behind it more than know what they're doing. However, it also masks a lot of what's going on in the backend...of your frontend.
What is create-react-app
First lets talk about what's generated by create-react-app. A file directory structure, yes. Also a pre-loaded package.json - this is very important. If you started from scratch you would have to build that yourself. Daunting, no? Not at all. Finally, Webpack and Babel configurations, which we'll also discuss.
The reason we want to build our own project is because otherwise we would have to "eject" the project in order to view these configurations, this restricts your ability to take full control of your own project.
The State of JavaScript
JavaScript is a uniquely complex language. In the same way that lower-level languages must be compiled to the specifications of the processor architecture, JavaScript must match each browser engine's specifications for what it expects JavaScript to be.
The difficulty with JavaScript is that it iterates on itself extremely quickly, and web developers tend to pick up these iterations and begin using them almost as fast (see: ES6). However, not all browsers are capable of interpreting this code. On the function level, we solve this with polyfills. Base structural changes to the language on the other hand call for more drastic measures.
Say we want to use ES6 arrow functions, or more importantly, the ES6 module. Only recently did browsers begin supporting these features. In addition, we're using Ract, which leverages the JSX format, a syntax extension to the standard JavaScript extension which is certainly not interpretable by browsers, so how do we make this all work together? Answer - Babel.
Babel is a JavaScript transpiler that gets added to your Webpack configuration as a loader. By working Babel into the Webpack deployment, web developers are able to more passively keep up with the constantly changing standards we deal with in frontend development.
With all of these requirements to achieve a functioning application, you can begin to see exactly why create-react-app is so appealing. But eventually, we need to pull back the curtain and take a good long look at the little man pulling the levers.
Understanding Your Dependencies
So you've created a directory and done an npm init
and you'd like to know what's next.
The trouble with React applications is that they pretty clearly are too large to serve up to the client. Between massive CSS files, thousands of lines of JavaScript, and Redux out the wazoo, not to mention everything is served up as twenty different files. Enter Webpack.
Webpack is a complex beast, and frankly deserving of an article entirely its own. It's what's referred to as a module bundler.
It does this by building a dependency graph of your project, and then relying on a number of loaders. Simply put, Webpack will traverse the dependency graph it's built and attempt to resolve each file it encounters against one of its loaders that are defined in its configuration file. If it isn't able to do so, it raises a runtime error. Otherwise, it generates a bundle and writes output in the form of a bootstrap script with a manifest file (think similar to Java's Maven). This manifest file describes how it should be executed in the browser.
Start by installing Webpack
npm install webpack
And webpack-cli
npm install webpack-cli --save-dev
I install path. This will make for a cleaner configuration file as our configuration file grows.
npm install path
Now, open up your package.json to add the script to enable you to build your project.
"scripts": {
"build": "webpack --mode production"
}
Next, let's create our Webpack configuration file (webpack.config.js). This is where the magic happens.
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: path.join(__dirname, 'app', 'index.js'),
output: {
path: path.join(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js'
},
}
What does this do? Let's start from the top, and work our way down.
Entry is simple, that's our source directory. In this case, I've named it "app" and my primary entry point is index.js.
Next up is the output. When Webpack runs it will create bundle.js, and place it in the build directory. That will be your final product.
Technically, we're now able to run Webpack, however, like we discussed before, older browsers will not be able to understand the ES6 and JSX syntax typically used in React development.
So, we'll add Babel to transpile our code to the standard ES5.
npm install @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev
Babel also requires its own configuration file, .babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
If you would like to keep it simple, this can also be done right in your package.json. But beware, if you start adding plugins, you're going to get a lot of bloat, and editing can become a bit of a nightmare.
Now that Babel is configured, Webpack has to be aware of it. Hence the babel-loader. Let's go back into the Webpack configuration and set up a rule. This will tell Webpack what file types to send to Babel via the loader.
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
]
}
That covers us for JavaScript, but the app looks like hell. Turns out, Webpack doesn't load CSS files natively.
create-react-app starting to look pretty good, huh?
Fortunately, this is simple, and allows you to easily select the type of CSS preprocessor you want to use in your project. This will vary from pre-processor to pre-processor, but you'll always need two things: a css-loader, and a style-loader. Both equally poorly named, as the css-loader takes the output from your pre-processor specific loader (for instance, your less-loader) and converts it to CommonJS, a specification used in Node.js. style-loader then takes this and loads it from standardized JavaScript as a style, naturally.
npm install less-loader css-loader style-loader
rules: [
{
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'less-loader'
}
]
}
]
Finally, you'll want to be able to run the application locally. You could do this by simply creating a production build every time, but that's not exactly ideal. Instead, we'll install the webpack-dev-server.
npm install webpack-dev-server --save-dev
Add the script to your package.json
"scripts": {
"start": "webpack-dev-server --mode-development",
"build": "webpack --mode production",
}
And update your Webpack configuration.
entry: path.join(__dirname, 'app', 'index.js'),
output: {
path: path.join(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: path.join(__dirname, '/'),
},
contentBase will act as the entry point for our local development server. Webpack-dev-server does not create it's own directory like the production build, as it will need to update each time we change the code locally.
Wrapping Up
Now there's a lot more you can, and should do once you've made it this far. Having read this, you should have a general idea of what Webpack does, how to install and configure it, and how to add Babel, and webpack-dev-server for a functioning development environment.
You'll go from here to creating your React components, but you'll also begin adding your own packages - Eslint, React Router, React Redux, etc.
For more interesting reading, comments on React and Webpack, follow me on Twitter. Or check out my other posts like Idempotency in API Design or .
Top comments (2)
Thank for posting! Nice and clear explanation for Webpack.
Minor Adjustment:
path
comes with nodejs by default so no need to install it nodejs.org/api/path.html