The Cost Of Client-side Rehydration

February 8, 2019

A common pattern in modern web-apps is server rendering a page and then rehydrating DOM client-side through a serialized version of a UI's dependencies. Rehydration can however come with a real cost. It can heavily delay Time To Interactive by making UI look ready before client-side processing has completed. Is painting pixels early worth it if it results in an Uncanny Valley for users?

Rehydration

Rehydration and potential solutions

Note: Jason Miller and I recently wrote up an article on rendering on the web with a focus on web performance. If you're interested in learning more about where rehydration checks in, do check it out!

Rehydration in isomorphic web apps involves re-generating state client-side used to render a page on the server. After rendering HTML, hydration is processing inline JSON state stores client-side (e.g Redux state) for data already in the page and attaching DOM event listeners for the experience.

function htmlTemplate(reactDOM, reduxState) {
    return `
        /* ... */
        <div id="app">${reactDOM}</div>
        <script>
            window.__PRELOADED_STATE__ = ${JSON.stringify(reduxState)}
        </script>
        <script src="./app.bundle.js"></script>
        /* ... */
    `;
}

Some solutions to the cost of rehydration are:

  • Avoid rehydration altogether (purely server render)
  • Load and hydrate components based on their visibility
  • Hydrate components as soon as users interact with them
  • Partial hydration (enable early interaction for some components, while others can use progressive enhancement to add more complexity after initial hydration)

I'm excited React is exploring partial hydration in core - enabling a user to start interacting with parts of the screen while others are still hydrating. Markus Oberlehner also has an excellent write-up on reducing time to interactive of SSR apps for Vue.js using the above concepts, like hydrating on user interaction.

Is (re)hydration always slow?

One possible takeaway is that server-rendering with (re)hydration is always slow. This is not always true depending on whether you are employing JavaScript as a strict progressive enhancement (i.e that pages are only interactive when all of your JavaScript bundle has been delivered.) There are strategies that can be employed for treating JS as an enhancement to bring Time to Interactive closer to First Contentful Paint (like next export in Next.js which generates static HTML).

We also didn't talk as much about CDN caching of server-rendered HTML. This can change the value proposition of the above idea of server-rendering with JavaScript as an enhancement. CDN caching of HTML can be both flexible and avoid high TTFB. As noted by Mike Sherov, this can lead to a potentially interesting solution of CDN cached server-rendered HTML with JavaScript as PE: streaming, fast TTFB, fast FCP and possibly fast TTI. Ultimately it depends on whether the engineering investment is worth it to your site.

Wrapping up

More research into limiting the costs of rehydration will ultimately lead to better experiences for end-users.