{"id":13527269,"url":"https://github.com/paypal/react-engine","last_synced_at":"2025-09-28T23:32:21.754Z","repository":{"id":30143288,"uuid":"33693426","full_name":"paypal/react-engine","owner":"paypal","description":"a composite render engine for universal (isomorphic) express apps to render both plain react views and react-router views","archived":true,"fork":false,"pushed_at":"2019-04-10T08:56:12.000Z","size":318,"stargazers_count":1452,"open_issues_count":26,"forks_count":154,"subscribers_count":64,"default_branch":"master","last_synced_at":"2024-05-13T04:02:51.969Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paypal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-09T21:21:08.000Z","updated_at":"2024-04-18T05:42:48.000Z","dependencies_parsed_at":"2022-08-22T15:20:52.239Z","dependency_job_id":null,"html_url":"https://github.com/paypal/react-engine","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paypal%2Freact-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paypal%2Freact-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paypal%2Freact-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paypal%2Freact-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paypal","download_url":"https://codeload.github.com/paypal/react-engine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234407442,"owners_count":18827627,"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":[],"created_at":"2024-08-01T06:01:44.505Z","updated_at":"2025-09-28T23:32:21.387Z","avatar_url":"https://github.com/paypal.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# react-engine\n\n[![Build Status](https://travis-ci.org/paypal/react-engine.svg?branch=master)](https://travis-ci.org/paypal/react-engine)\n\n### What is react-engine?\n* a react render engine for [Universal](https://medium.com/@mjackson/universal-javascript-4761051b7ae9) (previously [Isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/)) JavaScript apps written with express\n* renders both plain react views and **optionally** react-router views\n* enables server rendered views to be client mountable\n\n\n### Install\n```sh\n# In your express app, react-engine needs to be installed alongside react/react-dom (react-router is optional)\n$ npm install react-engine react react-dom react-router --save\n```\n\n### Usage On Server Side\n###### Setup in an Express app\n```javascript\nvar Express = require('express');\nvar ReactEngine = require('react-engine');\n\nvar app = Express();\n\n// create an engine instance\nvar engine = ReactEngine.server.create({\n  /*\n    see the complete server options spec here:\n    https://github.com/paypal/react-engine#server-options-spec\n  */\n});\n\n// set the engine\napp.engine('.jsx', engine);\n\n// set the view directory\napp.set('views', __dirname + '/views');\n\n// set jsx or js as the view engine\n// (without this you would need to supply the extension to res.render())\n// ex: res.render('index.jsx') instead of just res.render('index').\napp.set('view engine', 'jsx');\n\n// finally, set the custom view\napp.set('view', require('react-engine/lib/expressView'));\n```\n\n###### Setup in a [KrakenJS](http://krakenjs.com) app's config.json\n```json\n{\n  \"express\": {\n    \"view engine\": \"jsx\",\n    \"view\": \"require:react-engine/lib/expressView\",\n  },\n  \"view engines\": {\n    \"jsx\": {\n      \"module\": \"react-engine/lib/server\",\n      \"renderer\": {\n        \"method\": \"create\",\n        \"arguments\": [{\n          /*\n            see the complete server options spec here:\n            https://github.com/paypal/react-engine#server-options-spec\n          */\n        }]\n      }\n    }\n  }\n}\n```\n\n###### Server options spec\nPass in a JavaScript object as options to the react-engine's [server engine create method](#setup-in-an-express-app).\nThe options object should contain the mandatory `routes` property with the route definition.\n\nAdditionally, it can contain the following **optional** properties,\n\n- `docType`: \\\u003cString\u003e - a string that can be used as a doctype (_Default: `\u003c!DOCTYPE html\u003e`_).\n                        (docType might not make sense if you are rendering partials/sub page components, in that case you can pass an empty string as docType)\n- `routesFilePath`: \\\u003cString\u003e - path for the file that contains the react router routes.\n                   react-engine uses this behind the scenes to reload the routes file in\n                   cases where [express's app property](http://expressjs.com/api.html#app.set) `view cache` is false, this way you don't need to restart the server every time a change is made in the view files or routes file.\n- `renderOptionsKeysToFilter`: \\\u003cArray\u003e - an array of keys that need to be filtered out from the data object that gets fed into the react component for rendering. [more info](#data-for-component-rendering)\n- `performanceCollector`: \\\u003cFunction\u003e - to collects [perf stats](#performance-profiling)\n- `scriptLocation`: \\\u003cString\u003e - where in the HTML you want the client data (i.e. `\u003cscript\u003evar __REACT_ENGINE__ = ... \u003c/script\u003e`) to be appended (_Default: `body`_).\n                    If the value is undefined or set to `body` the script is placed before the `\u003c/body\u003e` tag.\n                    The only other value is `head` which appends the script before the `\u003c/head\u003e` tag.\n\n- `staticMarkup`: \\\u003cBoolean\u003e - a boolean that indicates if render components without React data attributes and client data. (_Default: `false`_). This is useful if you want to render simple static page, as stripping away the extra React attributes and client data can save lots of bytes.\n- `scriptType`: \\\u003cString\u003e - a string that can be used as the type for the script (if it is included, which is only if staticMarkup is false). (_Default: `application/json`_).\n\n###### Rendering views on server side\n```js\nvar data = {}; // your data model\n\n// for a simple react view rendering\nres.render(viewName, data);\n\n// for react-router rendering\n// pass in the `url` and react-engine\n// will run the react-router behind the scenes.\nres.render(req.url, data);\n```\n\n### Usage On Client Side (Mounting)\n```js\n// assuming we use a module bundler like `webpack` or `browserify`\nvar client = require('react-engine/lib/client');\n\n// finally, boot whenever your app is ready\n// example:\ndocument.addEventListener('DOMContentLoaded', function onLoad() {\n\n  // `onBoot` - Function (optional)\n  // returns data that was used\n  // during rendering as the first argument\n  // the second argument is the `history` object that was created behind the scenes\n  // (only available while using react-router)\n  client.boot(/* client options object */, function onBoot(data, history) {\n\n  });\n};\n\n// if the data is needed before booting on\n// client, call `data` function anytime to get it.\n// example:\nvar data = client.data();\n```\n\n###### Client options spec\nPass in a JavaScript object as options to the react-engine's client boot function.\nIt can contain the following properties,\n\n- `routes` : **required** - _Object_ - the route definition file.\n- `viewResolver` : **required** - _Function_ - a function that react-engine needs to resolve the view file.\n  an example of the viewResolver can be [found here](https://github.com/paypal/react-engine/blob/ecd27b30a9028d3f02b8f8e89d355bb5fc909de9/examples/simple/public/index.js#L29).\n- `mountNode` : **optional** - _HTMLDOMNode_ - supply a HTML DOM Node to mount the server rendered component in the case of partial/non-full page rendering.\n- `history` : **optional** - _Object_ - supply any custom history object to be used by the react-router.\n\n### Data for component rendering\nThe actual data that gets fed into the component for rendering is the `renderOptions` object that [express generates](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/lib/application.js#L535-L588).\n\nIf you don't want to pass all that data, you can pass in an array of object keys or dot-lookup paths that react-engine can filter out from the `renderOptions` object before passing it into the component for rendering.\n\n```javascript\n// example of using `renderOptionsKeysToFilter` to filter `renderOptions` keys\nvar engine = ReactEngine.server.create({\n  renderOptionsKeysToFilter: [\n    'mySensitiveData',\n    'somearrayAtIndex[3].deeply.nested'\n  ],\n  routes: require(path.join(__dirname + './reactRoutes'))\n});\n```\n\nNotes:\n - The strings `renderOptionsKeysToFilter` will be used with [lodash.unset](https://lodash.com/docs/#unset), so they can be either plain object keys for first-level properties of `renderOptions`, or dot-separated \"lookup paths\" as shown in the `lodash.unset` documentation. Use these lookup paths to filter out nested sub-properties.\n - By default, the following three keys are always filtered out from `renderOptions` no matter whether `renderOptionsKeysToFilter` is configured or not.\n    - `settings`\n    - `enrouten`\n    - `_locals`\n\n### Handling redirects and route not found errors on the server side\nWhile using react-router, it matches the url to a component based on the app's defined routes. react-engine captures the redirects and not-found cases that are encountered while trying to run the react-router's [match function on the server side](https://github.com/reactjs/react-router/blob/master/docs/guides/ServerRendering.md).\n\nTo handle the above during the lifecycle of a request, add an error type check in your express error middleware. The following are the three types of error that get thrown by react-engine:\n\nError Type           | Description   \n-------------------- | --------------------------------------------------------\nMATCH_REDIRECT**     | indicates that the url  matched to a redirection\nMATCH_NOT_FOUND      |  indicates that the url  did not match to any component     \nMATCH_INTERNAL_ERROR | indicates that react-router encountered an internal error\n\n _**  for MATCH_REDIRECT error, `redirectLocation` property of the err has the new redirection location_\n\n```javascript\n// example express error middleware\napp.use(function(err, req, res, next) {\n  console.error(err);\n\n  // http://expressjs.com/en/guide/error-handling.html\n  if (res.headersSent) {\n    return next(err);\n  }\n\n  if (err._type \u0026\u0026 err._type === ReactEngine.reactRouterServerErrors.MATCH_REDIRECT) {\n    return res.redirect(302, err.redirectLocation);\n  }\n  else if (err._type \u0026\u0026 err._type === ReactEngine.reactRouterServerErrors.MATCH_NOT_FOUND) {\n    return res.status(404).send('Route Not Found!');\n  }\n  else {\n    // for ReactEngine.reactRouterServerErrors.MATCH_INTERNAL_ERROR or\n    // any other error we just send the error message back\n    return res.status(500).send(err.message);\n  }\n});\n```\n\n### Yeoman Generator\nThere is a Yeoman generator available to create a new express or KrakenJS application which uses react-engine:\n[generator-react-engine](https://www.npmjs.com/package/generator-react-engine).\n\n### Performance Profiling\n\nPass in a function to the `performanceCollector` property to collect the `stats`\nobject for every render.\n\n##### `stats`\nThe object that contains the stats info for each render by react-engine.\nIt has the below properties.\n- `name` - Name of the template or the url in case of react router rendering.\n- `startTime` - The start time of render.\n- `endTime` - The completion time of render.\n- `duration` - The duration taken to render (in milliseconds).\n\n```js\n// example\nfunction collector(stats) {\n  console.log(stats);\n}\n\nvar engine = require('react-engine').server.create({\n  routes: './routes.jsx'\n  performanceCollector: collector\n});\n```\n\n### Notes\n* On the client side, the state is exposed in a script tag whose id is `react-engine-props`  \n* When Express's `view cache` app property is false (mostly in non-production environments), views are automatically reloaded before render. So there is no need to restart the server for seeing the changes.\n* You can use `js` as the engine if you decide not to write your react views in `jsx`.\n* [Blog on react-engine](https://www.paypal-engineering.com/2015/04/27/isomorphic-react-apps-with-react-engine/)\n* You can add [nonce](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#Unsafe_inline_script) in `_locals`, which will be added in `script` tag that gets injected into the server rendered pages, like `res.locals.nonce = 'nonce value'` \n\n\n### License\n[Apache Software License v2.0](http://www.apache.org/licenses/LICENSE-2.0)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaypal%2Freact-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaypal%2Freact-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaypal%2Freact-engine/lists"}