-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hydration with shadow root doesn't work #52
Comments
There is certainly a big gap in the spec when it comes to doing SSR with shadow roots. Declaratively hydrating shadow roots is not possible without custom solutions. The approach you listed is very similar to what skatejs is doing in their SSR implementation. Upon rendering on the Server they serialize the shadow DOM, wrap it with a special node and inject a script that places all children back into the actual shadow DOM root when run in the browser. Whilst the serialization is out of scope for this repo, having am integrated way to hydrate nodes in this library sounds like an experiment worth doing. It could become tricky with multiple slots in play. To be honest I'm not sure if there is a way to avoid a re-layout or flashing of content. Usually moving nodes around will have that effect, but I wonder if a |
Perhaps one day we will get declarative shadow roots! I did take a rudimentary shot at using a template and it works well. Admittedly SEO is something I don't have a lot of knowledge about but to my understanding this code would still be crawlable. import { h, hydrate } from 'preact';
import { Counter } from './counter' // Counter component from original post sans register() call
export class Counter2 extends HTMLElement {
get increment() {
return Number(this.getAttribute('increment'))
}
constructor() {
super()
}
connectedCallback() {
const component = <Counter increment={this.increment} />
const templateShadowRoot= this.querySelector('template[shadow-root]')
const markupToHydrate = templateShadowRoot.content.cloneNode(true)
templateShadowRoot.remove()
this.attachShadow({ mode: 'open' })
this.shadowRoot.appendChild(markupToHydrate)
hydrate(component, this.shadowRoot)
}
}
customElements.define('my-counter2', Counter2) <my-counter2 increment="5">
<template shadow-root>
<p>Count: 0</p>
<button>Increment by: 5</button>
<button>Decrement by: 5</button>
</template>
</my-counter2> Of course this doesn't get the benefits from using the register method (e.g. automatic observed attributes) but perhaps this approach could be modified and implemented in the register method? I share your concern about it becoming tricky when slots are in play. I'm not sure how they could be handled in the template approach. |
If you register a component as a custom element with
{ shadow: true }
then the code in the connectedCallback will try to run the hydrate function with the shadow root as the target node. This ends up rendering the entire component inside the shadow root while preserving, but not rendering, existing children (perhaps they're being treated like slot contents?) outside the shadow root which defeats the purpose of hydration.I found this out while testing SSR with shadow root; I know you can't declaratively add a shadow root. But I was wondering if moving existing children of the custom element into the shadow root then attempting to hydrate would work? Is there risk of content reflow or style flashing? Is this simply just a limitation of custom elements that cannot be circumvented and therefore not in scope here?
Here's an example:
These are the results when the script loads:
This would be the desired results:
The text was updated successfully, but these errors were encountered: