Using Electrode to Improve React Server Side Render Performance By Up To 70%
We built Electrode, the react/node.js application platform that powers walmart.com with several goals in mind, including ease of use, re-usability of components across applications and, most importantly, performance.
We use server side rendering for almost all of our applications for two reasons:
- Improved performance for the customer
- Better for SEO
In our tests, however, we found that React’s renderToString()
takes quite a while to execute — and since renderToString()
is synchronous, the server is blocked while it runs. Every server side render executes renderToString()
to build the HTML that the application server will be sending to the browser.
To solve this problem, we created two Electrode modules: Above the Fold Render (ATF) and Server Side Render Profiling and Caching (SSR Caching). Let’s take a look at how these work, using the Walmart.com homepage application as our use case. This is the real homepage application, not a mocked app. It includes 10 carousels (lots of deals!), and it already uses our performance-enhancing redux-router-engine. For the purpose of testing, it’s running on a Macbook Pro, not on commodity server hardware (this makes a big difference in renderToString()
performance).
Baseline
As a baseline test, we first configured the application with no ATF module, no SSR Caching module, and with renderWithIds:true
, and we ran it 30 times synchronusly. The average renderToString()
call with this configuration took 153.80 ms.
Electrode Default — renderWithIds:False
By default, though, Electrode comes with renderWithIds:false
, which gives us a renderToString()
time of 124.80 ms. Not bad — Electrode’s default configuration has already improved render time by 19%!
Server Side Render Profile and Caching
Next we added SSR Caching of the header, footer, and carousels, which gave us an average renderToString()
of 96.80 ms. Very nice! That’s a 23% improvement from the Electrode default.
Above The Fold Render
Let’s say we didn’t implement the caching though, and did an ATF Rendering-only render. That brings our renderToString()
time to 65.73 ms. Wow — a 48% reduction! That means a lot of those carousels were below the fold.
SSR Caching + ATF Render
Finally, let’s use SSR Caching and ATF rendering together. That drops our renderToString()
time all the way to 36.56 ms — an astounding 71% improvement from the default Electrode configuration, and a 76% improvement from our original, unoptimized test. Remember, too, that this is synchronous code, which means it stops your Node.js application from doing anything else until it’s finished. We haven’t brought DOMContentLoaded()
into the conversation, but with ATF Rendering, we see an almost 30% improvement because we’re only rendering content above the fold, then immediately kicking off React rendering on the client side.
Conclusion
There you have it, Electrode is awesome and you should use it. Improve your react server side performance by up to 70%. We are using this @WalmartLabs and have been seeing massive performance gains on our production apps with these two modules (+ redux router engine), as you can see, home page saw a 70% improvement on local.
But keep in mind that these tests were ran on fast hardware. On commodity server CPUs, renderToString()
can take twice as long. That means a 70% improvement won’t mean 90 ms, but 180 ms saved.
Special Thanks:
To Arunesh Joshi, Dmitry Yesin, and the home page team who have implemented the Electrode modules and are using them on the home page in production.
To Caoyang Shi, for helping to gather data and ensuring we’re getting the right numbers!
More Information:
Check out Joel Chen’s post on ReactJS SSR Profiling and Caching or Arpan Nanavati’s post on Building React.js at Enterprise Scale.
The Electrode website: www.electrode.io