DEV Community

Cover image for From Static to Interactive: Why Resumability is the Best Alternative to Hydration
Miško Hevery for Builder.io

Posted on • Edited on • Originally published at builder.io

From Static to Interactive: Why Resumability is the Best Alternative to Hydration

Original: https://www.builder.io/blog/from-static-to-interactive-why-resumability-is-the-best-alternative-to-hydration

No one wants a slow, unresponsive website. Prerendering your site is one of your most powerful tools as a web developer when it comes to website performance optimization.

Almost all front-end JavaScript (JS) frameworks have a way to prerender content as HTML through server side rendering (SSR) or static site generation (SSG). The result is that the site displays almost instantly as the HTML is streamed into your user’s browser.

However, there’s a problem. HTML is static whereas websites are usually dynamic and interactive. How do frameworks make pre-rendered HTML interactive on the browser?

The current generation of frameworks solves this through a client-side process called hydration, a resource-intensive process that adds significant overhead to the page’s startup cost.

Here’s my controversial opinion: hydration is what happens when you add SSR/SSG as an afterthought to a front-end framework.

A framework designed from the ground up for prerendering can avoid hydration and its performance penalty by serializing state on the server and resuming it on the client.

Before diving into serialization and resumability, let’s talk about the problem that hydration solves.

How does a framework make a page interactive?

Any front-end framework needs three things to be able to respond to interactivity:

Associating event handlers: The framework must have a way to associate DOM elements with their corresponding event handlers, which are the functions that respond to user input and enable interaction with your website.
Recovering application state: Once a user event triggers an event handler, the function updates application state.
Recreating the component hierarchy: Once the application state updates, the framework needs to re-render the application view to reflect the state to the user. The framework must understand the relationship between the state and the components to complete rendering.
Let's look deeper into how hydration handles these tasks and why it’s an expensive approach.

Associating event handlers

Frameworks associate event handlers with specific DOM elements by executing a component template. For example, in React, a button component written in JSX might have an onClick prop with an event handler. Hydration requires that the browser downloads and executes all components’ templates before associating event handlers.

Unfortunately, the JS bundle’s download size and code execution time is proportional to the complexity of the page. A small demo page will download a small amount of JS and execute quickly, but the bootstrap cost becomes prohibitively expensive when it comes to real-world pages, often leading to multi second times to interactive (TTI).

Some frameworks mitigate this performance penalty by attempting to delay when certain parts of a page are rendered. This strategy works reasonably well for content-centric pages such as marketing pages. For sites like web apps where components share state in complex ways, however, frameworks still need to download every component in the DOM tree and execute its template.

Recovering application state

Event handlers need an application state to update, which is present on the server during prerendering. Frameworks must reconstruct this state on the client for the DOM to update properly.

Hydration’s basic approach is to execute the same code that generated the application state on the server again within the browser, which adds to execution time and delays interactivity.

That’s why many meta-frameworks serialize the application state on the server and include it in the HTML so that state can be restored using JSON.parse(). Deserialization is significantly faster than reconstructing state by executing application code on the browser, and it works well for simple and complex pages.

Even when application state is serialized, however, hydration still reconstructs internal framework state by slowly executing code.

Recreating the component hierarchy

For the final piece, frameworks need to recreate the component hierarchy, which is part of a framework’s internal state. It keeps track of which components need to be rerendered when your application state changes.

Similar to how it associates event handlers with DOM elements, hydration must download all of a page’s components and execute their templates to rebuild a component hierarchy, adding still more overhead.

The alternative to hydration

Front-end frameworks perform hydration to recover event handlers, application state, and the component hierarchy in order to make the page interactive.

Each step requires downloading and executing code, which is expensive. Code execution time in particular is proportional to your page’s complexity when using hydration. We could roughly model this limitation with an equation:

Time to interactive = hydration execution cost * page complexity + payload size

No matter how small your payload, hydration will always be a bottleneck.

One solution to this problem is to eliminate the need to execute any code to restore a page’s interactivity, which we can do through serialization. As mentioned above, many meta-frameworks already serialize application state. Why not serialize event handler associations and component hierarchies, as well?

Because it’s really hard!

Function closures, promises, and resource references, among other structures, are all difficult to serialize. So a framework needs to be designed with serializability and resumability in mind. These aren’t features that can easily be added to existing frameworks without large-scale breaking changes.

The biggest win of serializing page state into HTML is that making the page interactive doesn’t require downloading or executing any template code. The framework simply resumes the page.

The bottom line? Resumable frameworks would reduce execution cost to zero and incur a constant overhead regardless of page complexity.

Resumability in Qwik

We at Builder.io have created a resumable front-end framework, Qwik. Our goal is to bring every web page’s time to interactive to its absolute minimum.

While we got the ball rolling, Qwik is open to the community. Everyone who’s as passionate about web performance as we are is invited to try it out and contribute and comment.

Head over to Qwik’s repository to learn more, or try our starter: npm init qwik@latest.

If you have any questions, ask away on our discussions forum and join our Discord server.

Top comments (0)