- a react render engine for Universal (previously Isomorphic) JavaScript apps written with express
- renders both plain react views and optionally react-router views
- enables server rendered views to be client mountable
# In your express app, react-engine needs to be installed along
# side react and optionally react-router (+ history, react-router's dependency)
npm install react-engine react react-router history --save
var Express = require('express');
var ReactEngine = require('react-engine');
var app = Express();
// create an engine instance
var engine = ReactEngine.server.create({
/*
see the complete server options spec here:
https://github.com/paypal/react-engine#server-options-spec
*/
});
// set the engine
app.engine('.jsx', engine);
// set the view directory
app.set('views', __dirname + '/views');
// set jsx or js as the view engine
// (without this you would need to supply the extension to res.render())
// ex: res.render('index.jsx') instead of just res.render('index').
app.set('view engine', 'jsx');
// finally, set the custom view
app.set('view', require('react-engine/lib/expressView'));
Setup in a KrakenJS app's config.json
{
"express": {
"view engine": "jsx",
"view": "require:react-engine/lib/expressView",
},
"view engines": {
"jsx": {
"module": "react-engine/lib/server",
"renderer": {
"method": "create",
"arguments": [{
/*
see the complete server options spec here:
https://github.com/paypal/react-engine#server-options-spec
*/
}]
}
}
}
}
Pass in an optional JavaScript object as options to the react-engine's server engine create method. The options object can contain properties from react router's create configuration object.
Additionally, it can contain the following optional properties,
docType
: <String> - a string that can be used as a doctype (Default:<!DOCTYPE html>
). (docType might not make sense if you are rendering partials/sub page components, in that case you can pass an empty string as docType)routesFilePath
: <String> - path for the file that contains the react router routes. react-engine uses this behind the scenes to reload the routes file in cases where express's app propertyview 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.renderOptionsKeysToFilter
: <Array> - an array of keys that need to be filtered out from the data object that gets fed into the react component for rendering. more infoperformanceCollector
: <Function> - to collects perf stats
var data = {}; // your data model
// for a simple react view rendering
res.render(viewName, data);
// for react-router rendering
// pass in the `url` and react-engine
// will run the react-router behind the scenes.
res.render(req.url, data);
// assuming we use `browserify`
var client = require('react-engine').client;
// finally, boot whenever your app is ready
// example:
document.addEventListener('DOMContentLoaded', function onLoad() {
// `onBoot` - Function (optional)
// returns data that was used
// during rendering as the first argument
// the second argument is the `router` created behind the scenes
// (only available while using react-router)
client.boot(/* client options object */, function onBoot(data, router) {
});
};
// if the data is needed before booting on
// client, call `data` function anytime to get it.
// example:
var data = client.data();
Pass in a JavaScript object as options to the react-engine's client boot function. The options object can contain properties from react router's create configuration object.
Additionally, it can contain the following properties,
viewResolver
: required - Function - a function that react-engine needs to resolve the view file. an example of the viewResolver can be found here.mountNode
: optional - HTMLDOMNode - supply a HTML DOM Node to mount the server rendered component in the case of partial/non-full page rendering.
The actual data that gets fed into the component for rendering is the renderOptions
object that express generates.
If you don't want to pass all that data, you can pass in an array of object keys that react-engine can filter out from the renderOptions
object before passing it into the component for rendering.
// example of using `renderOptionsKeysToFilter` to filter `renderOptions` keys
var engine = ReactEngine.server.create({
renderOptionsKeysToFilter: ['mySensitiveDataThatIsIn_res.locals'],
routes: require(path.join(__dirname + './reactRoutes'))
});
Note: By default, the following three keys are always filtered out from renderOptions
no matter whether renderOptionsKeysToFilter
is configured or not.
settings
enrouten
_locals
While 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.
To 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:
Error Type | Description |
---|---|
MATCH_REDIRECT** | indicates that the url matched to a redirection |
MATCH_NOT_FOUND | indicates that the url did not match to any component |
MATCH_INTERNAL_ERROR | indicates that react-router encountered an internal error |
** for MATCH_REDIRECT error, redirectLocation
property of the err has the new redirection location
// example express error middleware
app.use(function(err, req, res, next) {
console.error(err);
// http://expressjs.com/en/guide/error-handling.html
if (res.headersSent) {
return next(err);
}
if (err._type && err._type === ReactEngine.reactRouterServerErrors.MATCH_REDIRECT) {
return res.redirect(302, err.redirectLocation);
}
else if (err._type && err._type === ReactEngine.reactRouterServerErrors.MATCH_NOT_FOUND) {
return res.status(404).send('Route Not Found!');
}
else {
// for ReactEngine.reactRouterServerErrors.MATCH_INTERNAL_ERROR or
// any other error we just send the error message back
return res.status(500).send(err.message);
}
});
There is a Yeoman generator available to create a new express or KrakenJS application which uses react-engine: generator-react-engine.
Pass in a function to the performanceCollector
property to collect the stats
object for every render.
The object that contains the stats info for each render by react-engine. It has the below properties.
name
- Name of the template or the url in case of react router rendering.startTime
- The start time of render.endTime
- The completion time of render.duration
- The duration taken to render (in milliseconds).
// example
function collector(stats) {
console.log(stats);
}
var engine = require('react-engine').server.create({
routes: './routes.jsx'
performanceCollector: collector
});
- On the client side, the state is exposed on the window object's property
__REACT_ENGINE__
- 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. - You can use
js
as the engine if you decide not to write your react views injsx
. - Blog on react-engine
While upgrading to 3.x version of react-engine, make sure to follow the react-router's 1.x upgrade guide to upgrade react-router related code in your app. Then, add to your express error middleware, react-engine's MATCH_REDIRECT and MATCH_NOT_FOUND checks.
2.x version of react-engine brought in a major api change. Basically it affects the property names of the object that gets passed in during the engine creation on the server side and also how routes definition is passed into react-engine.
In v2.x, routes
need to be explicitly required and passed in to the engine creation method. Also, any react-router known properties can be passed in.
An example engine creation can be found here.