Boilerplate with all the good stuff but without the JavaScript fatigue. Made by Dillon de Voor, follow me for updates and more!
Includes Koa 2, React 15, Webpack 2 and React Hot Loader 3. See section “Feature rich” for what other awesome features you can expect.
This is v2 of this boilerplate. See the v1 branch for the previous version.
Because this boilerplate is different than most boilerplates out there. Most boilerplates will just give you a rather complex starting point but not how they got to that starting point. Ever heard ot JavaScript fatigue? These boilerplates are not helping.
This boilerplate attempts to solve this by providing a series of logical steps to build your own boilerplate. Of course you’re free to just use this one too but I encourage you to at least take a look at each step to get a basic understanding of each part of the app.
The steps are in the form of commits and I really hope this makes React and all the tooling around React more comprehensible, even for beginners. If you like it, please spread the word!
Despite my aim to make this the easiest understandable boilerplate you will find lots of features useful for development and for production. The finished boilerplate will include the following:
- Universal or isomorphic. However you’re calling it... “Server-side rendering is not a fallback; client-side rendering is an enhancement” (source).
- React for obvious reasons, React Router to deal with routing and React Helmet to manage changes in the document head.
- Hot Module Replacement using React Hot Loader 3.
- Redux to keep application state predictable and Redux DevTools to help with debugging.
- Server using Koa, the next generation web framework developed by the team behind Express.
- Babel so we can use the latest ECMAScript additions and even the ones that have not made it to the standard yet, use with caution.
- Client bundle created by Webpack 2, which includes tree-shaking.
- Mocha and Enzyme to run your tests so your app (and you) stay healthy.
- It’s your code style, but ESLint helps keep it consistent.
- Static type checking using Flow can help you find errors quickly.
- Development and production build processes
git clone https://github.com/CrocoDillon/universal-react-redux-boilerplate.git
cd universal-react-redux-boilerplate
npm install
npm run dev
npm run build
npm start
Run ESLint, Mocha and Flow:
npm run check
Or separately:
npm run lint
npm run test
npm run flow
Or even lint JS and CSS separately:
npm run lint:js
npm run lint:css
Here is a summary of each step, where each step also represents one commit. Each step is like a milestone that brings us closer to the finished boilerplate. Feel free to modify to your needs!
You might want to use ECMAScript 6 (and beyond) features like modules, at the very least you will want to use JSX (React just isn’t the same without it). To be able to do that we will transpile our code using Babel. In addition to Babel we will use Webpack to create a bundle for the client.
Because the options passed to Babel need to be different for the server and the client, we won’t include a .babelrc
options file. Instead, we pass the options to babel-register
for the server and in the Webpack config for the client. This might lead to some duplication but makes the separation more verbose.
To make use of a Webpack 2 feature called tree-shaking, we have to use the Babel preset es2015-webpack
so Babel doesn’t transform ES6 modules to CommonJS.
Oh and do yourself a favor, make it a habit to always specify displayName
and propTypes
(if applicable) to React components.
Before starting your project it’s probably a good idea to set up some rules about code style. ESLint is awesome to enforce these rules because it’s pluggable and fully configurable. Linting is not about “best practices” and other people’s opinions, it’s about code style consistency, maintainability and preventing errors. The rules are up to you or your team, ESLint will do the rest.
The .eslintrc.js
file in this boilerplate is absolutely huge but don’t be intimidated by it. We could go the easy way and extend eslint:recommended
or for example eslint-config-airbnb
but remember, it’s about your rules. I included every single standard ESLint rule and every single eslint-plugin-react
rule whether they are enabled or not. That way all the rules are in one place which makes it easier to make them fit your needs.
It’s also a good idea to start testing early. We use Mocha and Enzyme (which provides some nice testing utilities for React), along with Chai as assertion library. Even though we are not using it yet I also added Sinon to create spies, stubs and mocks and some Chai plugins to make assertions more expressive.
Files containing tests are named *.spec.js
and kept as close to the module they are testing as possible. If there are more than a few test files in one directory it can still be a good idea to put them in a subdirectory to keep things clean.
The last step in ensuring code quality is static type checking with Flow. This might require some annotations in your code which will be stripped out by a Babel plugin included in the React preset. Not everyone will be comfortable with that so let’s call this step optional.
Now is a good time to add continuous integration which will run all our checks whenever we push to GitHub. And the badges... they are awesome!
Restarting the server and rebuilding the bundle every time you make a code change is kinda boring, we need something better. Using Webpack’s dev middleware we can automatically update the bundle on code changes. The bundle is served from memory and rebuilds are much faster. And it gets even better, using Webpack’s hot middleware together with React Hot Loader we can apply the updates to a running React app, no need for a page refresh. It is awesome! Hot reloading on the server is done by plugging into the Webpack compiler and trashing the require cache when Webpack detects changes in the app source.
This is a little bit tricky to set up, but bear with me. The goal is to have Sass, CSS Modules and Autoprefixer working together with server-side rendering, Webpack, hot loading, sourcemaps and of course linting.
Most of the work is already done by adding some additional loaders to Webpack. Webpack knows how to import (or require) an asset other than JavaScript as it passes it trough a series of loaders specified in the config file. Webpack makes those imports work for the client, but for the server we need something else. This is where webpack-isomorphic-tools
come in handy. This module keeps track of what is generated by Webpack (and its loaders) and makes importing these assets work on the server as well.
Setting up webpack-isomorphic-tools
is not trivial, but it’s worth it.
In production we want to get rid of the overhead of transpiling by pre-building server and client code. We also want hashed assets to make use of far-future caching. These asserts can be deployed to a CDN, by changing the publicPath
in webpack.config.js
to point to the CDN the paths are updated automatically.
While not your only choice, React Router definitely is the most popular choice when it comes to routing. It supports server-side routing and it supports browser History API on the client.
What React Router doesn’t solve out of the box is handling of meta data like title or status. For title (and other <head>
related meta data) there is React Helmet and for HTTP status when rendering on the server we use a small helper utility. Both are used React component’s render functions to support dynamically changing them, for example when a blog article is not found.
Sooner or later you might need some more advanced state management than React gives you with component state. Redux is a good choice. Since we’re building a blog here it makes sense to keep loaded articles in this state. Redux features a store, reducers and actions, which are often grouped together. For this boilerplate I want to try a different approach and instead of grouping by nature we will group by domain. If you want to know more about this approach I encourage you to read “Rules For Structuring (Redux) Applications” and “A Better File Structure For React/Redux Applications”.