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:

  1. Improved performance for the customer
  2. 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.

Check out my post about the release of Electrode, the customer-facing platform that powers Walmart.com.

The Electrode website: www.electrode.io