Project structure


Generated app uses the following technologies:

App structure

App based on monorepo structure. It consists of plugins of two types: system plugins (rispa plugins) and project plugins (features). It has following structure:

├── build
├── packages
│   ├── rispa-config
│   ├── rispa-routes
│   ├── rispa-ui-kit
│   …
│   ├── feature-plugin
│   │   └── src
│   │   │   ├── components
│   │   │   │   └── Component
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── Component.js
│   │   │   │   │   ├── Component.sss
│   │   │   │   │   ├── Component.test.js
│   │   │   ├── containers
│   │   │   │   └── Container
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── Container.js
│   │   │   │   │   ├── Container.test.js
│   │   │   ├── redux
│   │   │   │   └── reducer.js
│   │   │   ├── index.js
│   │   │   ├── register.js
│   │   └── package.json
│   ...
├── rispa.json
└── package.json

List of used rispa plugins:

Plugin name Description
rispa‑core Provides functionality of communicating plugins with each others
rispa‑config Contains configuration settings (host and port for server, outputPath and publicPath for build)
rispa‑server Runs express server and rendering through extension point
rispa‑render‑server Implement server-side rendering using universal-webpack
rispa‑client Entry point for client app.
  • configures store
  • creates tree with root components: AppContainer (hot reloading), Provider (redux), CookieProvider, ConnectedRouter (rr4)
  • inject routes get from rispa-routes plugin to the component tree
  • register React Hot Loader
rispa‑routes This plugin is a extension point for whole app. It gathers async routes from other plugins and register then in a global Switch, which rispa-client plugin injects into components tree
rispa‑redux Contains functions for configuring store and helpers for creating reducers (hor) and data loading (when)
rispa‑ui‑kit App ui kit
rispa‑vendor Contains bunch of vendor libs used by other plugins
rispa‑webpack Makes build for dev server and production

Running the App

To start work you should install rispa-cli globally. It provides command to run and maintain app:

yarn global add @rispa/cli


npm install -g @rispa/cli

We use yarn as a package manager by default. To switch to npm you may change it in the lerna configuration (lerna.json)

Each plugin has its own commands which you can run in project root using following syntax:

ris [pluginName] [commandName]

pluginName - a plugin name (name property in the package.json) or it’s alias (rispa:name property in the package.json) commandName - one of the scripts of package.json of the plugin

pluginName and commandName are optional. If you omit them command will allow to pick plugin or command from the list

Full list of command for each plugin you can see on github. Main commands are in the table below:

Command Description
ris server start-dev Builds and runs app dev server
ris server start-prod Runs app server for production
ris server start-dev-client Builds and runs app dev server with SSR disabled
ris server start-prod-client Runs app server for production with SSR disabled
ris server start-profile Builds and runs app dev server with profiling components cache
ris webpack build Builds app for production
ris ui sb Runs storybook
ris ui build-storybook Builds and runs storybook for production
ris all test, ris all test:coverage Runs tests for all plugins
ris all lint, ris all lint:fix Runs eslint for all plugins
ris all stylelint Runs stylelint for all plugins
ris core g Runs modules generator

Note: To run app for production you should run commands ris webpack build and ris server start-prod one by one


It is assumed that the functionality of the application is divided into several features. Each feature is a part of the application that has its own route. For example, Home Page or About Page are individual features that have their own route. To add a new feature, you should create a plugin. To do this, you can use the generator by following command:

ris core g

Then pick feature-plugin in the list and specify the name and the route path. The plugin will be added to the packages folder and will have the following structure:

├── feature-plugin
│   └── src
│   │   ├── index.js
│   │   ├── register.js
│   ├── package.json

index.js specified as a main property in the package.json of the plugin. It exports the function which should return the config for router (object which contains path and component properties).

import Loadable from '@rispa/vendor/loadable'

const loadable = (store, when) => Loadable({
  LoadingComponent: () => null,
  loader: () => import('./register'),
  resolveModule: module => module.default(store, when),
  webpackRequireWeakId: () => require.resolveWeak('./register'),

const createRoute = (store, when) => ({
  path: '/path',
  component: loadable(store, when),

export default createRoute

We use Loadable for code splitting and async loading chunks.

register.js exports the function which accepts store and when parameters for injecting reducers or registering data loading for plugin. Function should return the component which will be displayed for specified route:

import Component from './components/Component/'
import reducer, { action } from './redux/reducer'
import { when, match } from '@rispa/redux/when'

const registerReducer = (store) => {
  store.injectReducer('reducerName', reducer)

const registerWhen = (when) => {
  when(match('/path'), () => action(), true)

const registerModule = (store, when) => {

  return Component

export default registerModule

After you create a plugin it's automatically added as a dependency to rispa-routes plugin and registered as a Route in global Switch

results matching ""

    No results matching ""