How can partial hydration, progressive hydration be accomplished in React Part 1
May 14, 2021
What is hydration in web application
To explain hydration we first need some background on how web applications can be created. There are different ways to server HTML to the users browser and these days, developers use front end frameworks like React and Vue to build web user applications.
Server side rendering and static rendering can both have their content generated before reaching the user’s browser. Upon reaching the user’s browser, these frameworks will kick start the process of “hydration” which then adds interactivity back into the HTML document. You can think of it as attaching event listeners to the HTML content when the content reaches the browser.
What is partial hydration
I understand partial hydration of a web as hydrating only certain parts of the web application. An example of this would be choosing to hydrate one or a few components only within a page. On the code level, the server and client (the user’s browser) may run different code like so:
// Page.js runs on both server and client
function Page() {
return (
<Text />
<Ads />
);
}
// Ads.js runs on both server and client
function Ads() {
return <div>Here are some ads</ div>
}
// Text.server.js
function Text() {
return (
<div>Click here
<button onclick={window.alert('NOPE')}>BUTTON</ button>
</div>
);
}
// On client side, Text component runs as follows.
// This component's hydration is skipped.
// Text.csr.js
function Text() {
return <div dangerouslySetInnerHTML={''} suppressHydrationWarning />;
}
In this example, we see that <Ads />
component is hydrated whereas <Text />
component hydration ends at it’s root <div>
. This will cause <Text />
component to not get hydrated and thus, no interactivity will be attached to them. This means they will not re-render when new props are passed into them. They will not be interactive so if you clicked the button rendered within within Text component, nothing would happen.
What is progressive hydration
When the page reaches the user’s browser, the hydration phase kicks in. During this hydration phase, all components that require interactivity are all hydrated during this phase. Just imagine 100 components being hydrated all at once, on page load. The idea of Progressive hydration suggest spreading out this huge hydration task so as to reduce the huge initial CPU activity when the page loads.
Here’s the talk about it in Google IO 2019:
Conceptually, here’s what will happen:
- The Server renders HTML string (Rendered UI) and adds it into HTML
- The Server serializes data for UI and adds it into HTML
<script>
tag - Only after bundle.js finished loading and executing then UI will be interactive
So how do you skip React hydration?
When you use dangerouslySetHTML
as an empty string in a <div>
(or dom element), it stops react from walking down the tree. So on server side, if we return the rendered DOM components. We can use dangerouslySetHTML
to signal to react to stop hydrating this subtree.
Here is a code example of how we can skip the initial hydration:
public/index.html
mimics the server rendered HTML. On client side, we need to ensure that the DOM structure matches. In this instance, App
returns a <div>
, so Counter
have to match up with rendered DOM structure within <div>
.
Do note that when you set a component to skip rehydration, all its children will follow and skip hydration too.
// On server side
function Container({ children }) {
return <div>{children}</div>
}
function Page() {
return (
<Container>
<Text />
<Text />
<Text />
</Container>
)
}
// On Client side, you will not be able to reach Text.
// Passing in children will not cause this component to re-render
function Container({ children }) {
return (
<div suppressHydrationWarning dangerouslySetInnerHTML={{ __html: "" }}>
{children}
</div>
)
}
In the next blog post, I will show some experiments and strategies of progressive hydration.