Skip to content

MortezaT/react-cool-starter

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

React Cool Starter ๐Ÿ˜Ž

Build Status dependencies Status devDependencies Status Coverage Status code style: prettier MIT licensed Twitter URL

๐ŸŽ‰ My react-native app LeadFit now available on App Store, which is a workout app. Welcome to try, it's free!

A simple but feature rich starter boilerplate for creating your own universal app. It built on the top of Node.js, Express, React, Redux and React Router v4. Includes all the hot stuff and modern web development tools such as Webpack 4, Babel, PostCSS, React Hot Loader 4, Jest, Flow and Redux Devtools Extension. See the โ€œFeaturesโ€ section for other awesome features you can expect.

I will maintain the starter boilerplate and keep all of the technologies on trend. Welcome to join me if you want. Hope you guys love it ๐Ÿคฉ

๐Ÿ‘ป I'm curious what kind of app that you guys building via this starter? Please feel free to tell me, let's make some sharing between us.

Real Case Study

  • Rendah Mag is a music magazine which exists to bring exposure to an ever-growing community, focusing on the latest Halftime, Beats & Experimental news & releases. Congrats for the amazing product. ๐ŸŽง ๐ŸŽ‰
  • BECK Friends is an international delivery service, which is built based on this starter boilerplate. Congrats for successful migrating to React. ๐ŸŽ‰
  • DealDrop is the best place to find verified coupon codes, deals, promos and offers for thousands of stores & brands you love. Never pay full price again ๐Ÿค‘

Features

Really cool starter boilerplate with the most popular technologies:

  • Universal rendering with async data fetching.
  • React as the view.
  • React Router v4 as the router.
  • Redux's futuristic Flux implementation.
  • Express server.
  • Webpack 4 for bundling and "Tree-Shaking" support.
  • Babel for ES6 and ES7 transpiling.
  • React Hot Loader 4 to tweak React components in real time.
  • nodemon to monitor for any changes in your node.js application and automatically restart the server.
  • axios for universal data fetching/rehydration on the client.
  • redux-thunk as the middleware to deal with asynchronous action.
  • react-router-redux to keep your router in sync with Redux state.
  • react-helmet to manage title, meta, styles and scripts tags on both server and client.
  • loadable-components makes React code splitting easy. Reduce your bundle size without stress.
  • 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.
  • css-modules-require-hook compiles CSS Modules in runtime for SSR.
  • asset-require-hook allows your assets files required during runtime for SSR.
  • assets-webpack-plugin generates assets with hash so you can use them for SSR.
  • Webpack Bundle Analyzer creates a visualize size of webpack output files with an interactive zoomable treemap.
  • morgan the HTTP request logger for server side debugging.
  • Redux Devtools Extension for next generation developer experience.
  • Flow as the static type checker for javascript.
  • ESLint to maintain a consistent javascript code style (With Airbnb configuration).
  • StyleLint to maintain a consistent css/scss code style.
  • Prettier to format javascript and css/scss code.
  • CSS and SASS support with PostCSS for advanced transformations (e.g. autoprefixer, cssnext etc.). CSS modules enabled.
  • Image (with imagemin-webpack-plugin for compressing images with imagemin) and Font support.
  • Split vendor's libraries from client bundle.
  • No other view engines, just javascript based HTML rendering component.
  • Shared app config between development and production.
  • 404 error page and redirect handling.
  • Integrate Jest with enzyme as the solution for writing unit tests with code coverage support.
  • Yarn as the package manager.

Who's the Starter for?

This starter is for those who with basic knowledge of React and have the need for building a server-side app. In other words, it's not for a newbie. If you're new to React or you don't need a server-side rendering app, I'd recommend you give create-react-app a try.

Some Feature Branches

Requirements

Getting Started

1. You can start by cloning 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 dependencies:

yarn

3. Start to run it:

yarn build  # Building bundle
yarn start  # Running production server

Now the app should be running at http://localhost:8080/

Note: You can change the port that you want from ./package.json.

NPM Script Commands

I use better-npm-run to manage the scripts in a better way, which also provides the compatibility of cross-platform. All of the scripts are listed as following:

yarn <script> Description
dev Run your app on the development server at localhost:3000. HMR will be enabled.
start Run your app on the production server only at localhost:8080.
build Remove the previous bundled files and bundle it to ./public/assets.
analyze Visualize the contents of all your bundles.
lint Lint all .js and .scss files.
lint:js Lint all .js files (With --fix to auto fix eslint errors).
lint:style Lint all .scss files (With --fix to auto fix stylelint errors).
flow Run type checking for .js files.
flow:stop Stop type checking.
test Run testing once (with code coverage reports).
test:watch Run testing on every test file change.
test:update-snapshot Update jest snapshot.
clean Remove the client/server bundled stuff and the coverage report.
clean:build Remove the ./public/assets folder to clean the client bundled files.
clean:test Remove the ./coverage folder to clean the code coverage report.

App Structure

Here is the structure of the app, which serves as generally accepted guidelines and patterns for building scalable apps.

.
โ”œโ”€โ”€ public                          # Express server static path/Webpack bundled output
โ”‚   โ””โ”€โ”€ favicon.ico                 # Favicon is placed in the same path with the main HTML page
โ”œโ”€โ”€ src                             # App source code
โ”‚   โ”œโ”€โ”€ config                      # App configuration settings
โ”‚   โ”‚   โ”œโ”€โ”€ default.js              # Default settings
โ”‚   โ”‚   โ”œโ”€โ”€ index.js                # Configuration entry point
โ”‚   โ”‚   โ””โ”€โ”€ prod.js                 # Production settings (overrides the default settings)
โ”‚   โ”œโ”€โ”€ components                  # Reusable components (including scss/testing files)
โ”‚   โ”œโ”€โ”€ pages                       # Page components (including scss/testing files)
โ”‚   โ”œโ”€โ”€ app                         # App root component (including scss/testing files)
โ”‚   โ”œโ”€โ”€ actions                     # Redux actions (including testing files)
โ”‚   โ”œโ”€โ”€ reducers                    # Redux reducers (including testing files)
โ”‚   โ”œโ”€โ”€ utils                       # App-wide utils (e.g. configure Redux store, HTML template etc.)
โ”‚   โ”œโ”€โ”€ theme                       # App-wide style and vendor CSS framework
โ”‚   โ”œโ”€โ”€ types                       # Flow types for reducer, action, state, store
โ”‚   โ”œโ”€โ”€ client.js                   # App bootstrap and rendering (webpack entry)
โ”‚   โ”œโ”€โ”€ routes.js                   # Routes configuration for both client and server side
โ”‚   โ””โ”€โ”€ server.js                   # Express server (with webpack dev/hot middlewares)
โ”œโ”€โ”€ tools                           # Project related configurations (testing/build etc.)
โ”‚   โ”œโ”€โ”€ flow                        # Flow types, interface, module aliasing definitions
โ”‚   โ”œโ”€โ”€ jest                        # Jest CSS modules and assets mocks settings
โ”‚   โ”œโ”€โ”€ webpack                     # Webpack settings
โ”‚   โ”‚   โ”œโ”€โ”€ config.babel.js         # Webpack configuration
โ”‚   โ”‚   โ””โ”€โ”€ hooks.js                # Assets require hooks
โ”œโ”€โ”€ index.js                        # App entry point
โ””โ”€โ”€ postcss.config.js               # PostCSS configuration

Server-Side Security and Performance

Concerning the security and performance of Express in production, I already setup some middleware for you:

  • helmet - Helps secure Express server with various HTTP headers.
  • hpp - Express middleware to protect against HTTP Parameter Pollution attacks.
  • compression - Gzip compression support for speeding up Express server responses.

Note: It's just a basic protected mechanism for your app, you can see the security best practices for more advanced configuration.

Setup Redux DevTools Extension

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 these installation guides to use it:

For Chrome

For Firefox

For Electron

For other browsers and non-browser environment

Overview

Stateless Functional Components

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 I use it wherever possible.

Adding Routes

This starter use React Router v4 library to manage our routes. For the purpose of SSR with data pre-fetched, I put the routes in a centralized Route Config. You can setup your routes in ./src/routes.js. For example:

import RouteComponent from "./pages/RouteComponent";

// ...

export default [
  {
    // Define your route path
    path: "/TopPath",
    // If the route matches the location.pathname exactly or not (used for index route usually)
    exact: true,
    // Add your route component here
    component: RouteComponent,
    // Add your sub route component here
    routes: [
      {
        path: "/TopPath/SubPath",
        component: SubRouteComponent
      }
    ]
    // ...
  }
  // Setup other route components...
];

Data Fetching from Server-side

Just write Redux actions and stores as normal (read the Redux docs if you are new). The starter 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.

Register the action(s) in ./src/routes.js, which have to be called from server-sdie:

// ...

export default [
  {
    path: "/TopPath",
    exact: true,
    component: RouteComponent,
    // Actions in the loadData function will be fetched from server-side
    loadData: () => [
      myReduxAction()
      // Add other pre-fetched actions here
    ]
  }
  // ...
];

The action(s) will be dispatched through ./src/server.js on server-side:

// ...

app.get("*", (req, res) => {
  // ...

  // Here's the method for loading data from server-side
  const loadBranchData = (): Promise<any> => {
    const branch = matchRoutes(routes, req.path);

    const promises = branch.map(({ route, match }) => {
      if (route.loadData) {
        return Promise.all(
          route
            .loadData({ params: match.params, getState: store.getState })
            .map(item => store.dispatch(item))
        );
      }

      return Promise.resolve(null);
    });

    return Promise.all(promises);
  };

  // ...
});

// ...

On client-side, don't forget to invoke the action(s) 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(s) to figure out if fetches for data need to be made or not:

componentDidMount() {
  // Invoke your redux action(s) for client rendering
  this.props.myReduxAction();
}

Code Splitting

One great feature of the web is that you donโ€™t have to make your visitors download the entire app before they can use it. You can think of code splitting as incrementally downloading the app. It divides your code into small pieces called โ€œchunksโ€ to reduce the size of bundle loaded by user. Reducing the size of a chunk makes it load and run faster.

To accomplish this, I integrate loadable-components into this starter. The reason I choose the library is because of its design philosophy of SSR. It works seamless with the starter rather than others. Letโ€™s see how we split our app by route:

I use the following folder/file structure:

 |- pages
    |- AsyncRouteComponent
       |- index.js            // Wrap the route component as async component
       |- RouteComponent.js   // The route component

The index.js will be:

import loadable from "loadable-components";

import { Error, Loading } from "../../components";

export default loadable(
  () => import("./AsyncRouteComponent"), // Import your async route component here
  {
    ErrorComponent: Error, // Error component for displaying error message
    LoadingComponent: Loading // Loading component will be displayed when the component is being loaded
  }
);

Then you can setup the route as usual.

Note: I just show a general case route-based splitting, however you can even split your app by component-based depends on your need. For more advanced configuration you can refer to the docs of loadable-components.

Managing Title, Meta, Styles and Scripts

The ./src/app/index.js (app root component) defines the base title and meta in a <Helmet {...config.app} /> component. Any sub-component can override/add properties (supports meta, link, script, style tags and html attributes). See the react-helmet documents for more info.

App config

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 instead, and it inherits the data info from default.js.

You can access the correct config with:

import config from "./config";

Styles

The starter supports CSS, SASS and CSS modules is enabled by default. I 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 './styles.scss';

// ...

render() {
  return (
    <div className={styles.myClass}>   // The className matches one of CSS classes in your SCSS file
      <Helmet title="Home" />
      {this.renderUserList()}
    </div>
  );
}

Without CSS modules (you need to turn off CSS modules from ./tools/webpack/config.babel.js):

import './styles.scss';

// ...

render() {
  return (
    <div className="myClass">    // Use the CSS class as normal
      <Helmet title="Home" />
      {this.renderUserList()}
    </div>
  );
}

By the way, if you want to use vendor CSS frameworks or global styles, just import it through the ./src/app/index.js file (app root component). For example:

import "../../theme/normalize.css"; // Import a vendor stylesheet here
import styles from "./styles.scss"; // Import your based stylesheet here

// ...

export default routes => {
  // ...
};

Image and Font

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("./assets/logo.svg")} alt="Logo" role="presentation" />

Using font-awesome:

// With CSS modules
import styles from "./styles.scss";

// ...

return (
  <div>
    <div>
      <i className={styles.iconUser} /> Welly
    </div>
  </div>
);

// Without CSS modules
import "./font-awesome.css";

// ...

return (
  <div>
    <div>
      <i className="fa fa-user" /> Welly
    </div>
  </div>
);

For using CSS modules, you have to set the proper font path in your scss/sass file:

$fa-font-path:"../node_modules/font-awesome/fonts";
@import "../node_modules/font-awesome/scss/font-awesome";
.icon-user {
  @extend .fa;
  @extend .fa-user;
}

Boost App Performance by Shallow Compare

If your React component's render() function renders the same result given the same props and state, you can use React.PureComponent for a performance boost.

React.PureComponent is exactly like React.Component but implements shouldComponentUpdate() with a shallow prop and state comparison. See the Optimizing Performance topic for more info.

How we implemented the optimizing:

import React, { PureComponent } from "react";

// ...

class Home extends PureComponent {
  // Use PureComponent instead of Component
  // ...
}

Type Checking by Flow

Flow, a static type checker for javascript. It adds static typing to javascript to improve developer productivity and code quality. In particular, static typing offers benefits like early error checking, which helps you avoid certain kinds of runtime failures, and code intelligence, which aids code maintenance, navigation, transformation, and optimization.

Flowโ€™s static analysis makes building web apps with React safe by tracking the types of props and state. Flow understands which props are required and also supports default props.

I love to write React, Redux with Flow, I know it's not easy to learn at the beginning. But trust me, it's worth to learn. There're some useful instructions that I can give you as below:

  • If you are new to Flow, read the official docs to understand it.

  • Learn how to use Flow with React from here.

  • Here's an example, which shows you the overall concept of integrating Flow with Redux.

Moreover, often you will want to use third-party libraries. For these circumstances, Flow supports the concept of a "libdef" ("Library Definition") which allows you to describe the interface and types of the library separate from the library and without needing to add types to or change the library itself. You can write a libdef file yourself if you need to or use flow-typed, which is a repository of third-party library interface definitions for use with Flow.

Note: If you don't want to use Flow, just remove the /* @flow */ comment and related typing definitions from each javascript file.

JavaScript and Style Lint

ESLint (With Airbnb configuration), StyleLint, Prettier and lint-staged are integrated into this starter to maintain a consistent javascript and style code style and give you a elegant code formatting. You can configure your lint rules through ./package.json file.

Unit Tests

This starter use Jest as the testing engine. It runs in a Node environment, so you won't have access to the DOM. In addition, Jest support the feature of snapshot testing, which is very powerful for testing React component. Give it a try, you'll be impressed.

I also use enzyme as the testing utility for React, which makes it easier to assert, manipulate, and traverse your React Components' output. The unit tests focus on three parts as below:

  • React Components
  • Actions
  • Reducers

By the way, Jest built-in code coverage reports, the report files are generated in ./coverage folder. You can configure ./package.json to define which files that you want to cover. For example:

{
  // ...

  "jest": {
    "collectCoverageFrom": [
      "src/pages/**/*.js",        // Define the files, which want to be covered
      "src/components/**/*.js",
      "!src/pages/index.js"       // The files will be ignored by code coverage
    ],
    // Other configurations
  },

  // ...
}

You can also use istanbul's ignore hints to specify specific lines of code in a javascript file to skip code coverage.

Troubleshooting

  • If you get the the following errors, try to run yarn build to re-generate the necessary webpack-assets.json file for enable related assets (e.g. javascript, styles etc.) working on universal rendering.

    • Cannot find module '../public/assets.json'

    • Failed to load resource: the server responded with a status of 404 (Not Found)

  • If you encounter the markup mismatches error (it's a react universal issue, which usually occurs due to the non-synchronized rendering result between client and server), you can do:

    • Restart the server to solve it.
    • Or for v16.1.0 up, you can use suppressHydrationWarning attribute for intentional client/server text mismatches (#11126).
  • If you run the starter through a cloud computing service such as AWS EC2 instance etc. and you encounter an UnhandledPromiseRejectionWarning like this issue. It might caused by the "openBrowser" tool. You can solve the issue like following.

In the ./package.json script:

// ...

"start:prod": {
  "command": "node ./index.js",
  "env": {
    "NODE_PATH": "./src",
    "NODE_ENV": "production",
    "PORT": 8080,
    "BROWSER": "none"   // Add this node variable to turn off the function of open browser automatically
  }
},

// ...
  • If you are on windows and encounter the following error: Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style. The following rule must be added to ./package.json.
"linebreak-style": 0

So it will look like:

// ...
 "rules": {
      "linebreak-style": 0,
      "global-require": 0,
// ...

To Do...

There's a features (updates) which will be included in this starter in the near future:

  • Code splitting for reducers.

About

๐Ÿ˜Ž A starter boilerplate for an universal web app with the best development experience and a focus on performance and best practices.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 98.1%
  • CSS 1.9%