About WebLogs Services Projects

React 18 Partial Hydration

Last Modified: 2022-10-31
Knfrmd - React 18 Partial Hydration
Favour Nwachukwu
Favour Nwachukwu

React 18 Partial Hydration/Islands Architecture

Written by Favour Nwachukwu


This is the process of rendering your components and attaching event handlers. It’s like watering the “dry” HTML with the “water” of interactivity and event handlers.

Hydration is the name given to the process in JavaScript frameworks to initializing the page in the browser after it has previously been server rendered. While the server can produce the initial HTML we need to augment this output with event handlers and initialize our application state in a way that it can be interactive in the browser.

Partial hydration is a technique that allows web developers to build sites using their preferred framework, but only send generated HTML and CSS to the browser by default. Where dynamic functionality is desired, components can be hydrated to become interactive. In most frameworks this process carries a pretty heavy cost when our page is first loading. Depending on how long it takes for JavaScript to load and for hydration to complete, we can be presenting a page that looks to be interactive but in reality isn't. This can be terrible for user experience and is especially felt on lower end devices and slower networks.

Long first input delays are typically caused when a user tries to interact with the page while the main thread is busy and unable to respond right away. On a server-side rendered page, the hydration process can take up a lot of space on the main thread. To improve the FID as well as the user experience, our goal is to reduce and split the work on the main thread into smaller tasks so that the user can start interacting with the page sooner and that the browser can handle these interactions as quickly as possible. That’s where hydration strategies come into play.

When it comes to client side hydration there are two characteristics that are pretty unfortunate. One is we render on the server, and now we basically need to re-render it again in the browser to hydrate everything. The second is we tend to send everything across the wire twice. Once as HTML and once in JavaScript. Generally it gets sent across in 3 forms:

The Template - component code/static templates

The Data - the data we use to fill our template

The Realized View - the final HTML

The template views are both in your bundled JavaScript and rendered HTML, and the data is present both as usually a script tag rendered into the page and also in part in the final HTML.

With client rendering we just sent the template, and requested the data to render it. There is no duplication. However, we are at the mercy of the network loading the JavaScript bundle before we can show anything.

So having the realized HTML from the server is where we get all the benefits of server rendering. It lets us not be at the mercy of JavaScript loading times to display our site. But how do we combat the inherent extra overhead that comes from server rendering?

How does SSR work?

In SSR, data is fetched, and the HTML is generated from the React components on the server. The HTML is then sent out to the client.

Here are the steps which are followed during SSR:

  • Data is fetched for the entire application on the server

  • The HTML is generated for the entire application from the React components on the server, which is then sent to the client.

  • On the client ( browser ), the JavaScript code is loaded for the the entire application

  • The JavaScript logic is then connected to the server-generated HTML for the the entire application. This process is called Hydration. It makes the site interactive.

React 18 offers two major features for SSR:

Streaming HTML lets you start emitting HTML as early as you’d like, streaming HTML for additional content together with the script tags that put them in the right places.

Selective Hydration

This lets you start hydrating your app as early as possible, before the rest of the HTML and the JavaScript code are fully downloaded. It also prioritizes hydrating the parts the user is interacting with, creating an illusion of instant hydration.

These features solve three long-standing problems with SSR in React:

You no longer have to wait for all the data to load on the server before sending HTML. Instead, you start sending HTML as soon as you have enough to show a shell of the app, and stream the rest of the HTML as it’s ready.

You no longer have to wait for all JavaScript to load to start hydrating. Instead, you can use code splitting together with server rendering. The server HTML will be preserved, and React will hydrate it when the associated code loads.

You no longer have to wait for all components to hydrate to start interacting with the page. Instead, you can rely on Selective Hydration to prioritize the components the user is interacting with, and hydrate them early.

Below we have some methods:

  1. Static Routes (No Hydration)

  2. Lazy-loading the JavaScript (Progressive Hydration)

  3. Extracting Data from the HTML

  4. Islands (Partial Hydration)

  5. Out of Order Hydration

  6. Server Components

Partial Hydration:

Picture a web page as mostly static HTML that doesn't need to be re-rendered or hydrated in the browser. Inside it there are the few places where a user can interact which we can refer to as our "Islands". This approach is often called Partial Hydration since we only need to hydrate those islands and can skip sending the JavaScript for anything else on the page.

With an app architected this way we only need to serialize the inputs or props to the top level components since we know nothing above them is stateful. We know 100% it can never re-render. That outside of the islands is incapable of change. In so we can solve a lot of the double data problem simply by never sending the data we don't use. If it isn't a top-level input there is no way it can be needed in the browser.

But where do we set the boundaries? Doing it at a component level is reasonable as it is something we as humans can make sense of. But the more granular the islands the more effective they are. When anything under an island can re-render you need to send that code to the browser.

One solution is developing a compiler smart enough to determine state at a sub-component level. In so not only are static branches pruned from our tree but even ones nested under stateful components. But such a compiler would need a specialized Domain Specific Language(DSL) so that it could be analyzed in a cross module fashion.

More importantly, islands means server rendering each page on navigation. This multi-page (MPA) approach is the way the web classically works. But it means no client side transitions and loss of client state on navigation.

Effectively, Partial Hydration is an improved version of our Static Routes above. One where you only pay for the features you use.

Contact Favour Nwachukwu