A simple but feature rich starter boilerplate for you to build an universal web app with the best development experience and a focus on performance and best practices.
Built on the top of Node.js, Express, React, Redux and React Router. Includes all the hot stuff and modern web development tools such as Webpack 2, Babel, PostCSS, Immutable-js, React Hot Loader 3 and Redux Devtools Extension. See section βFeaturesβ for more other awesome features you can expect.
I will improve the starter boilerplate continuously and keep all of the technologies on trend. Welcome to join me if you want. Hope you guys love it :)
Really cool starter boilerplate with the most popular technologies:
- Universal rendering.
- React as the view.
- React Router as the router.
- react-helmet to manage title, meta, link, script and base tags on both server and client.
- Express server.
- Babel for ES6 and ES7 transpiling.
- morgan the HTTP request logger for server side debugging.
- Webpack 2 for bundling and "Tree-Shaking" support.
- Webpack Dev Middleware serves the files emitted from webpack over the Express server.
- Webpack Hot Middleware allows you to add hot reloading into the Express server.
- webpack-isomorphic-tools to allow require() work for statics both on client and server.
- Redux's futuristic Flux implementation.
- redux-thunk as the middleware to deal with asynchronous action.
- Redux Devtools Extension for next generation developer experience.
- react-router-redux to keep your router in sync with Redux state.
- React Hot Loader 3 tweaks React component/store in real time.
- nodemon to reload non-boundled files (e.g.
./src/server.js
,./tools/*.js
). - Immutable-js provides persistent data collections which increase efficiency and simplicity.
- react-addons-shallow-compare for a performance boost, it works perfectly with immutable data structure.
- axios for universal data fetching/rehydration on the client.
- ESLint to maintain a consistent javascript code style (Airbnb's code style).
- StyleLint to maintain a consistent css/scss code style.
- CSS and SASS support with PostCSS for advanced transformations (e.g. autoprefixer). CSS Modules enabled.
- Image (with image-webpack-loader for optimizing) and Font support.
- Split vendor's libraries from client bundle.
- No other view engines, just javascript based HTML rendering template.
- Shared app config between development and production.
- 404 error page and redirect handling.
- karma, mocha, enzyme, chai and sinon as the integrated solution for wrting unit tests.
1. You can start by clone the repository on your local machine by running:
git clone https://github.com/wellyshen/react-cool-starter.git
cd react-cool-starter
2. Install all of the npm packages:
npm install
3. Start to run it:
npm run start:production # Building bundle and running production server
Now the app should be running at http://localhost:8080/
I use better-npm-run to manage the scripts in a better way, which also provides the compatibility of corss-platform. All of the scripts are listed as following:
npm run <script> |
Description |
---|---|
start |
Run your app on the development server at localhost:3000 . HMR will be enabled. |
start:production |
Compiles the app to ./public/dist and run it on the production server at localhost:8080 . |
start:prod |
Run your app on the production server only at localhost:8080 . |
build |
Clean the compiled stuff and compile your app to ./public/dist . |
build:clean |
Remove the dist folder from ./public to clean the compiled stuff. |
lint |
Lint all .js and .scss files. |
lint:js |
Lint all .js files. |
lint:style |
Lint all .scss files. |
test |
Run testing once. |
test:watch |
Run testing on every test file change. |
Note: If you get the the following message, try to run npm run build
to fix it.
webpack-isomorphic-tools (waiting for the first webpack build to finish)
Here is the structure of the app, which serve as generally accepted guidelines and patterns for building scalable apps.
.
βββ public # The root path of static file
β βββ dist # All the built files will be placed into it
β βββ favicon.ico # Favicon is placed in the same path with the main HTML page
βββ src # App source code
β βββ __tests__ # Collections of testing files
β βββ actions # Collections of actions
β βββ config # App configuration settings
β β βββ default.js # Default settings
β β βββ index.js # Configuration entry point
β β βββ prod.js # Production settings (overrides default settings)
β βββ containers # Reusable container components
β βββ reducers # Collections of reducers (registry and injection)
β βββ theme # App-wide style, vendor style, generally settings
β βββ client.js # App bootstrap and rendering (webpack entry)
β βββ configureStore.js # Configure and instrument redux store
β βββ renderHtmlPage.js # Main HTML page layout for app
β βββ routes.js # Routes shared between client and server side
β βββ server.js # Express app (uses webpack middleware)
βββ tools # Project related configurations (testing/build etc.)
β βββ testing # Testing configuration settings
β β βββ karma.conf.js # Karma configuration file
β β βββ test-bunlder.js # Karma pre-processor settings file
β βββ webpack # Webpack configuration settings
β β βββ config.js # Webpack configuration file
β β βββ config.test.js # Webpack configuration file for testing (for karma config)
β β βββ index.js # Webpack configuration entry point
β β βββ webpack-isomorphic-tools.config.js # Webpack Isomorphic Tools configuration file
β βββ es2015Preset.js # es2015 preset configuration file (for .babelrc)
βββ index.js # App start point
The Redux Devtools Extension let us wire up our Redux app to a time-traveling debugger. It's enabled in development only. You can follow the installation guide to use it:
For Chrome
- from Chrome Web Store
- or build it with
npm i && npm run build:extension
and load the extension's folder./build/extension
- or run it in dev mode with npm i && npm start and load the extension's folder
./dev
.
For Firefox
- from AMO
- or build it with
npm i && npm run build:firefox
and load the extension's folder./build/firefox
(just select a file from inside the dir).
For Electron
- just specify REDUX_DEVTOOLS in electron-devtools-installer.
For other browsers and non-browser environment
React 0.14 introduced a simpler way to define components called stateless functional components. These components are written in plain JavaScript functions. In the starter boilerplate we use it wherever possible.
Add your routes in ./src/routes.js
. For example:
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="NewRoute" component={NewRoute} /> // Adding a new route
<Route path="*" component={NotFound} />
</Route>
The parent App.js
defines the base title and meta in a <Helmet {...config.app} />
component. Any sub-component can override/add properties (supports base, meta, link, script, style tags and html attributes). See the react-helmet documents for more info.
You can store app settings under ./src/config
. By default the default.js
will be loaded. If the process.env.NODE_ENV
matches to production, the prod.js
will be used insteadlly, and it inherits the data info from default.js
.
You can access the correct config with:
import config from './config';
The starter boilerplate supports CSS, SASS and CSS Modules is enabled by default. We use PostCSS plugin to parse CSS and add autoprefixer to your stylesheet. You can access your stylesheet with two ways.
With CSS Modules:
import styles from './Home.scss';
...
render() {
return (
<div className={styles.Home}> // The className matches one of CSS classes in your SCSS file
<Helmet title="Home" />
{this.displayUserList()}
</div>
);
}
Without CSS Modules (you need to turn off CSS Modules from ./tools/webpack/index.js
):
import './Home.scss';
...
render() {
return (
<div className="Home">
<Helmet title="Home" /> // Use the CSS class as normal
{this.displayUserList()}
</div>
);
}
It's super easy to render the image and font both on client and server, the usage would be like below.
Using image:
// Require an image
<img src={require('./logo.svg')} alt="Logo" role="presentation" />
Using font-awesome:
// With CSS Modules
import styles from './myStyle.scss';
...
return (
<div>
<div><i className={styles.iconUser}></i> Welly</div>
</div>
);
// Without CSS Modules
import './font-awesome.css';
...
return (
<div>
<div><i className="fa fa-user"></i> Welly</div>
</div>
);
For using CSS Modules, you have to set the proper font path in your scss file:
$fa-font-path:"../node_modules/font-awesome/fonts";
@import "../node_modules/font-awesome/scss/font-awesome";
.icon-user {
@extend .fa;
@extend .fa-user;
}
Just write Redux actions and stores as normal (read the Redux guide if you are new). The starter boilerplate using axios as the data fetcher, it's quite simple and easy to use. If the action creator is asynchronous then it will return a Promise (or a Promise.all) in the inner function.
You can write dispatches for actions that must be called for the container to be ready:
// Write a static function which be called by server and client
static fetchData = (dispatch, params) => Promise.all([
// Add the asynchronous actions which must be called while paga loading here
dispatch(fetchAnUser.fetchAnUserIfNeeded(params.id)),
]);
Then invoke the actions in componentDidMount
. This ensures that if the component is reached on the client, then the same actions will be invoked. It's up to the action to figure out if fetches for data need to be made or not:
componentDidMount() {
const { dispatch, params } = this.props;
// Invoke the action for client rendering
UserInfo.fetchData(dispatch, params);
}
If your React component's render function is "pure" (in other words, it renders the same result given the same props and state), you can use shallowCompare with shouldComponentUpdate
for preventing it from re-render.
Luckily, we writing our stores using Immutable-js, the immutable data structures provides you a cheap and less verbose way to track changes on objects, which is all we need to implement shouldComponentUpdate
. See the React Advanced Performance topic for more info.
How Shallow Compare is practiced:
import shallowCompare from 'react-addons-shallow-compare';
...
class Home extends Component {
...
shouldComponentUpdate(nextProps, nextState) {
// Implement the Shallow Compare helper function
return shallowCompare(this, nextProps, nextState);
}
...
}
The starter boilerplate uses mocha to run your unit tests, it uses karma as the test runner, and uses enzyme as the testing utility for React, which makes it easier to assert, manipulate, and traverse your React Components' output. Moreover it also uses chai as the assertion library and uses sinon to provide the standalone test spies, stubs and mocks. The unit tests focus on four parts as below:
- Actions
- Containers
- Components
- Reducers
Warning: [react-router] You cannot change ; it will be ignored
You will see the error message above whenever the hot reload is triggered. This is because the React Hot Loader 3 will re-render the routes dynamically for view updates, but React Router doesn't support that yet (see this issue react-router#2704). I'll wait for the official fix, and not mind the error message ;)
There're some features I'd like to include in the starter boilerplate in the near future. If you have any great ideas or suggestions, feel free to fork the repository and share it.
- Testing code coverage
- Dynamic Routing
- Internationalization