Improve Server-Side Rendering throughput with ESX
React is a hugely popular frontend framework that revolutionised the frontend development world.
As a Principal Architect and Consultant at NearForm, it has become painfully clear that React’s Server-Side Rendering (SSR) is a performance bottleneck for web backends around the world. ESX presents a simple solution that can be dropped into pre-existing React applications to significantly improve Server-Side rendering throughput.
This month I announced ESX at React Amsterdam. It’s primarily designed as a high-speed Server-Side Rendering engine for React that may be used with absolutely no manual modifications to your code base. Check out the repo at https://github.com/esxjs/esx.
There are many workarounds to React SSR overhead such as caching, streams, and snapshotting – all of which can be useful in different scenarios. However, ESX improves the rendering algorithm itself. This is extremely important when it comes to any dynamic SSR requirements such as feature flags, A/B testing, or content based on contextualized state. Not to mention SSR is also important for PWA perceived performance.
A template engine essentially injects variables into strings, this is quite efficient. On the other hand, React SSR creates an object for every HTML element, builds a tree representation of the DOM in memory and then converts that tree into a string. It does this every time a request is made. While it’s still under active development, ESX proves that we can take a template engine approach to React SSR for higher performance, lower overhead Server-Side Rendering.
In the small app microbenchmark a 15x speed improvement can be observed. But by nature, microbenchmarks are not necessarily indicative of real-world performance gains. Due to the multitude of architectural options in React applications, every case is different and ESX can improve speed to varying degrees. It may not increase the speed of your Server Side Rendering at all. In these cases SSR won’t be the bottleneck – it may be the CSS solution you’re using, it may be to do with a lot of serializing, it could be the HTTP fetching library you’re using, it could even be your logger. One of the easiest ways to figure out what your bottleneck is to use a flamegraph tool, such as 0x, or clinic-flame, but that’s a whole other subject.
The https://github.com/esxjs/esx-demo repo strives to represent a conventional React architecture with certain pieces (such as CSS and HTTP requests) stripped out to remove noise. You can peruse the React application here and view its optimized form here.
If we clone the repository, run
npm install and then run
npm run bench the benchmarking script will start a server for the unoptimized application and load test it with autocannon. Then it will do the same for the optimized application. The result should show approximately 75% lower latency, 325% more request per second and 330% higher throughput.
In future posts, I’ll explain the techniques used to achieve this gain in detail. For now, the aim of this post is to highlight the key piece of this optimization: tagged template literals. Not only can template literals be faster, but they’re also native syntax. It is my firm belief that if React were created today, it would use tagged template literals instead of JSX.
Almost the exact same syntax can be achieved:
To be clear, ESX does not enforce a requirement to use template literals directly. The JSX to ESX transpiler is currently experimental and work is ongoing but you have the choice between a babel plugin (babel-plugin-esx-ssr) or the built-in ESX preloader to convert all JSX and
React.createElement calls into ESX tagged template literals.
You can try the experimental preloader out today with the following command:
node -r esx/experimental-optimize app.js
In addition, you can also use ESX directly both on the server and in the browser. Depending on preference, this can be quite useful during development. While ESX works in the browser, it won’t be as efficient in the browser environment as calling
React.createElement. This is why a production transpiler for the browser is provided: babel-plugin-esx-browser. This converts ESX to
React. createElement. It’s the ESX equivalent of transpiling JSX. Of course, this won’t be needed if you stick with JSX and simply use the SSR babel plugin or preloader mentioned above.
If you’d like to get started with ESX there is an easy and straightforward way to minimize the risk: render with both React and ESX and diff the resulting HTML. This is both strongly advised and highly encouraged. Issues and PR’s are very welcome as we take ESX forward.
About the Author
With NearForm, David has engaged in performance engineering, architectural consultancy, brown-field technical leadership and project bootstrapping for a vast array of our clients. We help connect enterprises and users with builders and maintainers to help them adapt to a modern technology stack to drive faster lead-times for digital innovation.