[TPAC Prep] Scoped Custom Element Registries #73
Replies: 1 comment
-
Sorry for the late reply on this, but I did want to provide some Salesforce feedback on this spec. We at Salesforce are shipping a quasi-polyfill for Scoped Custom Element Registries. You can find some public documentation on it here and here. Our implementation is partially based on the Basically, in the Salesforce Lightning world, the important encapsulation boundary is not shadow roots, but instead "namespaces," which have a one-to-many relationship with components on the page. These are separated using Lightning Web Security, which is basically a wrapper around our polyfill of ShadowRealms plus closed-shadow-root semantics. In short, the current spec seems to do ~90% of what we want, but the main missing part is that, in our case, it's not enough for custom elements to be encapsulated by shadow root – they really need to be encapsulated by ShadowRealm. So e.g. this: globalThis.customElements.define('x-foo', ...) Or this: document.body.div.innerHTML = '<x-foo></x-foo>' ... needs to be encapsulated to the given ShadowRealm running the script, not at the shadow-root level (or global level). There is probably some clever way we can make the Scoped Custom Element Registry implementation (when it hits browsers) follow these restrictions. For instance, we can grab a shadow root associated with a Scoped Custom Element Registry (since in our case, there would always be a component shadow root for a given ShadowRealm) and associate the I would defer to the experts in this case (:wave: @rwaldron @caridy) but this is my basic understanding of where we're at right now. |
Beta Was this translation helpful? Give feedback.
-
Authors: Michael Warren (@michaelwarren1106) and Peter Burns (@)
Background
When building a custom element, it needs to be defined as such in order for the browser to recognize and upgrade non-native HTML tags and associate those with their correct JS classes for runtime behavior. The current API is the
customElements.define(tagName, klass)
which takes a custom tag name and a JS class reference and registers a new custom element.Developer-facing problem
The issue with the current custom element definition approach is that there is only a single registry in the global space. Having a single global registry means also having the possibility of tag name or class collisions which will present problems when different released versions of the same custom element need to exist on a single HTML page at the same time.
Design system use case example
Design system teams are largely responsible for making standalone reusable components that can be consumed in all manner of applications across an organization. Design system components can be small and directed (something like
x-label
or coarse-grained likex-accordion
). For DRY principles, it is often very desirable for large coarse-grained components to internally (in the shadow root render template) use smaller components to keep logic encapsulated.When a design system component internally uses a component that a consuming application also uses, a version conflict can arise. If the large component uses
x-small-component
version 1.0.0, and the application usesx-small-component
version 2.0.0, the custom element tag names will be the same, and both versions will attempt to register in the global registry.The first element registered will "win". The second registration will fail. Whichever element "wins" will cause functional failures for either the application or design system component, depending on which component registration "wins".
Micro-front end use case example
Micro-front-end architecture (MFE) is rising in popularity as a way to develop smaller pieces of what will be the final user-facing application, then assemble those pieces at runtime in some sort of shell application, service worker, etc. The micro-front-end architecture basically means that whole pieces of applications will be developed independently by separate probably-siloed teams then placed on the same page at runtime.
When MFE remote component 1 uses a certain version of a component, and MFE remote component 2 uses a different version of that same component, and both attempt to register in the same global registry under the same tag name, version conflicts arise.
If MFE 1 uses
x-component
version 1.0.0, and MFE 2 usesx-component
2.0.0, whichever component registration comes first "wins" and breaks the other MFE remote component. If 1.0.0 wins, then MFE 2 is broken because it is expecting 2.0.0 and use the 2.0.0 component API. Likewise, if 2.0.0 wins, MFE 1 breaks because it is dependent on the 1.0.0 component API which may not be supported in 2.0.0.Proposal Discussions
Breakout Session Issue
w3c/tpac2023-breakouts#15
Proposed Spec
https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Scoped-Custom-Element-Registries.md
Proposal Issue
WICG/webcomponents#716
Polyfills
@webcomponents/scoped-custom-element-registry
https://github.com/webcomponents/polyfills/tree/master/packages/scoped-custom-element-registry
Mixins
The mixins below are offered as helper libraries to consume the polyfill implementation in an consistent, automated way.
Open Web Componenst: @open-wc/scoped-elements: ScopedElementsMixin
https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements
Lit (labs): @lit-labs/scoped-registry-mixin : ScopedRegistryMixin
https://github.com/lit/lit/tree/main/packages/labs/scoped-registry-mixin
Browser Positions
Chrome Position : Intent to Prototype
https://chromestatus.com/feature/5090435261792256
Webkit position: Nothing official yet, likely positive
WebKit/standards-positions#38
Firefox/Gecko position: No position yet
mozilla/standards-positions#424
Next steps
Declared positions from Webkit and Firefox would be awesome, as well as a completed pilot implementation from Chrome.
Beta Was this translation helpful? Give feedback.
All reactions