Rendering Relay on the server

NOTE: as of Relay v3.0.0 the server-side rendering performance de-optimization described below has been removed.  


If you’re using Relay and server-side rendering React components, you must optimize Relay for server-side rendering, or suffer a substantial performance cost.

TL;DR
You’ll need to configure babel-preset-fbjs to hoist its require calls — but only on the server.

We’ll get into more detail, but first, this graph illustrates why you need to do this:

graph shows improvement
40-50% decrease in server-side rendering time results from optimizing Relay (Relay 1.4.1/React 16.2.0)

The vertical line represents a deployment of a server-side optimized Relay. The light blue is the time spent in the node.js process, which is almost exclusively time spent in ReactDOMServer.renderToString. The dramatic drop in time spent in the node.js process is obvious. It went from an average of 110ms per request to 55ms (roughly). The performance difference is even more remarkable in synthetic benchmarks.

Client optimizations harm server-side rendering

The issue was introduced way back in Relay 0.9. The Relay team added an optimization to their babel configuration that improves performance on mobile devices. Unfortunately, it harms the server-side use-case. Since there are workarounds to solve the issue, the Relay team felt that it wasn’t something they needed to focus on.

Our workaround

At 1stdibs, we ran into this issue a little over a year ago and we’re working with the Relay team to come up with a solution that works for everyone. But in the meantime, here’s what we did to get around it.

The core of the problem is the configuration of babel-preset-fbjs. The inlineRequires option is set to true, which causes module require calls to be inlined. The fix is to have a build that sets the that option to false.

So, we forked Relay, updated the inlineRequires option to false, and published our fork to our private registry.

In our projects, we use the babel-plugin-module-resolver to alias react-relay, relay-runtime and react-relay/compat to the server-optimized version — but only in the server babel configuration (that last part is important since the server optimization is a de-optimization on the client).

The workaround is fairly simple, which is why the Relay team hasn’t offered a solution. But, you do need to maintain the fork when you upgrade and ensure that your projects are all configured to use the server-optimized fork on the server.

Many thanks to Kostas Manionis and Rob Richard for identifying and fixing this for our team at 1stdibs.