{"id":14977636,"url":"https://github.com/dekoruma/centarius","last_synced_at":"2025-07-28T19:32:18.983Z","repository":{"id":57195897,"uuid":"143600439","full_name":"Dekoruma/centarius","owner":"Dekoruma","description":"♥ Sweet React SSR for Everyone ♥","archived":false,"fork":false,"pushed_at":"2020-08-03T15:05:54.000Z","size":297,"stargazers_count":11,"open_issues_count":0,"forks_count":3,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-05-08T01:34:17.640Z","etag":null,"topics":["afterjs","code-splitting","next","nextjs","react","react-router","server-side-rendering","ssr"],"latest_commit_sha":null,"homepage":null,"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/Dekoruma.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}},"created_at":"2018-08-05T09:40:22.000Z","updated_at":"2023-11-07T12:49:20.000Z","dependencies_parsed_at":"2022-09-16T12:12:39.229Z","dependency_job_id":null,"html_url":"https://github.com/Dekoruma/centarius","commit_stats":null,"previous_names":["rayandrews/centarius"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dekoruma%2Fcentarius","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dekoruma%2Fcentarius/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dekoruma%2Fcentarius/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dekoruma%2Fcentarius/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Dekoruma","download_url":"https://codeload.github.com/Dekoruma/centarius/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227949316,"owners_count":17846114,"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","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":["afterjs","code-splitting","next","nextjs","react","react-router","server-side-rendering","ssr"],"created_at":"2024-09-24T13:56:02.666Z","updated_at":"2024-12-03T15:43:26.640Z","avatar_url":"https://github.com/Dekoruma.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  Centarius\n  \u003cbr /\u003e\n  \u003cbr /\u003e\n  \u003ca align=\"center\" href=\"https://nodei.co/npm/centarius/\"\u003e\u003cimg align=\"center\" src=\"https://nodei.co/npm/centarius.png?downloads=true\u0026downloadRank=true\u0026stars=true\" /\u003e\u003c/a\u003e\n\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003e\n  \u003cbr\u003e\n    Like \u003ca href=\"https://github.com/jaredpalmer/after.js\"\u003eAfterJS\u003c/a\u003e but more customizable\n  \u003cbr /\u003e\n  \u003cbr /\u003e\n    Inspired from \u003ca href=\"https://github.com/jaredpalmer/after.js\"\u003eAfterJS\u003c/a\u003e and \u003ca href=\"https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config\"\u003eReact Router Config\u003c/a\u003e\n  \u003cbr /\u003e\n  \u003cbr /\u003e\n\u003c/h3\u003e\n\n## Getting started with Centarius\n\n__Centarius__ has same API as After.js.\n\nIf you have familiarize yourself with After, then you are not finding it difficult to migrate to __Centarius__.\n\n__Also :__ You can build it on your SSR boilerplate (either it webpack, parcel, etc).\n\n__Centarius__ is just another component wrapper to ease React SSR.\n\n### Quickstart with Razzle\n\n```bash\ncurl https://codeload.github.com/rayandrews/centarius/tar.gz/master | tar -xz --strip=2 centarius-master/examples/basic\ncd basic\n```\n\n---\n\n## Background\n\n[AfterJS](https://github.com/jaredpalmer/after.js) is awesome library but it has some drawbacks that I found it difficult to modify it in my other projects such as :\n\n* Routes' config only one level depth, and\n\n* Not able to modify routes config as we wish, imagine you are building complex application's routes. Sometimes just map over through your routes' config and get initial props are not enough.\n\n* Some routes are using same logical needs. We need strategy for providing it.\n\n\u003e We need to adopt and a little bit to modify [React Router Config](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config) strategy in our apps, to make more complex and declarative application based on routes config\n\n* Not able to modify static method for get initial props (getInitialProps is good, but you should be able to modify the name based on your content)\n\n* Not able to handle loading or error state while transitioning and getting initial props for other route\n\n---\n\n**Table of Contents**\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [How Centarius Works](#how-centarius-works)\n- [Data Fetching](#data-fetching)\n  - [`getInitialProps | { name } : (ctx) =\u003e object`](#getinitialprops---name---ctx--object)\n  - [Injected Context Data](#injected-context-data)\n- [Routing](#routing)\n- [Custom Options](#custom-options)\n- [Code Splitting](#code-splitting)\n- [Custom Document](#custom-document)\n- [Custom/Async Rendering](#customasync-rendering)\n- [Authors](#authors)\n- [Special Thanks](#special-thanks)\n- [Inspirations](#inspirations)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n---\n\n## How Centarius Works\n\n__Centarius__ will read through your routes' config to find component that you've already specified.\n\n## Data Fetching\n\nIn all components that you want to passed the initial data, you can add a `static async getInitialProps` or `another function's name that exactly does the same`.\n\nThis will be called on both initial server render and while transitioning between routes.\n\n\u003ch4\u003e\n  Results are made available on the props\n  \u003cbr /\u003e\n  \u003cbr /\u003e\n\u003c/h4\u003e\n\n```js\n// Home.js\nimport React from 'react';\nimport { NavLink } from 'react-router-dom';\n\nclass Home extends React.Component {\n  static async getInitialProps({ req, res, match }) {\n    const stuff = await CallMyApi();\n    return { stuff }; // returned value from static method not passed on props by default\n  }\n\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cNavLink to=\"/about\"\u003eAbout\u003c/NavLink\u003e\n        \u003ch1\u003eHome\u003c/h1\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\nexport default Home;\n```\n\n### `getInitialProps | { name } : (ctx) =\u003e object`\n\nWithin `getInitialProps` or `another function name`, you will get access to all you need to fetch data on both\nthe client and the server (same like After)\n\n* `req?: Request object`: (server-only) A Express.js request object\n* `res?: Request object`: (server-only) An Express.js response object\n* `match: object`: React Router 4's `match` object.\n* `history: object`: React Router 4's `history` object.\n* `location: object`: (client-only) React Router 4's `location` object.\n* `isServer: boolean`: Check whether code is running on server or client\n* `query: object`: Parsed query string from url\n* `params: object`: Parsed param object from React Router\n\n__You can also add another variable to be passed into static method like Redux Store, etc.__\n\n\u003e If you are using some server only modules inside `getInitialProps` or `anoher function name`, make sure to [import them properly](https://arunoda.me/blog/ssr-and-server-only-modules).\n\u003e Otherwise, it'll slow down your app.\n\n_Taken from [Next](https://github.com/zeit/next.js)_\n\n### Injected Page Props\n\n* Whatever you have returned in `getInitialProps`\n* `prefetch: (pathname: string) =\u003e void` - Imperatively prefetch _and cache_ data for a path.\n\n```js\n// Home.js\nimport React from 'react';\nimport { NavLink } from 'react-router-dom';\nimport { CentariusConsumer } from 'centarius/core';\n\nclass Home extends React.Component {\n  static async getInitialProps({ req, res, match }) {\n    const stuff = await CallMyApi();\n    return { stuff };\n  }\n\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cNavLink to=\"/about\"\u003eAbout\u003c/NavLink\u003e\n        \u003ch1\u003eHome\u003c/h1\u003e\n        \u003cdiv\u003e{this.props.stuff || ''}\u003c/div\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\nexport default Home;\n```\n\n## Routing\n\nReact Router 4 is used in all over __Centarius__ API.\n\n### Parameterized Routing\n\n```js\n// ./src/routes.js\nimport Home from './Home';\nimport About from './About';\nimport Counter from './Counter';\n\n// Internally these will become:\n// \u003cRoute path={path} exact={exact} render={props =\u003e \u003ccomponent {...props} data={data} /\u003e} /\u003e\nconst routes = [\n  {\n    path: '/',\n    exact: true,\n    component: Home,\n  },\n  {\n    path: '/about',\n    component: About,\n  },\n  {\n    path: '/counter/:count',\n    component: Counter,\n  },\n];\n\nexport default routes;\n```\n\n### Custom Route Component\n\nSometimes you need to modify the route component for your needs such as Protected Route to handle your  system's authentication. Centarius provides you with a simple solution for this by using attribute routerComponent in your routes config\n\n```js\n// ./src/routes.js\nimport Home from './Home';\nimport User from './User';\nimport About from './About';\nimport Counter from './Counter';\n\nimport ProtectedRoute from './ProtectedRoute';\n\nconst routes = [\n  {\n    path: '/',\n    exact: true,\n    component: Home,\n  },\n  {\n    path: '/user/:username',\n    routeComponent: ProtectedRoute,\n    component: User,\n    ...rest\n\n    // Internally these will become:\n    // \u003cProtectedRoute path={path} exact={exact} render={props =\u003e \u003ccomponent {...props} data={data} /\u003e} {...rest } /\u003e\n  },\n  {\n    path: '/about',\n    component: About,\n  },\n  {\n    path: '/counter/:count',\n    component: Counter,\n  },\n];\n\nexport default routes;\n```\n\n### Nested Route\n\nSometimes you need to nested your routes to handle so many things.\n\n---\n\nHowever, you need to make __parent component render children component.__\n\n__TL;DR : path will be concatted recursively from parent routes__\n\n```js\n{\n  path: '/user',\n  exact: true,\n  routeComponent: ProtectedRoute,\n  component: User,\n  routes: [\n    {\n      path: '/:username',\n      component: UserDetail,\n      exact: true,\n      routes: [\n        {\n          path: '/edit',\n          component: UserEdit,\n        }\n      ]\n    },\n  ],\n  ...rest\n}\n```\n\n__That code will make `/user/:username` render `UserDetail` component and `user/:username/edit` will render `UserEdit` component.__\n\n---\n\n```js\n// ./src/routes.js\nimport Home from './Home';\n\nimport User from './User';\nimport UserDetail from './UserDetail';\n\nimport About from './About';\nimport Counter from './Counter';\n\nimport ProtectedRoute from './ProtectedRoute';\n\nconst routes = [\n  {\n    path: '/',\n    exact: true,\n    component: Home,\n  },\n  {\n    path: '/user',\n    exact: true,\n    routeComponent: ProtectedRoute,\n    component: User,\n    routes: [\n      {\n        path: '/:username',\n        component: UserDetail,\n      },\n    ],\n    ...rest\n\n    // Internally these will become:\n    //\n    // \u003cProtectedRoute\n    //    path=\"/user\"\n    //    exact\n    //    render={props =\u003e\n    //      \u003cUser {...props} data={data}\u003e\n    //        \u003cRoute\n    //          path=\"/user/:username\"\n    //          render={childrenProps =\u003e\n    //            \u003cUserDetail {...props} data={data} /\u003e\n    //          }\n    //        /\u003e\n    //      \u003c/User\u003e\n    //    }\n    //    {...rest }\n    //  /\u003e\n  },\n  {\n    path: '/about',\n    component: About,\n  },\n  {\n    path: '/counter/:count',\n    component: Counter,\n  },\n];\n\nexport default routes;\n```\n\n\n## Custom Options\n\n\u003cp\u003e\n  \u003cdetails\u003e\n    \u003csummary\u003e\n      \u003cb\u003eExamples\u003c/b\u003e\n    \u003c/summary\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-loadable-components\"\u003eRedux RNW React Loadable\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-loadable-components\"\u003eRedux RNW Loadable Components\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/details\u003e\n\u003c/p\u003e\n\n__Centarius__ has default options as follows\n\n```js\n{\n  document: React.Component\u003cany, any\u003e = DefaultCentariusDocument,\n  staticMethod: string = 'getInitialProps',\n  rootId: string = 'root',\n  dataId: string = 'server-app-state',\n  isServer: boolean,\n\n  routes: Array\u003cRouteObject\u003e = [], // override this!\n}\n```\n\n\u003ch3\u003eIf you want to change static method, rootId, and dataId, you must pass it both in client and server\u003c/h3\u003e\n\nExample\n\n`Centarius : ({ routes, data, options, beforeNavigating, afterNavigating }) =\u003e React.Component\u003cany, any\u003e`\n\n* `routes: array[]`: Routes config\n* `data: object`: Initial data for Centarius\n* `options: object`: Centarius custom options\n* `beforeNavigating: () =\u003e void`: (client-only) Function that runs before navigating between route\n* `afterNavigating: ()=\u003e void`: (client-only) Function that runs after navigating between route\n\n```js\n// client.js\n\nimport React from 'react';\nimport { hydrate } from 'react-dom';\nimport { BrowserRouter } from 'react-router-dom';\nimport './client.css';\n\nimport { Centarius } from 'centarius/core';\nimport { getSsrData } from 'centarius/client';\nimport routes from './routes';\n\nconst data = getSsrData();\nconst options = {\n  staticMethod: 'fetchData', // * change the method to make client can preload data\n\n  // Anything else you add here will be made available\n  // within static method in client\n  // e.g a redux store, etc.\n}\n\nhydrate(\n  \u003cBrowserRouter\u003e\n    \u003cCentarius routes={routes} data={data} options={options} /\u003e\n  \u003c/BrowserRouter\u003e,\n  document.getElementById('root')\n);\n\nif (module.hot) {\n  module.hot.accept();\n}\n```\n\n`render : (options: object) =\u003e html : string`\n\n```js\n// server.js\n\nimport express from 'express';\nimport { render } from 'centarius/server';\n\nimport routes from './routes';\n\nconst assets = require(process.env.RAZZLE_ASSETS_MANIFEST);\n\nconst server = express();\nserver\n  .disable('x-powered-by')\n  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))\n  .get('/*', async (req, res) =\u003e {\n    const routerContext = {};\n\n    if (req.url.match(/.map$/)) return;\n\n    try {\n      const html = await render({\n        req,\n        res,\n        routes,\n        assets,\n        staticMethod: 'fetchData',\n        customThing: 'thing',\n\n        // Anything else you add here will be made available\n        // within static method in server\n        // e.g a redux store, etc.\n      });\n      res.send(html);\n    } catch (error) {\n      res.json(error);\n    }\n  });\n\nexport default server;\n```\n\n## Code Splitting\n\n\u003cp\u003e\n  \u003cdetails\u003e\n    \u003csummary\u003e\n      \u003cb\u003eExamples\u003c/b\u003e\n    \u003c/summary\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-loadable-components\"\u003eLoadable Components with React Native Web and Redux\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-react-loadable\"\u003eReact Loadable with React Native Web and Redux\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/details\u003e\n\u003c/p\u003e\n\n__Centarius__ does not defining any code splitting method like After, Next, or Rogue (with loadable-components) did.\n\n\u003e But __Centarius__ does enforce you to implement code splitting with other libraries\n\nWith the right custom routes config, you can implement it with another React code splitting library out there such as\n\n* [React Loadable](https://github.com/jamiebuilds/react-loadable)\n* [Loadable Components](https://github.com/smooth-code/loadable-components)\n\n\u003e Currently, Centarius only suppors code splitting library that has static method [load | preload] that return component and also hoisting static method such as getInitialProps after it has been loaded.\n\n## Custom Document\n\n\u003cp\u003e\n  \u003cdetails\u003e\n    \u003csummary\u003e\n      \u003cb\u003eExamples\u003c/b\u003e\n    \u003c/summary\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"./examples/basic-rnw\"\u003eBasic with React Native Web\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-loadable-components\"\u003eLoadable Components with React Native Web and Redux\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-react-loadable\"\u003eReact Loadable with React Native Web and Redux\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/details\u003e\n\u003c/p\u003e\n\nCentarius works like After and Next, you can override any html structure that suitable for your needs.\n\n**_Why we need it?_**\n\n__Centarius__ does not support React Helmet by default, you must add it on your document and custom render.\n\nIt really helps if you want to add CSS or other component with side-effects (React Helmet, etc) that needs custom document structure.\n\n__Example with React Helmet and React Native Web__\n\n```js\n// document.js\n\nimport React, { Component } from 'react';\nimport { AppRegistry } from 'react-native';\nimport { renderToStaticMarkup } from 'react-dom/server';\n\nimport { CentariusRoot, CentariusData } from 'centarius/document';\n\n/* eslint-disable */\n\nexport default class CustomDocument extends Component {\n  static async getInitialProps({ assets, data, renderPage }) {\n    const page = await renderPage();\n\n    return { assets, data, ...page };\n  }\n\n  render() {\n    const {\n      rootId,\n      dataId,\n      data,\n\n      // we passed it via custom renderer\n      assets,\n      helmet,\n      rnwCss,\n    } = this.props;\n\n    const htmlAttrs = helmet.htmlAttributes.toComponent();\n    const bodyAttrs = helmet.bodyAttributes.toComponent();\n\n    return (\n      \u003chtml lang=\"en\" {...htmlAttrs}\u003e\n        \u003chead\u003e\n          \u003cmeta httpEquiv=\"X-UA-Compatible\" content=\"IE=edge\" /\u003e\n          \u003cmeta charSet=\"utf-8\" /\u003e\n          \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n          {helmet.title.toComponent()}\n          {helmet.meta.toComponent()}\n          {helmet.link.toComponent()}\n          {assets.client.css \u0026\u0026 (\n            \u003clink rel=\"stylesheet\" href={assets.client.css} /\u003e\n          )}\n          \u003cstyle\n            id=\"react-native-stylesheet\"\n            dangerouslySetInnerHTML={{\n              __html: rnwCss\n                .replace(/\u003c\\/style\u003e/g, '')\n                .replace(/\u003cstyle id=\"react-native-stylesheet\"\u003e/g, ''),\n            }}\n          /\u003e\n        \u003c/head\u003e\n        \u003cbody {...bodyAttrs}\u003e\n          \u003cCentariusRoot id={rootId} /\u003e\n          \u003cCentariusData id={dataId} data={data} /\u003e\n          \u003cscript\n            type=\"text/javascript\"\n            src={assets.client.js}\n            crossOrigin=\"anonymous\"\n          /\u003e\n        \u003c/body\u003e\n      \u003c/html\u003e\n    );\n  }\n}\n```\n\n```js\n// server.js\n\nimport express from 'express';\n\nimport React, { Fragment } from 'react';\nimport { renderToString, renderToStaticMarkup } from 'react-dom/server';\nimport { AppRegistry } from 'react-native';\nimport Helmet from 'react-helmet';\n\nimport { render } from 'centarius/server';\n\nimport document from './document';\n\nimport routes from './routes';\n\nconst assets = require(process.env.RAZZLE_ASSETS_MANIFEST);\n\nconst server = express();\nserver\n  .disable('x-powered-by')\n  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))\n  .get('/*', async (req, res) =\u003e {\n    if (req.url.match(/.map$/)) return;\n\n    try {\n      const customRenderer = async (node) =\u003e {\n        const helmet = Helmet.renderStatic();\n\n        const CustomApp = () =\u003e \u003cFragment\u003e{node}\u003c/Fragment\u003e;\n\n        AppRegistry.registerComponent('App', () =\u003e CustomApp);\n\n        const { element, getStyleElement } = AppRegistry.getApplication(\n          'App',\n          {}\n        );\n\n        return {\n          helmet,\n          rnwCss: renderToStaticMarkup(getStyleElement()),\n          html: renderToString(element),\n        };\n      };\n\n      const html = await render({\n        req,\n        res,\n        routes,\n        assets,\n        document,\n        customRenderer,\n        customThing: 'thing',\n      });\n\n      if (res.finished) return;\n\n      res.send(html);\n    } catch (error) {\n      res.status(500);\n      res.send(error.stack);\n    }\n  });\n\nexport default server;\n```\n\nIf you were using something like `styled-components`, and you need to wrap you entire app with some sort of additional provider or function, you can do this with `renderPage()`.\n\n_Taken from [After](https://github.com/jaredpalmer/after.js)_\n\n```js\n// Document.js\nimport React, { Component } from 'react';\nimport { ServerStyleSheet } from 'styled-components'\nimport { renderToStaticMarkup } from 'react-dom/server';\n\nimport { CentariusRoot, CentariusData } from 'centarius/document';\n\nexport default class CustomDocument extends Component {\n  static async getInitialProps({ assets, data, renderPage }) {\n    const sheet = new ServerStyleSheet();\n    const page = await renderPage(App =\u003e props =\u003e sheet.collectStyles(\u003cApp {...props} /\u003e));\n    const styleTags = sheet.getStyleElement();\n    return { assets, data, ...page, styleTags };\n  }\n\n  render() {\n    const {\n      rootId,\n      dataId,\n      helmet,\n      assets,\n      data,\n      styleTags,\n    } = this.props;\n\n    return (\n      \u003chtml lang=\"en\"\u003e\n        \u003chead\u003e\n          \u003cmeta httpEquiv=\"X-UA-Compatible\" content=\"IE=edge\" /\u003e\n          \u003cmeta charSet=\"utf-8\" /\u003e\n          \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n          {styleTags}\n        \u003c/head\u003e\n        \u003cbody\u003e\n          \u003cCentariusRoot id={rootId} /\u003e\n          \u003cCentariusData id={dataId} data={data} /\u003e\n          \u003cscript\n            type=\"text/javascript\"\n            src={assets.client.js}\n            defer\n            crossOrigin=\"anonymous\"\n          /\u003e\n        \u003c/body\u003e\n      \u003c/html\u003e\n    );\n  }\n```\n\n__To use custom document, you need to pass it on server file__\n\n```js\n// server.js\n\nimport express from 'express';\nimport { render } from 'centarius/server';\n\nimport routes from './routes';\nimport Doc from './Document';\n\nconst assets = require(process.env.RAZZLE_ASSETS_MANIFEST);\n\nconst server = express();\nserver\n  .disable('x-powered-by')\n  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))\n  .get('/*', async (req, res) =\u003e {\n    if (req.url.match(/.map$/)) return;\n\n    try {\n      const html = await render({\n        req,\n        res,\n        assets,\n        staticMethod: 'fetchData',\n        customThing: 'thing',\n        document: Doc,\n        // Anything else you add here will be made available\n        // within static method in server\n        // e.g a redux store, etc.\n      });\n      res.send(html);\n    } catch (error) {\n      res.json(error);\n    }\n  });\n\nexport default server;\n```\n\n## Custom/Async Rendering\n\n\u003cp\u003e\n  \u003cdetails\u003e\n    \u003csummary\u003e\n      \u003cb\u003eExamples\u003c/b\u003e\n    \u003c/summary\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-loadable-components\"\u003eLoadable Components with React Native Web and Redux\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"./examples/redux-rnw-react-loadable\"\u003eReact Loadable with React Native Web and Redux\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/details\u003e\n\u003c/p\u003e\n\nYou can provide a custom (potentially async) rendering function as an option to Centarius `render` function, just like After.js.\n\nIf it presents, it will be used instead of the default ReactDOMServer renderToString function.\n\nIt has to return an object of shape `{ html : string!, ...otherProps }`, in which `html` will be used as the rendered string.\n\n`otherProps` will be passed as props to the rendered Document.\n\n```js\ndefaultRenderer = (node) =\u003e ({ html: ReactDOMServer.renderToString(node) })\n```\n\nExample\n\n```js\n\n// server.js\n\nimport express from 'express';\nimport { render } from 'centarius/server';\n\nimport { Capture } from 'react-loadable';\nimport { getBundles } from 'react-loadable/webpack';\n\nimport stats from 'build/react-loadable.json';\n\nimport configureStore from 'store/configureStore';\n\nimport routes from './routes';\nimport Doc from './Document';\n\nconst assets = require(process.env.RAZZLE_ASSETS_MANIFEST);\n\nconst server = express();\nserver\n  .disable('x-powered-by')\n  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))\n  .get('/*', async (req, res) =\u003e {\n    if (req.url.match(/.map$/)) return;\n\n    try {\n      const preloadedState = {};\n      const store = configureStore(preloadedState);\n      const modules = [];\n\n      const customRenderer = (node) =\u003e {\n        const CustomApp = (\n          \u003cCapture report={(moduleName) =\u003e modules.push(moduleName)}\u003e\n            \u003cProvider store={store}\u003e{node}\u003c/Provider\u003e\n          \u003c/Capture\u003e\n        );\n\n        const bundles = getBundles(stats, modules);\n        const chunks = bundles.filter((bundle) =\u003e bundle.file.endsWith('.js'));\n\n        return {\n          chunks,\n          store, // notice that this will passed into document\n          html: renderToString(CustomApp),\n        };\n      };\n\n      const html = await render({\n        req,\n        res,\n        routes,\n        assets,\n        staticMethod: 'fetchData',\n        customThing: 'thing',\n        document: Doc,\n        store, // this will be passed in static method in server\n\n        // Anything else you add here will be made available\n        // within static method in server\n        // e.g a redux store, etc.\n      });\n      res.send(html);\n    } catch (error) {\n      res.json(error);\n    }\n  });\n\nexport default server;\n```\n\n## Packages / Plugins / Addons / HOCs\n\n| Package | Version | Dependencies | Description |\n|--------|:-------:|:------------:|-----------|\n| [`centarius`](/packages/centarius) | [![npm](https://img.shields.io/npm/v/centarius.svg?maxAge=86400)](https://www.npmjs.com/package/centarius) | [![Dependency Status](https://david-dm.org/rayandrews/centarius.svg?path=packages/centarius)](https://david-dm.org/rayandrews/centarius?path=packages/centarius) | Core package. _Required_ |\n| [`@centarius/state-hoc`](/packages/state-hoc) | [![npm](https://img.shields.io/npm/v/@centarius/state-hoc.svg?maxAge=86400)](https://www.npmjs.com/package/@centarius/state-hoc) | [![Dependency Status](https://david-dm.org/rayandrews/centarius.svg?path=packages/state-hoc)](https://david-dm.org/rayandrews/centarius?path=packages/state-hoc) | State HOC for Centarius |\n| [`@centarius/react-loadable`](/packages/react-loadable) | [![npm](https://img.shields.io/npm/v/@centarius/react-loadable.svg?maxAge=86400)](https://www.npmjs.com/package/@centarius/react-loadable) | [![Dependency Status](https://david-dm.org/rayandrews/centarius.svg?path=packages/react-loadable)](https://david-dm.org/rayandrews/centarius?path=packages/react-loadable) | React Loadable HOC for Centarius |\n\n---\n\n## Authors\n\n* Ray Andrew [@rayandrews](https://github.com/rayandrews)\n* Natan Elia [@natanelia](https://github.com/natanelia)\n\n---\n\n## Special Thanks\n\n* __Jared Palmer [@jaredpalmer](https://github.com/jaredpalmer) for After.JS__\n* Ivana Irene [@ivanaairenee](https://github.com/ivanaairenee)\n* Reinaldo Ignatius [@nimitz21](https://github.com/nimitz21)\n\n---\n\n## Inspirations\n\n* [Next.js](https://github.com/zeit/next.js)\n* [After.js](https://github.com/jaredpalmer/after.js)\n* [Rogue.js](https://github.com/alidcastano/rogue.js)\n* [Razzle](https://github.com/jaredpalmer/razzle)\n\n---\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdekoruma%2Fcentarius","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdekoruma%2Fcentarius","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdekoruma%2Fcentarius/lists"}