{"id":13822010,"url":"https://github.com/jcoreio/redux-features","last_synced_at":"2025-07-31T03:31:45.831Z","repository":{"id":14647458,"uuid":"76698671","full_name":"jcoreio/redux-features","owner":"jcoreio","description":"a powerful feature-oriented programming framework for redux","archived":false,"fork":false,"pushed_at":"2023-11-09T19:22:24.000Z","size":772,"stargazers_count":14,"open_issues_count":18,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-29T05:18:22.160Z","etag":null,"topics":["code-splitting","feature-oriented-programming","features","fop","redux"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jcoreio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-12-17T02:14:19.000Z","updated_at":"2023-11-09T02:03:43.000Z","dependencies_parsed_at":"2023-09-22T02:45:29.868Z","dependency_job_id":"84b16774-f0a5-416f-998c-cd511f0b2e48","html_url":"https://github.com/jcoreio/redux-features","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/jcoreio/redux-features","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-features","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-features/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-features/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-features/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcoreio","download_url":"https://codeload.github.com/jcoreio/redux-features/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-features/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267983366,"owners_count":24176058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-31T02:00:08.723Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-splitting","feature-oriented-programming","features","fop","redux"],"created_at":"2024-08-04T08:01:38.098Z","updated_at":"2025-07-31T03:31:45.543Z","avatar_url":"https://github.com/jcoreio.png","language":"JavaScript","readme":"# redux-features\n\n[![npm](https://badge.fury.io/js/redux-features.svg)](https://badge.fury.io/js/redux-features)\n[![Build Status](https://travis-ci.org/jcoreio/redux-features.svg?branch=master)](https://travis-ci.org/jcoreio/redux-features)\n[![Coverage Status](https://coveralls.io/repos/github/jcoreio/redux-features/badge.svg?branch=master)](https://coveralls.io/github/jcoreio/redux-features?branch=master)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)\n\nA feature-oriented programming and code-splitting framework for Redux (rewrite of [`redux-plugins-immutable`](https://github.com/jcoreio/redux-plugins-immutable)).\nAsynchronously and atomically load features containing Redux reducers and middleware, React Components, react-router\nroutes, etc. using Webpack code splitting. Supports server-side rendering.\n\n## Huh?\n\nWhen a web app gets big enough, initial page load will be too slow unless you split your code into bundles and avoid loading various features until they're needed. For instance, in a React/Redux app, you may have a `UserProfileView` and `userProfileReducer` that you don't want to load until the user visits your `/user/profile` route.\n\nWebpack provides a foundation for this; you can `System.import` those modules when needed and Webpack will put them in a separate code bundle. And `react-router` versions 2 and 3 support `getComponent` hooks on routes in which you can `System.import` the component to render.\n\nBut these are bare-bones tools: they don't automatically show any kind of spinner and tell the user what's happening while a bundle is loading, and more importantly, they don't automatically show the user an error if it fails to load. Also, if you want to load reducers or redux middleware from a separate bundle, you need some way to install them in your redux store once they've loaded. None of these are incredibly difficult problems, and you may have basic solutions to them, but you may not have time to design a systematic, well-organized, DRY solution.\n\nThis is where `redux-features` comes in! It allows you to define features, each of which may contain a reducer, middleware, React components, or anything else you want. All you have to do is dispatch a `loadFeature` action, and it takes care of loading them and hooking in your reducer and middleware when it's finished. It stores their loading status and load errors (if any) in Redux state, which you can then connect into a component and display to the user. Together with `react-redux-features`, you can create a component with just a few lines of code that automatically starts loading a feature when it mounts, displays its loading or error status to the user, and renders a React component from the feature once it's loaded.\n\n`redux-features` is also a great plugin framework for your app. A third-party developer can write a feature, following the contract of this package, and by simply adding the feature definition via `redux-features`, it can extend or modify your app's behavior.\n\n## Ecosystem\n\n- [`react-redux-features`](https://github.com/jcoreio/react-redux-features): creates declarative feature loaders that proxy to feature components.\n- [`redux-features-hot-loader`](https://github.com/jcoreio/redux-features-hot-loader): webpack hot reloader for features.\n\n## TOC\n\n- [Overview](#overview)\n  - [But Redux state shouldn't contain functions, React components, etc.!!](#but-redux-state-shouldnt-contain-functions-react-components-etc)\n  - [Feature lifecycle](#feature-lifecycle)\n  - [Feature API](#feature-api)\n- [Quick start](#quick-start)\n  - [Creating the redux store](#creating-the-redux-store)\n  - [Adding a feature](#adding-a-feature)\n  - [Loading the feature](#loading-the-feature)\n- [Custom start](#custom-start)\n  - [`featuresReducer` and `featureStatesReducer`](#featuresreducer-and-featurestatesreducer)\n  - [`loadFeatureMiddleware`](#loadfeaturemiddleware)\n  - [featureReducersReducer](#featurereducersreducer)\n  - [featureMiddlewaresMiddleware](#featuremiddlewaresmiddleware)\n  - [Using custom `createReducers`, `composeReducers`, `createMiddleware`, and `composeMiddleware`](#using-custom-createreducers-composereducers-createmiddleware-and-composemiddleware)\n- [Server-side rendering](#server-side-rendering)\n  - [One-pass rendering example](#one-pass-rendering-example)\n  - [Two-pass rendering example](#two-pass-rendering-example)\n    - [reducer.js](#reducerjs)\n    - [counterFeature.js](#counterfeaturejs)\n    - [counterFeatureImpl.js](#counterfeatureimpljs)\n    - [Counter.js](#counterjs)\n    - [serverSideRender.js](#serversiderenderjs)\n    - [client.js](#clientjs)\n\n## Overview\n\nEach feature is an object you put into Redux state that groups together everything needed for a feature to function.\nTypically this will include one ore more React components, a `reducer`, and maybe even `middleware`. By grouping these\ntogether and atomically adding them to the redux state, you can atomically activate entire parts of your application.\n\nWhen you combine this with Webpack code splitting, you get a nice, clean way to dynamically load application features\nwhen they are needed, for instance when the user visits a certain route.\n\nThis can also be a great foundation for people to develop plugins for your app.\n\n### But Redux state shouldn't contain functions, React components, etc.!!\n\nPeople have good reasons for advocating this, primarily because you can't serialize functions or React components and\nsend them over the wire. But that doesn't mean you can't send everything else, and reconstruct the features from it.\n\nFor awhile, I thought I should store the functions, React components, etc. for features outside of Redux state,\nbut I came to the conclusion that it's best to keep them in the Redux state. Here's why:\n\n- The alternative would be a separate store and provider of some sort to pass down features' functions and components\n  through React context, which would be very similar to the Redux store and provider in form.\n- All of the non-serializable feature functions and components are organized in a single subtree of your state that is\n  easy to strip out before serializing.\n- The client can reconstruct the same state as the server by adding all of the same features, then loading all of the\n  features marked as loaded in the serialized state from the server.\n\n### Feature lifecycle\n\n`redux-features` keeps track of the state of each feature in a separate subtree from the features themselves. Here is\nwhat the feature lifecycle looks like:\n\n- You dispatch `addFeature(...)`\n- The feature's state is set to `NOT_LOADED`. `redux-features` middleware calls its `init` method, if it has one.\n- You dispatch `loadFeature(...)`\n- The feature's state is set to `LOADING`. `redux-features` middleware calls its `load` method, if it has one.\n- The promise returned by `load` resolves to the full feature, which `redux-features` middleware dispatches in an\n  `installFeature(...)` action.\n- The feature's state is set to `LOADED`.\n- Or the promise returned by `load` rejects, in which case `redux-features` middleware dispatches a\n  `setFeatureStatus(...)` action with the rejection reason.\n- The feature's state is set to the rejection reason.\n\nIf you add a feature with a `reducer`, its `reducer` will be active, even if its state is `NOT_LOADED` -- whatever\nis in the redux state is active. Indeed, you may sometimes want to add a feature without a `load` method at all.\nYou only need to load a feature if you want to fetch code for it asynchronously or perform some other long-running\ninitialization in the background.\n\n### Feature API\n\nThe following optional properties on features are handled by `redux-features`. However, you may add any other\nproperties you want.\n\n- `reducer: (state, action) =\u003e state`: a reducer to apply to the top-level redux state for each action\n- `middleware: store =\u003e next =\u003e action =\u003e any`: middleware to apply for each action\n- `init: (store) =\u003e any`: called when the feature is added by dispatching an `addFeature` action\n- `load: (store) =\u003e Promise\u003cFeature\u003e`: called when you dispatch a `loadFeature` action for the feature. It should\n  return a promise that will resolve to the full feature after loading. The full feature **will replace\n  the current feature in the redux state**, so initial properties of the feature will be blown away unless you merge\n  them into the full feature yourself.\n- `dependencies: Array\u003cstring\u003e`: an array of feature ids to load when this feature is loaded. All dependencies will be\n  loaded in parallel with this feature, so they may not be available during the `load` method. Circular feature\n  dependencies are not supported, and the behavior is undefined. As an alternative to `dependencies`, you can load\n  other features (by dispatching `loadFeature` actions) in your feature's `load` method, in which case it's easy to\n  wait for those features to load and do something with them before resolving your feature's `load` promise.\n\n## Quick start\n\n### Creating the redux store\n\n```js\nimport {createStore, applyMiddleware, combineReducers} from 'redux'\nimport {\n  featureStatesReducer,\n  featuresReducer,\n  featureReducersReducer,\n  loadFeatureMiddleware,\n  featureMiddlewaresMiddleware,\n  composeReducers,\n  addFeature,\n  loadFeature,\n} from 'redux-features'\n\nconst reducer = composeReducers(\n  combineReducers({\n    featureStates: featureStatesReducer(),\n    features: featuresReducer(),\n    ...\n    // your own app reducers here\n  }),\n  featureReducersReducer(),\n)\n\nconst store = createStore(reducer, applyMiddleware(\n  featureMiddlewaresMiddleware(),\n  loadFeatureMiddleware(),\n  // more middleware you want here\n))\n\n```\n\n### Adding a feature\n\n```js\nconst counter = {\n  load(store) {\n    return System.import('./counter/reducer').then(reducer =\u003e ({...this, reducer})\n  }\n}\nstore.dispatch(addFeature('counter', counter))\n\n// state now looks like this:\n/*\n * {\n *   featureStates: {\n *     counter: 'NOT_LOADED'\n *   }\n *   features: {\n *     counter: {\n *       load: load(store)\n *     }\n *   }\n * }\n */\n```\n\n### Loading the feature\n\nNow let's say `./counter/reducer.js` contains this:\n\n```js\nexport default function counterReducer(state, action) {\n  switch (action.type) {\n    case 'COUNTER.INCREMENT':\n      return { ...state, counter: (state.counter || 0) + 1 }\n    case 'COUNTER.DECREMENT':\n      return { ...state, counter: (state.counter || 0) - 1 }\n    default:\n      return state\n  }\n}\n```\n\nOnce the `'counter'` feature is loaded, `featureReducersReducer` will call `counterReducer`:\n\n```js\nstore.dispatch({ type: 'COUNTER.INCREMENT' }) // does nothing yet\n\nstore.dispatch(loadFeature('counter')).then(() =\u003e {\n  // state now looks like this:\n  /*\n   * {\n   *   featureStates: {\n   *     counter: 'LOADED'\n   *   }\n   *   features: {\n   *     counter: {\n   *       load: load(store),\n   *       reducer: counterReducer(state, action)\n   *     }\n   *   }\n   * }\n   */\n\n  store.dispatch({ type: 'COUNTER.INCREMENT' })\n  // state.counter is now 1\n})\n```\n\n## Custom start\n\nYou may be setting up your reducer and middleware in a different way than in the examples above; you may be using a\ndifferent type for your state, like an Immutable.js Map; or you may want to mount the features and feature states at\nnon-default locations. To support that, `redux-features` exports a modular collection of reducers and middleware\nyou can hook into your app however you need:\n\n- `featuresReducer` - puts the features into the state\n- `featureStatesReducer` - controls state specifying whether features are loaded or not\n- `featureReducersReducer` - calls features' reducers\n- `loadFeatureMiddleware` - handles `loadFeature` actions by calling a feature's `load` method\n  and dispatching `installFeature` when it resolves (or `setFeatureStatus` with an `error` if it rejects).\n- `featureMiddlewaresMiddleware` - calls features' middleware\n\nTechnically you could use only `featuresReducer` if all you want to do is add features that provide React components\nto your app at startup, and not do any dynamic loading.\n\nIf you want to dynamically load features, you'll need to use `featureStatesReducer` and `loadFeatureMiddleware`.\nAnd if you want to support reducers or middleware in features, you'll need to use `featureReducersReducer` and\n`featureMiddlewaresMiddleware`, respectively.\n\n### `featuresReducer` and `featureStatesReducer`\n\nIf you're using `combineReducers`, you can hook `featuresReducer` and `featureStatesReducer` into it with any keys you\nwant:\n\n```js\nimport {combineReducers} from 'redux'\nimport {featureStatesReducer, featuresReducer} from 'redux-features'\n\nconst appReducer = combineReducers({\n  ...\n  myFeatureStates: featureStatesReducer(),\n  myFeatures: featuresReducer(),\n})\n```\n\nWhich would make your state look like\n\n```js\n{\n  ...\n  myFeatureStates: {\n    feature1: 'NOT_LOADED',\n    feature2: 'LOADING',\n  },\n  myFeatures: {\n    feature1: {...},\n    feature2: {...},\n  },\n}\n```\n\nHowever, you can hook in `featuresReducer` and `featureStatesReducer` however you want, so long as you call them with\nthe features subtree and feature states subtree, respectively.\n\n### `loadFeatureMiddleware`\n\nIf you mount the features and their states anywhere other than `state.features` and `state.featureStates`, you have to\ntell `loadFeatureMiddleware` how to get them out of the state, like this:\n\n```js\nimport { createStore, applyMiddleware } from 'redux'\nimport { loadFeatureMiddleware } from 'redux-features'\nimport reducer from './reducer'\nimport initialState from './initalState'\n\nconst store = createStore(\n  reducer,\n  initialState,\n  applyMiddleware(\n    loadFeatureMiddleware({\n      getFeatureStates: (state) =\u003e state.myFeatureStates,\n      getFeatures: (state) =\u003e state.myFeatures,\n    })\n  )\n)\n```\n\n### `featureReducersReducer`\n\nIf any feature has a `reducer` property, then `featureReducersReducer` will call it for each action\ndispatched to the store. It must be composed with your top-level reducer so that it can pass the top-level state to\nthe feature reducers. You may use the `composeReducers` function from `redux-features` to do this. Again, if you\nmount the features anywhere other than `state.features`, you have to tell `featureReducersReducer` where to find them:\n\n```js\nimport {combineReducers} from 'redux'\nimport {featureStatesReducer, featuresReducer, featureReducersReducer, composeReducers} from 'redux-features'\n\nconst baseReducer = combineReducers({\n  ...\n  myFeatureStates: featureStatesReducer(),\n  myFeatures: featuresReducer(),\n})\n\nconst appReducer = composeReducers(\n  baseReducer,\n  featureReducersReducer({getFeatures: state =\u003e state.myFeatures})\n)\n```\n\n### `featureMiddlewaresMiddleware`\n\nJust like `featureReducersReducer`, if any feature has a `middleware` property, then `featureMiddlewaresMiddleware` will\ncall it for each action dispatched to the store. If you mount the features anywhere other than `state.features`, you\nhave to tell `featureMiddlewaresMiddleware` where to find them:\n\n```js\nimport {createStore, applyMiddleware, combineReducers} from 'redux'\nimport {\n  featureStatesReducer,\n  featuresReducer,\n  loadFeatureMiddleware,\n  featureMiddlewaresMiddleware,\n} from 'redux-features'\n\nconst reducer = combineReducers({\n  myFeatureStates: featureStatesReducer(),\n  myFeatures: featuresReducer(),\n  ...\n  // your own app reducers here\n})\n\nconst store = createStore(reducer, applyMiddleware(\n  featureMiddlewaresMiddleware({\n    getFeatures: state =\u003e state.myFeatures,\n  }),\n  loadFeatureMiddleware({\n    getFeatureStates: state =\u003e state.myFeatureStates,\n    getFeatures: state =\u003e state.myFeatures,\n  }),\n  // more middleware you want here\n))\n```\n\n### Using custom `createReducers`, `composeReducers`, `createMiddleware`, and `composeMiddleware`\n\nYou can pass in your own implementations of `createReducer`, `composeReducers`, `createMiddleware`, and\n`composeMiddleware`. For instance, you can use the implementations from `mindfront-redux-utils`, which can compose\nreducers from its `createReducer` and middleware from its `createMiddleware` more efficiently than a naive\nimplementation, for high-action-throughput apps:\n\n```js\nimport {createStore, applyMiddleware, combineReducers} from 'redux'\nimport {\n  featureStatesReducer,\n  featuresReducer,\n  featureReducersReducer,\n  loadFeatureMiddleware,\n  featureMiddlewaresMiddleware,\n  addFeature,\n  loadFeature,\n} from 'redux-features'\nimport {createReducer, composeReducers, createMiddleware, composeMiddleware} from 'mindfront-redux-utils'\n\nconst reducer = composeReducers(\n  combineReducers({\n    featureStates: featureStatesReducer({createReducer}),\n    features: featuresReducer({createReducer}),\n    ...\n    // your own app reducers here\n  }),\n  featureReducersReducer({composeReducers}),\n)\n\nconst store = createStore(reducer, applyMiddleware(\n  featureMiddlewaresMiddleware({composeMiddleware}),\n  loadFeatureMiddleware({createMiddleware}),\n  // more middleware you want here\n))\n```\n\n## Server-side rendering\n\nLoading features during SSR, and then again on the client, when bootstrapping, takes a bit of extra effort. There are\nvarious options:\n\n- One-pass rendering:\n  - Manually dispatch `loadFeature` actions applicable to the requested route.\n  - Dispatch `loadFeature` actions in `react-router` v2/v3 hooks like `getComponent`, `getIndexRoute`, etc.\n- Two-pass rendering:\n  - Create the store with a middleware that keeps track of `loadFeature` promises\n  - Render the app in your first pass with components that dispatch the applicable `loadFeature` events (e.g.\n    `featureLoader`s from `react-redux-features`\n  - Wait for the `loadFeature` promises to resolve\n  - Render again, which will now include the loaded feature components, and send it to the client\n\nOn the client, after you create your store from the initial server-side redux state, dispatch the `loadInitialFeatures`\naction, which will load all the features that were loaded on the server side. Once it resolves, you are ready to\nmount your app.\n\n### One-pass rendering example\n\nTODO\n\n### Two-pass rendering example\n\n(using Webpack 1x)\n\n#### reducer.js\n\n```js\nimport {\n  composeReducers,\n  featuresReducer,\n  featureStatesReducer,\n  featureReducersReducer,\n} from 'redux-features'\n\nexport default composeReducers(\n  combineReducers({\n    features: featuresReducer(),\n    featureStates: featureStatesReducer(),\n  }),\n  featureReducersReducer()\n)\n```\n\n#### counterFeature.js\n\n```js\nexport default {\n  load: (store) =\u003e\n    new Promise((resolve) =\u003e\n      require.ensure(\n        ['./counterFeatureImpl'],\n        (require) =\u003e require('./counterFeatureImpl').default\n      )\n    ),\n}\n```\n\n#### counterFeatureImpl.js\n\n```js\n// polyfill require.ensure for the server side\nif (__SERVER__) require.ensure = (modules, callback) =\u003e callback(require)\n\nexport default {\n  reducer: (state, action) =\u003e\n    action.type === 'INCREMENT'\n      ? { ...state, count: (state.count || 0) + 1 }\n      : state,\n  Counter: connect(({ count }) =\u003e ({ count }))(({ count }) =\u003e (\n    \u003cdiv\u003e{`Counter: ${count || 0}`}\u003c/div\u003e\n  )),\n}\n```\n\n#### Counter.js\n\n```js\nimport React from 'react'\nimport { featureLoader } from 'react-redux-features'\n\nconst Counter = featureLoader({\n  featureId: 'counter',\n  render({ featureState, feature, props }) {\n    const Comp = feature \u0026\u0026 feature.Counter\n    if (featureState instanceof Error) {\n      return (\n        \u003cdiv className=\"alert alert-danger\"\u003e\n          Failed to load counter: {featureState.message}\n        \u003c/div\u003e\n      )\n    } else if (!Comp) {\n      return \u003cdiv className=\"alert alert-info\"\u003eLoading counter...\u003c/div\u003e\n    }\n    return \u003cComp {...props} /\u003e\n  },\n})\n```\n\n#### serverSideRender.js\n\n```js\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { combineReducers, createStore, applyMiddleware } from 'redux'\nimport { connect, Provider } from 'react-redux'\nimport {\n  loadFeatureMiddleware,\n  featureMiddlewaresMiddleware,\n  addFeature,\n  LOAD_FEATURE,\n} from 'redux-features'\nimport reducer from './reducer'\nimport counterFeature from './counterFeature'\nimport Counter from './Counter'\n\nasync function serverSideRender(request, response) {\n  const featurePromises = []\n\n  const store = createStore(\n    reducer,\n    applyMiddleware(\n      (store) =\u003e (next) =\u003e (action) =\u003e {\n        const result = next(action)\n        if (action.type === LOAD_FEATURE) featurePromises.push(result)\n        return result\n      },\n      loadFeatureMiddleware(),\n      featureMiddlewaresMiddleware()\n    )\n  )\n\n  store.dispatch(addFeature('counter', counterFeature))\n\n  const app = (\n    \u003cProvider store={store}\u003e\n      \u003cCounter /\u003e\n    \u003c/Provider\u003e\n  )\n  renderToString(app)\n  try {\n    await Promise.all(featurePromises)\n  } catch (error) {\n    response.status(500).send(error.stack)\n  }\n\n  const body = renderToString(app)\n\n  // get all state except features\n  const { features, ...initialState } = store.getState()\n\n  response.status(200).send(`\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cscript type=\"text/javascript\"\u003e\n      window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}\n    \u003c/script\u003e\n    \u003cscript src=\"/client.js\"\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"root\"\u003e${body}\u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e`)\n}\n```\n\n#### client.js\n\n```js\nimport React from 'react'\nimport { render } from 'react-dom'\nimport { createStore, applyMiddleware } from 'redux'\nimport { Provider } from 'react-redux'\nimport reducer from './reducer'\nimport Counter from './Counter'\nimport counterFeature from './counterFeature'\nimport {\n  loadFeatureMiddleware,\n  featureMiddlewaresMiddleware,\n  addFeature,\n  loadInitialFeatures,\n} from 'redux-features'\n\nconst store = createStore(\n  reducer,\n  window.__INITIAL_STATE__,\n  applyMiddleware(loadFeatureMiddleware(), featureMiddlewaresMiddleware())\n)\nstore.dispatch(addFeature(counterFeature))\nstore\n  .dispatch(loadInitialFeatures())\n  .then(() =\u003e\n    render(\n      \u003cProvider store={store}\u003e\n        \u003cCounter /\u003e\n      \u003c/Provider\u003e,\n      document.getElementById('root')\n    )\n  )\n  .catch((error) =\u003e\n    render(\n      \u003cdiv className=\"alert alert-danger\"\u003e\n        Failed to load some features: {error.message}\n      \u003c/div\u003e,\n      document.getElementById('root')\n    )\n  )\n```\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcoreio%2Fredux-features","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcoreio%2Fredux-features","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcoreio%2Fredux-features/lists"}