A simple but feature rich starter boilerplate for creating your own universal app. It built on the top of React, Redux, React Router and Express. Includes all the hot stuff and modern web development tools such as Redux Toolkit, TypeScript, Webpack, Babel, PostCSS, React Refresh, Jest and React Testing Library. 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
��
�� If you have built a production web app based on this starter, please open a PR to add it here.
Really cool starter boilerplate with the most popular technologies:
They're several React frameworks today, however this is a DIY oriented start-kit. It shows you how to build a universal web app from scratch and how to test it. If you're new to React or you want a ready-to-go solution, I'd recommend the following alternatives for you:
You can find Docker support version on this branch.
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 the
./src/config
.
I use cross-env to set and use environment variables across platforms. All of the scripts are listed as following:
yarn <script> |
Description |
---|---|
dev |
Runs your app on the development server at localhost:3000 . HMR will be enabled. |
dev:build |
Bundles server-side files in development mode and put it to the ./public/server . |
start |
Runs your app on the production server only at localhost:8080 . |
build |
Bundles both server-side and client-side files. |
build:server |
Bundles server-side files in production mode and put it to the ./public/server . |
build:client |
Bundles client-side files in production mode and put it to the ./public/assets . |
analyze:server |
Visualizes the bundle content of server-side. |
analyze:client |
Visualizes the bundle content of client-side. |
lint |
Lints all .tsx? , .jsx? and .scss files. |
lint:code |
Lints all .tsx? and .jsx? files (With --fix to auto fix eslint errors). |
lint:type |
Runs type checking for .tsx? files. |
lint:style |
Lints all .scss files (With --fix to auto fix stylelint errors). |
lint:format |
Formats all files except the file list of .prettierignore . |
test |
Runs testing. |
test:watch |
Runs an interactive test watcher. |
test:cov |
Runs testing with code coverage reports. |
test:update |
Updates jest snapshot. |
Here is the structure of the app, which serves as generally accepted guidelines and patterns for building scalable apps.
.
├── public # Express server static path and Webpack bundles output
│ ├── favicon.ico # App favicon
│ ├── logo192.png # App logo small
│ ├── logo512.png # App logo large
│ └── manifest.json # App favicon and logo manifest
├── src # App source code
│ ├── config # App configuration by environments
│ │ ├── default.ts # Default settings
│ │ ├── index.ts # Configuration entry point
│ │ └── prod.ts # Production settings (overrides the default)
│ ├── components # Reusable components
│ ├── pages # Page components
│ ├── app # App root component
│ ├── store # Redux store creator, actions + reducers (a.k.a slice)
│ ├── services # API calls
│ ├── utils # App-wide utils (e.g. mock store creator for testing etc.)
│ ├── static # Static assets (e.g. images, fonts etc.)
│ ├── theme # App-wide style and vendor CSS framework
│ ├── types # App-wide type definitions
│ ├── client # App bootstrap and rendering (Webpack entry)
│ ├── routes # Routes configuration for both client-side and server-side
│ └── server # Express server (with Webpack dev and hot middlewares)
├── webpack # Webpack configurations
├── jest # Jest configurations
├── babel.config.js # Babel configuration
├── tsconfig.json # TypeScript configuration
├── postcss.config.js # PostCSS configuration
├── .eslintrc.js # ESLint configuration
├── .stylelintrc.js # stylelint configuration
└── nodemon.json # nodemon configuration
Concerning the security and performance of Express in production, I already setup some middleware for you:
Note: It's just a basic protected mechanism for your app, you can see the security best practices for more advanced configuration.
Redux Toolkit is the official, opinionated, batteries-included toolset for efficient Redux development. It includes several utility functions that simplify the most common Redux use cases. In a word, we can do more work with less code, start from the tutorial to learn more about it.
Let's see how powerful it is by a simple asynchronous data fetching example:
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
readyStatus: "invalid",
items: [],
error: null,
};
const userList = createSlice({
// A name for action types
name: "userList",
initialState,
// An object of "case reducers", key names will be used to generate actions
reducers: {
getRequesting: (state) => {
// Write an immutable updated state by a mutable way, thanks to the built-in Immer middleware
state.readyStatus = "request";
},
getSuccess: (state, { payload }) => {
state.readyStatus = "success";
state.items = payload;
},
getFailure: (state, { payload }) => {
state.readyStatus = "failure";
state.error = payload;
},
},
});
// We can get the "reducer" and "actions" from the slice instance
export default userList.reducer;
const { getRequesting, getSuccess, getFailure } = userList.actions;
export const fetchUserList = () => async (dispatch) => {
dispatch(getRequesting());
try {
const { data } = await axios("/api/users");
// Dispatch the action once data is ready via the built-in Redux Thunk middleware
dispatch(getSuccess(data));
} catch (error) {
dispatch(getFailure(error.message));
}
};
React v16.8 introduced a series of Hooks, which let you use state and other React features without writing a class. In the starter boilerplate, you can see how I leverage the benefit of functional components + hook APIs to write a demo with clean code.
This starter use React Router 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 the ./src/routes/index.ts
. For example:
import PageComponent from "../pages/PageComponent";
export default [
{
// Define your route path
path: "/top-path",
// If the route matches the location.pathname exactly or not (used for index route usually)
exact: true,
// Add your page component here
component: PageComponent,
// Add your sub route component here
routes: [
{
path: "/top-path/sub-path",
component: SubComponent,
},
],
},
// Other routes...
];
Strongly recommend to write Redux actions and reducers via the createSlice API of Redux Toolkit (start from the tutorial if you are new). The starter using axios as the data fetcher, it's quite simple and easy to use. If the action is asynchronous then it will return a Promise (or a Promise.all) in the inner function.
Register the action(s) in the ./src/routes/index.ts
, which have to be called from server-side:
export default [
{
path: "/top-path",
exact: true,
component: PageComponent,
// Async actions in the loadData function will be fetched from server-side
// You can access the URL parameters, Redux store, HTTP request and response by the event object
loadData: ({ params, getState, req, res }) => [
myReduxAction(),
// More pre-fetched actions...
],
},
];
The action(s) will be dispatched through the ./src/server/ssr.tsx
on server-side:
app.get("*", (req, res) => {
// ...
// Here's the method for loading data from server-side
const loadBranchData = () => {
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,
req,
res,
})
.map((item) => store.dispatch(item))
);
}
return Promise.resolve(null);
});
return Promise.all(promises);
};
// ...
});
In client-side, don't forget to invoke the action(s) in componentDidMount
or useEffect
hook. 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:
// Use React class component
componentDidMount() {
// Invoke your redux action(s) for client rendering
this.props.myReduxAction();
}
// Use functional component
useEffect(() => {
myReduxAction();
}, [])
For better architecture, we can centralize the data fetching methods in one place like how I did in the Home page.
const SomePage = () => {
// ...
// Fetch client-side data here
useEffect(() => {
dispatch(fetchUserListIfNeed());
}, [dispatch]);
// ...
};
// Fetch server-side data here, the method will be used by the route config
export const loadData = () => [fetchUserListIfNeed()];
export default SomePage;
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 page:
I use the following folder/file structure:
└─ pages
└─ SomePage
|- PageComponent.tsx // The page component
└─ index.tsx // Wrap the component into async component
The index.tsx
will be:
import loadable from "@loadable/component";
import { Error, Loading } from "../../components";
import { loadData } from "./PageComponent";
// Dynamically import your page component
const AsyncPage = loadable(() => import("./PageComponent"), {
// <Loading /> will be displayed when the component is being loaded
fallback: <Loading />,
});
export default (props) => (
// Wrap an <ErrorBoundary /> to catch the error of <AsyncPage /> (via "componentDidCatch()" life cycle)
<ErrorBoundary>
<AsyncPage {...props} />
</ErrorBoundary>
);
export { loadData }; // Export SSR data fetching method as well
Then you can setup the route as usual.
Note: I just show a general case page-based splitting, however you can even split your app by component-based depends on your needs. For more advanced configuration, please refer to the document. In additional, loadable-components supports Webpack pre-fetching/pre-loading out of the box.
<link rel="preload">
and<link rel="prefetch">
can be added directly server-side to improve performances.
The ./src/app/index.tsx
(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 document for more info.
You can store app settings under the ./src/config
. By default the default.ts
will be loaded. If the process.env.NODE_ENV
matches to production (alias as !__DEV__
), the prod.ts
will be used instead, and it inherits the properties of default.ts
.
You can access the correct configuration with:
import config from "./config";
The starter supports CSS, SASS and CSS modules is auto enabled for all files the [name].module.*
naming convention. 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.module.scss";
// ...
return (
<div className={styles.myClass}>
{/* The className matches one of CSS classes in your .scss file */}
<Helmet title="My title" />
{this.renderContent()}
</div>
);
Without CSS modules:
import "./styles.scss";
// ...
return (
<div className="myClass">
{/* Use the CSS class as normal */}
<Helmet title="My title" />
{this.renderContent()}
</div>
);
By the way, if you want to use vendor CSS frameworks or global styles, just import it through the ./src/app/index.tsx
file (app root component). For example:
import "../../theme/normalize.css"; // Import the vendor stylesheet first
import styles from "./styles.scss"; // Then your based stylesheet
const App = ({ route }) => (
// ...
};
It's super easy to render the image and font both on client and server, the usage would be like below.
Using image:
import logo from "../static/logo.svg";
<img src={logo} 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-icon" /> 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;
}
In this starter, you can see I use React.PureComponent and React.memo to demonstrate the basic performance optimizing for React app. The two APIs are used in different ways.
React.PureComponent
is used for React class components. It can do shallow prop and state comparison for a performance boost:import { PureComponent } from "react";
class MyComponent extends PureComponent {
// Only re-renders if props change
}
React.memo
is used for functional components. It plays the same role as React.PureComponent
:import { memo } from "react";
const MyComponent = memo((props) => {
// Only re-renders if props change
});
import { useMemo, useCallback } from "react";
// Performance optimizing via useMemo()
const ParentComponent = (props) => (
<div>
{/* Only re-renders if "a" change */}
{useMemo(
() => (
<ChildComponent someProp={a} />
),
[a]
)}
</div>
);
// Performance optimizing via useCallback()
const ParentComponent = (props) => (
<div>
{/* Return a memorized callback that only changes if "a" changed */}
{/* This is useful to prevent child component from unnecessary renders */}
<ChildComponent
callback={useCallback(() => {
doSomething(a);
}, [a])}
/>
</div>
);
For more performance optimizing techniques. Please see the Optimizing Performance topic.
TypeScript is a typed super-set of JavaScript. It's getting more and more popular in the Front-end world. And being widely used by many libraries. If you are new to TypeScript, you can check out its document here.
TypeScript has been integrated with our application to bring the following benefits:
ESLint (with Airbnb configuration), typescript-eslint, stylelint, Prettier and lint-staged are integrated into this starter to maintain a consistent code style and give you a elegant code formatting. You can configure your lint rules through the .eslintrc.js
, .stylelintrc.js
, and prettier configuration file.
This starter use Jest as the testing framework. We also use React Testing Library with jest-dom, give you a simple and complete React DOM testing utilities that encourage good testing practices.
Jest support the feature of snapshot testing, which is very powerful for testing React component. Give it a try, you'll be impressed. The unit testing focus on three parts as below:
By the way, Jest built-in code coverage reports, the report files are generated in the ./coverage
folder. You can configure the ./jest/config.js
to define which files that you want to cover. For example:
module.exports = {
collectCoverageFrom: [
"src/pages/**/*.tsx", // Define the files, which want to be covered
"!src/pages/index.ts", // 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.
To deploy you app to cloud service (e.g. AWS, GCP, Azure etc.), you can follow the instructions below.
yarn build # Building bundle
rm -rf node_modules # After building remove node modules
yarn install --production # Then install dependencies only
yarn start
Ideally, the above steps can be integrated into your CI. I recommend you to pack the
yarn.lock
file for yarn installation by CI.
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:
suppressHydrationWarning
attribute for intentional client/server text mismatches (#11126).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 .eslintrc.js
.
// ...
"rules": {
"linebreak-style": "off",
// Other rules...
}
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
by Alex Booker 通过亚历克斯布克 学习使用React和Electron一次构建自己的桌面聊天应用程序 (Learn to build your own desktop chat app with React and Electron, one step at a time) This tutorial was written in collaboration with the awe
gatsby Gatsby is a static site generator for React that released its first major version last month. It’s a tool that not only scaffolds projects (or websites) for you but claims that those sites are
Django Cool 是一个 Django 框架快速使用扩展库。目前 Django Cool 支持的 Python 环境有 3.6、3.7、3.8,支持 Django 版本 2.2、3.0、3.1。 models 扩展 自定义 Model 继承 BaseModel 可使用扩展功能: 支持字段变更监控记录 通过 save_changed() 保存已修改字段 主键唯一键缓存 缓存获取: get_ob
Cool-Node 是一个基于 Node.js 平台的轻量级 Web 应用程序框架。它拥有诸如自动路由映射、跨协议开发、多应用模式等及其出色的功能。 Cool-Node 体积精巧,它只定义了编写项目的一些规则,并对一些成熟的 Web 框架进行包装,使其更适合快速开发。 Cool-Node 是简单易用的,它不二次定义 Node.js 及依赖项所有的功能,而只是将其包装并提供更合适的接口和自动化特性去
cool-php-captcha 是一个很酷的 PHP 用来生成验证码的库。 示例代码: session_start(); $captcha = new SimpleCaptcha(); // Change configuration... //$captcha->wordsFile = null; // Disable dictionary words //$captcha-
Jfinal-Cool-Blog,这是一个基于jfinal开发的炫酷博客系统。 本博客采用了jfinal 作为后台框架 ,前端使用 bootstrap为主要结构,引用以下js和css <link href="/DJSBlog/static/css/bootstrap.min.css" rel="stylesheet"><link href="/DJSBlog/static/css/animate.
cool-retro-term > Default Amber C:\ IBM DOS $ Default Green Description cool-retro-term is a terminal emulator which mimics the look and feel of the old cathode tube screens.It has been designed to be
cool-admin(midway版)一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发CRUD,方便快速构建迭代后台管理系统,支持serverless、docker、普通服务器等多种方式部署到 论坛 进一步了解 技术栈 后端:node.js midway.js egg.js mysql typescript 前端:vue.js element-ui jsx vuex vue-rout