Hydration
By default all components are only rendered on the server. However, this means those components are "static" and can not add interactivity.
To add interactivity to your component you can either render it only on the client or you can render on the server and hydrate
it on the client.
Progressive Hydration
With Rocket we recommend to use progressive Hydration.
You can see it as
➡️ Start with 0 JavaScript
➡️ Load JavaScript of components as needed
In practice this means we use components without any loading strategy if we want server rendering. For a static site like a blog this will be almost all of my components. But some components will have interactivity and those will need to be hydrated.
In the following example we have two components being only server rendered and one being hydrated.
<blog-header></blog-header>
<p>Here is more text</p>
<blog-author></blog-author>
<blog-newsletter-sign-up loading="hydrate:onClick"></blog-newsletter-sign-up>
- If this is the only content on the page it means it will load with zero JavaScript 🎉.
- If we use the sign up then the JS of
blog-newsletter-sign-up
will be loaded to give feedback about a valid email address. - If we don't then no additional JS will be loaded at all.
tip
The automatic loading/hydration only works if you register components to the rocket loader.
Server Loading
Server Loading has almost zero impact on the client side page performance and is therefore the fastest possible solution available.
Loading and rendering on the server is the default and for that reason does not need to be specified.
<blog-header></blog-header>
will result in a server rendering only.
The output will be a Declarative Shadow Dom (DSD).
<blog-header>
<template shadowroot="open">
<style>
:host {
display: block;
}
h1 {
color: red;
}
</style>
<h1>Welcome to my Blog</h1>
</template>
</blog-header>
which looks like this
Welcome to my Blog
Open it on Codepen to play around.
A DSD does not need any JavaScript (in 2022: non-chromium browsers need a polyfill) to display elements with their styles scoped.
If possible you should always render your component only on the server to avoid unnecessary JavaScript. This results in the best possible site performance.
Server only rendered components however can never be interactive. For interactive components you need JavaScript which you can bring by using progressive hydration.
Hydration Loading
Hydration Loading roughly has a medium impact on the client side page performance. For the initial load there is almost zero impact and we then decide when to take the performance hit by loading and executing the JavaScript of components.
Hydration Loading becomes necessary as soon as we need interactive components. In Rocket we recommend to progressively hydrate based on user actions.
For example a counter only server rendered will not work as expected.
<my-counter loading="server"></my-counter>
<!-- you could omit the `loading="server"` as it's the default -->
👇 the +/- buttons will not do anything
To "enable" it we set the loading
attribute for example to hydrate:onClick
.
<my-counter loading="hydrate:onClick"></my-counter>
👇 now if we click the +/- buttons the component will hydrate and the counter works
tip
After you clicked on the 2nd example the 1st example will start working as well.
The reason for that is that it's the same component my-counter
.
Once you hydrate a component it will hydrate all its instances.
Hydration Conditions
You can specify and combine multiple conditions on when to hydrate a component.
<my-el loading="hydrate:[[ one or multiple conditions ]]"></my-el>
Option | Description |
---|---|
onClientLoad | as soon as possible |
onClick | as you click on the element (will reDispatch the click event after hydration) |
onFocus | as you focus the element (will reDispatch the focus event after hydration) |
onMedia | as soon as a media query is met |
onVisible | as soon as the component becomes visible on the screen |
onHover (⚠️ not implemented) | as you hover over the element + optional padding (click triggers hover => touchscreens) |
withIdle (⚠️ not implemented) | as soon there is a free slot in the main thread |
withDelay (⚠️ not implemented) | after x ms |
Each of the options can be combined via &&
or ||
.
Example | Description |
---|---|
onClientLoad | above the fold element that should become interactive as soon as possible |
onMedia('(max-width: 320px)') | mobile burger menu that triggers a drawer for navigation (only hydrate on screens smaller then 320p) |
onMedia('(min-width: 640px)') && onClick | chart that only becomes interactive on desktop after a click |
onMedia('(prefers-reduced-motion: no-preference)') && onClick | a visual animation that plays on click only if there is no prefers-reduced-motion |
onVisible | heavy chart that becomes interactive when element becomes visible |
warning
Hydration Loading does not (yet) support passing complex SSR data via properties. e.g. components that want to hydrate can only use attributes for now.
Client Loading
Client Loading has a large impact on the client side page performance. It means nothing happens on the server and the JavaScript for the component gets shipped and instantly executed to render the "initial" paint of the component.
HTML with components using client loading is shipped as is.
<my-el loading="client"></my-el>
As the HTML does not contain any details of the content within my-el
it also means that SEO might be impacted.
When will we still use Client Loading?
- Components that can not be server rendered
- Technical reasons like usage of
canvas
orwebgl
which can not be "rendered" into HTML - Components may do things that hit some of the SSR limitation
- SSR Limitations means to only do imperative manipulation of the DOM in
update, updated, firstUpdated
for more see lit-ssr docs - Web Components SSR is pretty new so some components may need adjustments before they can work with SSR
- Technical reasons like usage of
- Components that require user data do not make sense to SSR if using Build Time SSR.
Loading Strategy Impacts
Which loading strategy you choose will have an impact on the performance of your page. The higher the impact the more JavaScript you send to the client which will result in lower performance.
If you use a component on the same page with different loading strategies then the following impacts applies
- client (high impact)
- hydrate (medium impact)
- server (low impact)
The loading strategy with the highest impact will be applied to all components.
<my-component loading="client"></my-component>
<my-component loading="hydrate:*"></my-component>
<my-component loading="server"></my-component>
Results in client rendering
of all my-component
s.
<my-component loading="hydrate:*"></my-component>
<my-component loading="server"></my-component>
Results in server+hydration rendering
of all my-component
s.
<my-component loading="server"></my-component>
<my-component></my-component>
Results in server rendering
of all my-component
s.
Why theses impacts?
- client - if you eagerly load & render the component on the client then bloating the html of some components by server rendering does not bring any benefit
- hydrate - hydration means that all components with the same tag name will be hydrated on the client - you can not keep a server only variation of a component
- server - almost zero client side impact
tip
Always try to load all components of a page with the same (lowest) impact if possible.