Photo by anthony kelly on Flickr
As we reach the third article in this series focussed on exploring web rendering and performance, welcome back! Opportunities for performance improvements are really becoming apparent, so I hope you enjoy what’s coming up. If you have any questions, feel free to reach me on Twitter and I’ll do my best to help. As mentioned in the previous article about partial hydration a.k.a. “islands”, 2 main options exist to more quickly make a page interactive: (1) do less work by only hydrating interactive components and ignoring the rest, or (2) hydrate in the future; the former was covered in the previous article while the latter is the subject of this article and is known as progressive hydration.
A key reason why the progressive hydration portion of this article series comes after partial hydration (a.k.a. “islands”) is because they are often used together. Remember the goal of islands is to improve performance by limiting the number of components whose code is sent to the browser; interactive components can be thought of as islands of interactivity and only that code need be downloaded. Without the ability to break an application into individual hydration targets, the only way to progressively hydrate is to do so for an entire application which would effectively be opting into uncanny valley behavior and potentially, purposefully breaking interactivity; this is certainly a good way to frustrate your users! Instead, if some islands are hydrated immediately and others later on, the aforementioned delicate balance is restored and a better performance profile at page load will result.
How long do we wait to progressively hydrate? Luckily good patterns have been established by popular frameworks like Astro and Eleventy. Waiting to hydrate when a component scrolls into view or when the browser is idle are the options that will likely show the benefits most clearly; using Astro, the client:visible and client:idle directives correspond, respectively. For example, a
<Carousel /> React component can be hydrated when it scrolls into view by writing it as
<Carousel client:visible />; that’s it! The beauty of progressive hydration is that even if those established patterns don’t meet your needs, any time or event can be chosen as a target, but you’ll likely have to write your own code to do so. To make the process easier, Astro added the ability to add custom hydration directives in version 2.6, so check it out if you’re interested.
To emphasize the point of how useful partial and progressive hydration can be when used together, let’s reexamine the example from the previous article but this time with an overlay showing the boundaries of what’s rendered on screen (a.k.a. the browser viewport).
Three islands of interactivity exist on this page corresponding to the colored boxes: (1) the Shopping Cart component, (2) the Buy Now Button component, and (3) the Product Reviews component. From a user interest perspective, the Buy Now Button component is in the center of the screen so is likely to get the user’s attention first, and the Shopping Cart component is also on-screen and related especially if that button is clicked. However, the user can’t see the Product Reviews component at all. From an optimization perspective, progressive hydration can be used to delay execution of the Product Reviews component code until it scrolls into view; maybe the user won’t scroll down to it, so why bother? Using Astro’s syntax as a guide, let’s add a directive to the component and render it as
<ProductReviews client:visible /> to see how this decision changes the performance profile of the page:
The Product Reviews component box is now white meaning that HTML is effectively static until the user scrolls it into view. Assuming these colored components all have a similar weight, approximately 33% less code runs during hydration at page load; pretty nice improvement for typing a few extra characters!
Now that progressive hydration’s pros and cons are understood, what are the recommended ways to try it? At the risk of being repetitive, Eleventy and Astro have improved the developer experience significantly in this area, so the recommended way to learn is to start with either tool to gain experience. If more granularity is desired, build on top of either framework’s primitives or eventually build your own once the concepts are mastered.