Lets get a look at a “minimal” Redux boilerplate package.json file and count the dependencies.
// Intentionally showing only dependencies
"dependencies": {
"react": "^15.4.2",
"react-bootstrap": "^0.30.7",
"react-dom": "^15.4.2",
"react-redux": "^5.0.2",
"react-router": "^3.0.1",
"react-router-bootstrap": "^0.23.1",
"react-router-redux": "^4.0.7",
"redux": "^3.6.0",
"redux-form": "^6.4.3",
"redux-saga": "^0.14.3"
},
"devDependencies": {
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-polyfill": "^6.20.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-3": "^6.17.0",
"babel-runtime": "^6.20.0",
"clean-webpack-plugin": "^0.1.15",
"css-loader": "^0.26.1",
"enzyme": "^2.7.0",
"extract-text-webpack-plugin": "^1.0.1",
"ignore-styles": "^5.0.1",
"mocha": "^3.2.0",
"node-sass": "^4.3.0",
"react-addons-test-utils": "^15.4.2",
"react-hot-loader": "^1.3.1",
"redux-freeze": "^0.1.5",
"sass-loader": "^4.1.1",
"style-loader": "^0.13.1",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2",
"whatwg-fetch": "^2.0.1"
}
}
There are 32 (thirty two!) dependencies! Is this boilerplate really minimal? Of course not!
This is the minimal one with only 5 dependencies:
{
"name": "Mycolaos",
"version": "1.0.0",
"scripts": {
"build": "webpack src/index.js static/bundle.js"
},
"dependencies": {
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.5",
"redux": "^3.7.2"
},
"devDependencies": {
"webpack": "^3.3.0"
}
}
Will those dependencies be enough for you? Most likely not, you’ll need more of them.
Why don’t just use create-react-app? Why bother with building your own project? To have control and to understand
how things work. Create-react-app is a great tool, and it does really simplify our life.
But if you’re reading this post, you probably want to build things yourself.
So, what is the key to a fearless project startup? It’s simple — just layout what you really want!
Let’s list what a modern developer might need for a React-Redux project:
- Modules support
- ES6 and JSX support
- CSS modules and preprocessors
- Linting
- Hot reloading
- Test suites
- Packages specific to a project
Looks much clearer than a bloated package.json, right? It’s the first step.
Now, as we’ve defined what we want, it’s much simpler to create our project. We’re just going to add step by step our tools.
Actually, with this list you could leave this post and go ahead to do it yourself. Just remember that you’re using webpack, so most of the packages you’ll be using have their specific webpack loaders.
Besides, some of the tools have their own configs, so it’s worth to have a little more thorough look on how these tools work.
I’ll show the steps to add all the tools listed above. But, first lets define your starting file structure.
/src
index.js
/dist
index.html
# bundle.js will be created automatically when you run `npm run build`
package.json
Your index.html should load bundle.js.
<!doctype html>
<html>
<head>
<title>Fearless</title>
</head>
<body>
<div id='root'></div>
<script src='bundle.js'></script>
</body>
</html>
Copy-paste into package.json
the “true minimal” example.
Then open terminal inside that directory and run:
npm install |
Now you can put some code into your index.js
and try to run
npm run build |
If everything is working, lets start!
Modules support
Done with Webpack! 🙂
Actually, webpack is much more powerful tool. Our further tools will make use of its loaders system. I recommend to read webpack’s Concepts page, – it’s really short overview, just for having an idea about how it works and what it offers.
Then, you need to create webpack.config.js
which is much more convenient to use than cli. Here’s a short instruction you can use — using a configuration. I’ve pointed to the piece you actually need, but if you’re not familiar with webpack, read the whole page, it’s also short, even if doesn’t seem — near half of the page’s height is occupied by code examples.
Once created the webpack config file, you can also change our
"build": "webpack src/index.js static/bundle.js" |
command inside package.json
to
"build": "webpack" |
ES6 and JSX support
Well, probably, you already know that there’s Babel compiler for this. You just go to its official site babeljs.io/docs/setup/ directory, click on webpack and follow the guide, it’s extremly simple! Note that in the third step “Via config” of the guide means the webpack’s config file.
…
If you’ve followed the guide, you’ve seen that to make babel work properly you’ll need plugins. With the guide you add env
preset, which is ok. We need react support too, in the plugin page you’ll see that a “react” preset exists, so you just need to add that the same way you’ve done with env
preset.
Also, there are some more cool ES proposals like “object rest spread” or “transform class properties” and others. They are available in the stage-x
presets or individually. Personally, I don’t bother and just add the state-0
preset and enable all the coolest features of modern JavaScript.
There’s also a not obvious detail. Babel makes available the use of modules, but we’re already using Webpack for this purpose and it uses “imports” and “exports” to do a “tree shaking” which allows it to eliminate the dead code. You just need to disable modules support in your .babelrc
:
{
"presets": [
["env", {"modules": false}],
"react",
"stage-0"
]
}
CSS modules and preprocessors
Okay, we want modules. We want to import our .scss
code in our components modules and we want to create a css bundle.
It’s what we are using webpack for, but if you’ve read the webpack introduction, you could have notice that it understands only javascript. You need css-loader
to translate it properly.
We also want to use SCSS syntax, it should be transpiled to normal .css
. As you can probably guess, we will use a loader for it.
Lets handle first the modules. Fortunately, webpack has a really great documentation, look at Loading CSS. Just follow the guide.
…
As you’ve seen, the style-loader
adds your styles inline, but at the end of the guide there’s a link for splitting css bundle, and preprocessor loaders are also mentioned, we are interested in sass.
The splitting is dead simple, just remember to update your index.html
to link your styles.css
. Adding the sass-loader
is simple too. I use also the postcss-loader
and its autoprefixer
so I don’t worry about adding browser specific prefixes.
…
It was simple, nah?
I guess, currently your structure and your files should look closely like this:
/dist
bundle.js
index.html
styles.css
/node_modules
/src
index.js
styles.scss
.babelrc
.postcssrc
package-lock.json
package.json
webpack.config.js
{
"name": "Mycolaos",
"version": "1.0.0",
"scripts": {
"build": "webpack"
},
"dependencies": {
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.5",
"redux": "^3.7.2"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"css-loader": "^0.28.6",
"extract-text-webpack-plugin": "^3.0.0",
"node-sass": "^4.5.3",
"postcss-loader": "^2.0.6",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "^3.5.5"
}
}
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" },
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader", "postcss-loader", "sass-loader"]
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css")
]
};
{
"presets": [
["env", {"modules": false}],
"react",
"stage-0"
]
}
{
"plugins": {
"autoprefixer": {}
}
}
Linting
I use eslint for this. There’s a loader for webpack you can find in loaders page. If you click on eslint-loader
you’ll find a pretty straightforward guide to add it.
…
On the official site of ESLint you’ll find a short getting started guide and a far more extensive advanced configuration. In case you’ve never used ESLint previously, I’ll show you a simple example of .eslintrc
config file.
{
"extends": ["eslint:recommended", "plugin:react/recommended"],
"parser": "babel-eslint",
"env": {
"browser": true,
"node": true
},
"plugins": [
"react"
],
"rules": {
"no-debugger": 0,
"no-console": 0,
"new-cap": 0,
"strict": 0,
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"eol-last": 0,
"quotes": [2, "single"],
"jsx-quotes": [1, "prefer-single"]
}
}
You could notice that there’s a plugin react
and a parser babel-eslint
. We need them, so ESLint can understand our JSX and ES6 code. There’s nothing special about it, you can just install them
npm install --save-dev eslint-plugin-react |
npm install --save-dev babel-eslint |
a little more detail about: react plugin and babel parser.
Now, if you run webpack, you’ll be cursing the double quotes! 😀
Hot Reloading
It’s a great feature for a development process and you’ll have no need to use watch
. The guide is very simple. You’ll see that you need to install webpack-dev-server
first, so you can just install it before proceed npm install webpack-dev-server --save-dev
and add a script for running it to your package.json
:
"start": "webpack-dev-server" |
Here’s the guide.
…
Once done, you may want to install React Hot Loader. Here’s the guide to do that, but to simplify I’ll layout the steps for you:
- Install
npm install --save react-hot-loader@next
- Add it to your webpack entries list:
entry: ['react-hot-loader/patch','./src/index.dev.js'],
- Include this in the
.babelrc
config.
"plugins": ["react-hot-loader/babel"],
- Create a dev entry file, that will replace your root component. Comment
store
lines, if you haven’t set up the redux store.
import { AppContainer } from 'react-hot-loader';
import React from 'react';
import ReactDOM from 'react-dom';
import Root from './Root.dev';
import configureStore from './store/configureStore.dev'
const store = configureStore()
const rootEl = document.getElementById('root');
const render = Component =>
ReactDOM.render(
<AppContainer>
<Component store={store} />
</AppContainer>,
rootEl
);
render(Root)
if (module.hot) module.hot.accept('./Root.dev', () => {
render(Root)
});
Now, if you change your components their state will be preserved.
Test suites
You can refer directly to your prefered test suite docs, I guess that it should be documented enough. I’ll show an example how to use Tape for unit testing. It’s a little tricky.
- Install Tape.
npm install --save-dev tape
- Install faucet for a prettier log.
npm install --save-dev faucet
- Install chokidar as a better replacement for watch.
npm isntall --save-dev chokidar-cli
- Install babel-register in order to transpile ES6.
npm install --save-dev babel-register
Tape can require it before running the spec files.
- Install a plugin to ignore css requires.
npm install --save-dev babel-plugin-transform-require-ignore
- Add those scripts to
package.json
"test": "BABEL_ENV=test tape -r babel-register src/**/spec.*.js | faucet",
"watch": "chokidar 'src/**/*' -c 'clear && npm test'" - Update
.babelrc
as babel-register will using that and it needs modules support.
{
"presets": [
["env", { "modules": false }],
"react",
"stage-0"
],
"plugins": [
"react-hot-loader/babel"
],
"env": {
"test": {
"presets": [
"env",
"react",
"stage-0"
],
"plugins": [
["babel-plugin-transform-require-ignore",
{
"extensions": [
".css",
".scss"
]
}
]
]
}
}
}
It was just an example, but if you’ve followed it and want to give tape
a try, here its docs, very short and simple.
Packages specific to a project
We have covered the most generic tools a developer might use in his project, but obviously there will always be some tools more specific to your needs. Even if you use ready-made solutions, boilerplates — you’ll be facing it. For example, I use html-webpack-plugin
that allows me to create templates for webpack, so I control what kind of html I have in output. Try it!
Sure, the whole process of structuring your project is not really that simple, it’s tricky, and there are always little details you could omit or find out later. Fortunately, we have a big enough community to find answers to almost all the issues we encounter.
I hope this little hands-on guide made you a little more confident on starting your project by yourself.
Here’s the whole example code.