What hydration means for web components
When@lit-labs/ssr renders a Lit template it emits HTML comment markers (e.g., <!--lit-part ...-->) that encode the structure of the template. On the client, Lit’s hydration routine walks the DOM, reads these markers, and reconstructs the internal ChildPart and AttributePart data structures that lit-html normally builds when it first renders into a container.
After hydration, Lit treats the DOM exactly as if it had rendered it on the client. Subsequent calls to render() update only the parts of the DOM that changed.
Installing @lit-labs/ssr-client
Hydrating a template with hydrate()
Thehydrate() function from @lit-labs/ssr-client hydrates a server-rendered container:
rootValue— the same template result (with the same data) that was used on the server.container— the DOM element that was rendered into on the server.options— optionalRenderOptions(e.g.,host,renderBefore).
Example
Declarative Shadow DOM and the polyfill
LitElement shadow roots are emitted as <template shadowrootmode="open"> elements (Declarative Shadow DOM). Modern browsers parse these natively and attach shadow roots before JavaScript runs.
For browsers that do not support DSD natively, apply the @webcomponents/template-shadowroot polyfill:
LitElement auto-hydration
For pages containingLitElement components, load @lit-labs/ssr-client/lit-element-hydrate-support.js before any Lit module is imported. This module patches LitElement to automatically hydrate itself when it detects an existing shadow root from Declarative Shadow DOM:
LitElement connects to the DOM and already has a shadow root (because DSD created it), the hydrate-support module:
- Detects
this.shadowRootis already set and setsthis._$needsHydration = true. - On the first
update()call, runshydrate(value, this.renderRoot, this.renderOptions)instead ofrender(). - Removes any
hydrate-internals-*aria attributes that the SSR dom-shim added as placeholders. - All subsequent updates use the normal
render()path.
Bootup order
The order of initialization matters. The constraints are:- The DSD polyfill must run after all HTML is parsed.
@lit-labs/ssr-client/lit-element-hydrate-support.jsmust load beforelitor any component module.- Component definitions must load after both the polyfill and the hydrate-support module.
Load hydrate support early
Start fetching the hydrate-support module as early as possible, without blocking:
Prevent flash of un-styled content
Use a
dsd-pending attribute to hide content until shadow DOM is active (for browsers requiring the polyfill):Partial hydration
You do not have to hydrate every component at once. Becausehydrate() operates on a single DOM scope (it does not descend into shadow roots), you can selectively hydrate parts of the page:
defer-hydration attribute set by the server. The hydrate-support module removes this attribute when the enclosing scope is hydrated, triggering the element’s own hydration.
Common pitfalls
Mismatched server/client state
Hydration requires the template and data to be identical on the server and client. If the client renders a different template — even a different conditional branch — you will see:Browser-only APIs in render()
The DOM shim is minimal. Accessingdocument, window, localStorage, or querying the DOM inside render() (or any lifecycle method that runs on the server) will throw or return unexpected values.
Safe lifecycle callbacks to use DOM APIs:
LitElement.update()(server does not call this)LitElement.updated()(server does not call this)LitElement.firstUpdated()(server does not call this)Directive.update()(server does not call this)
isServer flag from lit-html/is-server.js to guard browser-only code:
Hydrating more than once
Callinghydrate() on a container that already has a live render will throw:
hydrate() exactly once per container, then use render() for all subsequent updates.
Event handlers and property bindings in server-only templates
Server-only templates (thehtml export from @lit-labs/ssr) do not support event handlers (@click) or property bindings (.value). These require the marker comments that server-only templates omit. Use the normal html tag from lit for any template that needs these bindings and will be hydrated.