Understanding React boilerplates

  • way to deliver all the code we wrote along with node_modules to the user
  • way to make JSX and ES6 run in our browser

Overall project information and configuration

First thing you need to understand is the purpose of using package.json — this is a meta-file that contains information about your project such as its name, version etc. but also contains a list of 3rd party libraries and their configuration. Let us skip to devDependencies of package.json — we can notice a couple of important libraries:

  • babel — this is a transpiler that will change our JSX, ES6 and other types of source into ES5 which runs without issues in most of the browsers, all packages with (using the babel-preset- packages)
  • a couple of -loader libraries that webpack uses to understand the corresponding file type (more on that in webpack section)
  • eslint — this is a popular linter, a small tool that will perform various checks on your source to make sure all your developers are on the same page with coding standards etc.
  • express — a small yet powerful http server to serve our application when we are developing it
  • jest — a test runner that is gaining popularity in the React ecosystem
  • react — because what else :)
  • webpack — lastly, webpack will gather all our files, convert them to a format our browser can consume and deliver it in a elegant manner

Webpack

Another important thing in your project is webpack. By default the configuration will live in webpack.config.js but in my example I’ve created a /webpack directory with multiple files — two of which are there to provide config for different environments and one to abstract some shared code. Let’s start with webpack.config.production.js

  • entry — this points to the file (or multiple files) webpack should start at, and resolve all the imports / require statements, once the “tree” of imports is finished (no more imports left to process) the bundling process is mostly done
  • output — where to put the file generated from the previous step
  • module — this section contains loaders that tell webpack how to handle different file types (recognized by extension), we will get back to this one in a second
  • plugins — the power of webpack lies in the plugin system, here we implement only a few:
    - a custom function that will clear out /dist directory
    - ExtractTextPlugin which will create a style.css file with all the CSS of our application (see module section explanation)
    - DefinePlugin — allows us to define some placeholder / variables, which will be replaced by webpack at bundle time (so only once, not every time we run our app — this can’t be dynamic!) with the corresponding value
    - UglifyJsPlugin — performs minimization and other optimizations on our JS and CSS files
    - HtmlWebpackPlugin — this will create for us a .html file which will load our application (here based on a provided template)
  • resolve — tells webpack where to look for file when we import / require them, the DirectoryNamedWebpackPlugin allows us request files easier when their name matches the directory:
import Button from "Button";
// will look for Button/index.js but also Button/Button.js
  • test — a regular expression ran against every file that is imported / required
  • use — information on how to process the file, this can be either an object or a function (usually in the form of plugins).
{    
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader",
query: {
presets: ["es2015", "react"],
plugins: ["babel-plugin-transform-class-properties"]
}
}]
}
  • test — will match any file ending with .js
  • exclude — skip whole node_modules (this is a regular expression pattern)
  • use.loader — use the babel-loader for this kind of file
  • use.query — pass some options to the loader; here we’re telling it to handle es2015 (ES6) and react (JSX) file, and to also add the (not yet contained in any preset) support for class properties
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [{
loader:'css-loader',
options: {
modules: true,
localIdentName: '[local]--[hash:base64:5]',
}
}, {
loader:'less-loader'
}]
})
},
  • use — you might notice that there are multiple entries defined in the inner use field, in that case all the configuration is analyzed right-to-left or “from the end”; here — the file will be first passed through less-loader and then css-loader. We also define some configuration options for css-loader to support css-modules which are very handy when working on big projects where class names might unintentionally overlap
  • ExtractTextPlugin — by default, all the CSS we import into our project by doing import Styles from "style.less" would end up in the bundle and be embeded in the HTML file by creating a <style> node. This might not be the best solution, because it’s not very performance friendly and would increase the size of our js file; to get around that we will use ExtractTextPlugin to pull out all of the CSS and place it in style.css (remember? we talked about it in the overall webpack.config.production.js file)

Webpack configured for developement

Ok, we have our basic webpack config done, this will allow us to generate a bundle with our application but it’s not very developer friendly. Let’s look into webpack.config.develpment.js and see what we can change:

  • devtool — our code is going to be transpiled, which means that the source in browser will no longer match our original source; in order to combat this and see where the errors really happen we will use source maps (here: as a .map file) that our browser can use to translate the bundle into source code
  • entry — our entry now contains two entry points, webpack will get them both and combine into a single file (in order which we tell it to); here we’re loading Hot Module Replacement functionality, which will allow us to change the code of our application and see the changes in browser without reloading the window
  • plugins — we also need to load the HMR plugin

Serving the application (for development needs!)

We have a couple of ways to make it easier — we can get webpack-dev-server which will once ran from the console monitor our source, bundle in memory and expose the application from a built in webserver. Or we can implement express to make a simple http server. Let’s take a look:

var webpack = require('webpack');
var config = require('./webpack/webpack.config.developement.js');
var compiler = webpack(config);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}));
app.use(require("webpack-hot-middleware")(compiler));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'src', 'public', 'index.ejs'));
});

Adding scripts to npm

We now have our simple boilerplate set up, let’s add a few scripts to our package.json file so we can run them with npm run:

"start": "node server.js", 
"test": "jest",
"lint": "eslint src",
"build": "NODE_ENV=production webpack --config webpack/webpack.config.production.js --progress --hide-modules -p"
  • start — this will start our development server we just finished
  • test / lint — this will run both unit testing and linting on our code, it’s a good practice to have them running in watch mode in the background as we code, but if that’s too much, run them and fix any errors before you commit!
  • build — this will build a production-ready bundle and place it in our /dist directory

On the topic of production-build

A topic that comes up often is “what server do I need for my React application”. And the not-so-obvious answers is: any. As long as you’re not running a universal / isomorphic / server-side-rendering application you can use any server you like. Once bundled, React application can be as small as one .js file, that can be sent to the browser from any server.

Where are the configuration files?

You might have noticed that the boilerplate is missing some files — .eslintrc. Are we OK with using the default settings? No — we are using it all in package.json — Dan Abramov recently tweeted, and I agree with him:

In conclusion

I hope this article made you realize the inner-workings of your boilerplate a bit more and maybe will encourage you to experiment and try to implement a boilerplate that’s suited just for you.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store