preactjs / preact-custom-element

Wrap your component up as a custom element
MIT License
360 stars 52 forks source link

Allow creating a CustomElement without defining the tagName #55

Open jvdsande opened 3 years ago

jvdsande commented 3 years ago

What does this PR do

The goal of this PR is to separate the Preact-to-CustomElement process from the tag-name registration process.

Why is this PR needed

Registering a CustomElement using window.customElements.define uses a shared, global registry for custom-elements. Thus, only one CustomElement can be associated with a given tag-name. This can cause problems when authoring a component library: we "reserve" keywords on behalf of our end users. It also causes isolation issue for patterns such a micro-frontend architecture, where two versions of our library can't be used together, as they would clash while registering components.

Implementation details

This PR moves all the creation of PreactElement into its own function, toCustomElement. This function takes the same parameters as the default register export, omitting the second parameter (tag name).

The default exports is unchanged, and still registers the element right away.

Usage is as follow:

import register from 'preact-custom-element'

function MyComponent({ name = "World" }) {
  return <span>Hello {name}!</span>  
}

// Usage 1: Register the element right away (default)
register(MyComponent, "my-component", ['name'])

// Usage 2: Create the element, but do not register it
export const MyElement = register.toCustomElement(MyComponent, ['name'])

// Users can later do their own window.customElements.define('some-lib-element', MyElement)

Misc

Right now the toCustomElement function is attached as a property of register, as to not introduce a named export next to the default one (microbundle was not happy with that). However it's not the most elegant thing. We could maybe introduce a new option to the register function, called define: boolean, defaults to "true": whether to define the element after creating it. If false, the customElement will be returned instead of being defined

leifriksheim commented 3 years ago

👍

This would be so useful for our use case. In our app, we are dynamically importing web components and registering them manually so we avoid name collisions.