Custom elements
A component as a tag any page can use.
defineElement(tag, Component) registers a custom element. When
the element is added to a page, it mounts the component inside itself.
When it is removed, the component is unmounted. Each element is its own
application, so it
works in plain HTML, in a CMS template or inside another framework.
It comes from signy/app, and from signy. The
signy/web entry does not include it.
Define and use
The tag name needs a hyphen, as the browser requires. Props are listed in
props: an object picks the type of each, a plain array means
strings. The attribute for maxItems is
max-items.
Nothing yet
Props
A prop is a getter over a signal. Read it inside a binding, like
${() => this.props.label}, and the text follows the
attribute. A value read once, such as in render() outside a
function, stays as it was.
| Type | From the attribute | When the attribute is missing |
|---|---|---|
String | The text. | undefined |
Number | Number(text), so NaN for text that is not a number. | undefined |
Boolean | true for any value, including an empty one. | false |
- Attributes change props. Setting
el.label = "x"changes the prop too, but it does not write the attribute back. - A property set before the element was defined is kept when the definition arrives.
- Only strings, numbers and booleans come from attributes. Pass an object or a function through the property:
el.items = [1, 2, 3]. - The component's own
propstype should list the same names.
Events
this.emit(name, detail) fires on the element, so
addEventListener and attributes like onchange work
from the outside. See
custom events and slots.
Shadow DOM and slots
A component with shadow = true renders into a shadow root on
the element. Content between the tags stays in the page and shows through
a <slot>. Without a shadow root, the output is added after
the children the element already has.
Lifecycle
- Connecting the element builds the component and mounts it.
setup(),render()andonMount()run as for any component. - Removing the element unmounts the component in a microtask, so
onUnmount()and cleanups run. - Moving the element, for example with
appendChildto another parent, removes and adds it in the same task. The component stays mounted. - An element that is removed, then added again later, gets a fresh component. State from the first one is gone.
- Every element has its own app, context and state. Two
<x-counter>tags do not share anything.
Plugins
The setup option runs for each element before its app
mounts. Install plugins there, for example a router or app level values.
defineElement('x-inbox', Inbox, {
setup(app, element) {
app.use(router);
app.provide(ThemeKey, element.dataset.theme ?? 'light');
},
});
Limits
- A tag can be defined once. A second
defineElementwith the same name throws, ascustomElements.definedoes. - The component runs in the browser.
defineElementdoes not render on a server. - A prop with a type other than
String,NumberorBooleanis a property, not an attribute.