Opinionated quickstart Create React App (CRA) template with Redux, React Testing Library, eslint and stylelint configurations.
See full documentation.
npx create-react-app %PROJECT_NAME% --template quickstart-redux
Or
yarn create react-app %PROJECT_NAME% --template quickstart-redux
npx
command installs most recent stable version of CRA from npm. --template
parameter points to this template, note that cra-template-
prefix is omitted.
Then
cd %PROJECT_NAME%
yarn start
I use Create React App pretty much often. But I hate to write same boilerplate code to make Redux working and configure some other useful stuff again and again. This template contains test and eslint configurations and Redux boilerplate code, required for rapid start of your fabulous project.
Due to CRA template limitations (we can change only scripts
and dependencies
inside generated package.json
) all configuration is done by adding config files where possible.
Template provides basic Redux configuration with feature based folder structure. You can use Redux devtools browser extension. Template has examples of sync (src/features/counter
) and async (src/features/random
) Redux features and tests for them.
Git hooks management is provided by husky and lint-staged.
Thus every time you commit something husky
will run eslint --fix
command on staged files, preventing you from committing badly formatted code. You can change or disable this behavior inside .linstagedrc
config file. Before each push tests will run in the same manner.
-
If pre-commit hooks not work (e. g. your code is not linted after commit), run
yarn prepare
in your project folder. -
You need to update snapshots and fix failing tests to be able to commit or push your code.
Snapshot testing done with react-testing-library. Example tests are included. Redux connected components are tested with redux-mock-store and axios-mock-adapter.
Code quality tools provide static check of your code and try to fix errors. Checks are triggered inside pre-commit hook. To run them manually:
yarn lint:js # runs eslint in src directory
yarn fix:js # runs eslint in src directory with --fix parameter
yarn lint:style # runs stylelint in src directory
yarn fix:style # runs stylelint in src directory with --fix parameter
Template extends CRA eslint rules with custom set, tailored for reasonable and clean development process. I added prettier
to force consistent formatting and eslint-plugin-fp
to avoid accidental mutations. Don't like trailing semicolons? Feel free to tweak prettier rules inside .prettierrc
file to match your code style.
Eslint rules are commented for your convenience, feel free to tweak or remove them. No judgement.
// Allow jsx tags inside .js files.
"react/jsx-filename-extension": [1, {"extensions": [".js", ".jsx"]}],
// Disable props spreading (<App {...props} />) warning.
"react/jsx-props-no-spreading": 0,
// Throw warning instead of error when using array index as a key.
"react/no-array-index-key": 1,
// Allow modules with named exports only.
"import/prefer-default-export": 0,
// Force {foo: 'bar'} object literal syntax.
"object-curly-spacing": ["error", "never"],
// Throw warning instead of error when function is not properly formatted.
// Feel free to choose your favorite option https://eslint.org/docs/rules/arrow-body-style
"arrow-body-style": ["warn", "as-needed"],
// Make prettier code formatting suggestions more verbose.
"prettier/prettier": ["warn"],
// Throw warning when <a href="#"> or <a href="javascript:void(0)"> are used.
// Use <button> instead.
"jsx-a11y/anchor-is-valid": ["warn", {"aspects": ["invalidHref"]}],
// Allow using (props) => <Component /> and ({propName}) => <Component /> syntax.
"react/destructuring-assignment": "off",
// Disable <Fragment> => <> replacement. Feel free to change
"react/jsx-fragments": "off",
// Below is the set of functional rules to warn developer about accidental mutations,
// which may cause error in reducers etc.
// No delete operator.
"fp/no-delete": "warn",
// Warning when Object.assign(a, b) used, since it mutates first argument.
// Object.assign({}, a, b) is ok.
"fp/no-mutating-assign": "warn",
// Warning when mutating method (pop, push, reverse, shift, sort, splice, unshift, etc)
// is used. Ramda and lodash/fp are allowed (_.pop, R.push)
"fp/no-mutating-methods": [
"warn",
{
"allowedObjects": ["_", "R"]
}
],
// Warning when mutating operators (++, --, etc) are used, object = {} also.
// `Component.propTypes`, `Component.defaultProps`, common.js (`module.exports`)
// and `ref.current` are ok.
"fp/no-mutation": [
"warn",
{
"commonjs": true,
"allowThis": true,
"exceptions": [{"property": "propTypes"}, {"property": "defaultProps"}, {"property": "current"}]
}
]
Template includes stylelint, to check CSS/SASS/LESS files. We are using stylelint-config-standard
rule set extended with:
// Check `calc` functions formatting, required for `calc` to work in IE11
"function-calc-no-unspaced-operator": true,
// Custom rules (aka CSS vars) should go first
"order/order": [
"custom-properties",
"declarations"
],
// Require rules to be in alphabetical order
"order/properties-alphabetical-order": true,
// Disallow vendor prefixes, since CRA has autoprefixer enabled
"property-no-vendor-prefix": true,
"media-feature-name-no-vendor-prefix": true,
"at-rule-no-vendor-prefix": true,
"selector-no-vendor-prefix": true,
// Limit rules nesting for readablity purposes
"max-nesting-depth": 3,
// Limit selector complexity for readablity purposes
"selector-max-compound-selectors": 5
Stylelint errors don't prevent build of application in development mode.
Template uses vanilla CSS with autoprefixer
enabled. To avoid classname collisions and reduce nesting we are using css-modules
. To make css-modules work, stylesheet file name should have .module
suffix.
import React from 'react';
import classes from './Component.module.css';
const Component = () => (
<div className={classes.wrapper}>Component</div>
)
CRA doesn't support style pre-processors except SASS. But this doesn't mean, that we shouldn't use them. In order to add support for custom style processor without ejecting, we can use file watchers. File watchers will track changes in style files and compile them to vanilla CSS, consumed by CRA.
SASS/SCSS support comes "out of the box" in CRA. To enable it:
-
Install
node-sass
yarn add node-sass --dev
-
Import SASS/SCSS files straight into Component.
import React from 'react'; import classes from './Component.module.scss'; // note the changed extension const Component = () => ( <div className={classes.wrapper}>Component</div> )
-
Change
.lintstagedrc
to lintscss
files instead ofcss
.{ "*.js": [ "eslint --fix" ], "*.scss": [ "stylelint --fix" ] }
You can see all changes required to enable SASS/SCSS in corresponding PR.
-
Install
postcss-cli
and related plugins:yarn add --dev postcss-nested postcss-cli postcss-preset-env npm-run-all
-
Modify package scripts:
{ "build:style": "postcss src/**/*.pcss --dir src --base src --ext css", "watch:style": "yarn build:style -w", "start": "npm-run-all -p watch:style start:js", "start:js": "react-scripts start", "build:js": "react-scripts build", "build": "npm-run-all build:style build:js" }
-
Add
postcss.config.js
file in the root folder. With following configuration:const pkg = require('./package.json'); module.exports = { plugins: [ require('postcss-nested'), // handle nested selectors, like LESS or SASS require('postcss-preset-env')({ browsers: pkg.browserslist.production, // use browsers list from production mode stage: 1, }), ], };
-
Add rule to
.gitignore
and.stylelintrc
to ignore all css files, since we are generating them.# css *.css
{ "ignoreFiles": ["**/*.snap", "**/*.css"] }
-
Change
.lintstagedrc
to lintpcss
files instead ofcss
.{ "*.js": [ "eslint --fix" ], "*.pcss": [ "stylelint --fix" ] }
You can see all changes required to enable PostCSS in corresponding PR.
-
Install
less
and related plugins:yarn add --dev less less-watch-compiler npm-run-all
-
Modify package scripts:
{ "build:style": "yarn watch:style --run-once", "watch:style": "less-watch-compiler src src", "start": "npm-run-all -p watch:style start:js", "start:js": "react-scripts start", "build:js": "react-scripts build", "build": "npm-run-all build:style build:js" }
-
Add rule to
.gitignore
and.stylelintrc
to ignore all css files, since we are generating them.# css *.css
{ "ignoreFiles": ["**/*.snap", "**/*.css"] }
-
Change
.lintstagedrc
to lintless
files instead ofcss
.{ "*.js": [ "eslint --fix" ], "*.less": [ "stylelint --fix" ] }
You can see all changes required to enable Less in corresponding PR.
You can use source folder relative paths for imports. import Component from './../../../../../../src/components/Component'
becomes import Component from 'components/Component'
. Configuration is inside jsconfig.json
file. You will love it π!
βββ .env # dotenv config file
βββ .eslintrc # eslint configutation
βββ .lintstagedrc # lintstaged configutation
βββ .nvmrc # required Node version
βββ .prettierrc # prettier configutation
βββ .stylelintrc # stylelint configutation
βββ README.md # this file
βββ README_CRA.md # original Readme from CRA
βββ .gitignore
βββ huskyrc-template # template for husky configuration
βββ jsconfig.json # absolute path configuration
βββ public # public assets
βββ src
Β Β βββ components # React components folder
Β Β βββ config.js # shared config file
Β Β βββ features # features logic folder
Β Β βββ index.css
Β Β βββ index.js # entry point file
Β Β βββ serviceWorker.js # service worker boilerplate
Β Β βββ setupTests.js # configuration ti run jest tests
Β Β βββ withProvider.js # utility to generate Provider components
Β Β βββ withReduxFeatures.js # Redux store HOC
Feature is a minimal working set of code capable of performing business logic task (e.g. event tracking, shopping basket etc). From the architectural point of view feature is ES Module. Exports of the module (see index.js
in each folder) define feature's public interface.
Features live in the src/features
folder.
All possible exports from the feature module can be divided in two groups:
- Selectors return data from the feature state or external source.
- Action creators mutate data inside feature state or perform externally applied actions (ajax requests, websocket messages etc).
Both groups are implemented as React hooks.
- Each folder has
index.js
file which contains allpublic
methods of this feature. Please do not import directly from feature files. They considered to beprivate
methods. - In order to avoid circular dependencies don't use own
index.js
defined exports inside module, use relative imports instead. - Features may depend on other features existing using public interface.
- Features' hooks may and should be used in React components. Please avoid making React components too smart in terms of business logic. Use features instead.
- Some features may require internal state. State management is provided by Redux.
- Though Redux state is not mandatory for the feature. Some features may only depend on external sources (ajax endpoints, URL parameters etc).