Skip to content

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.

TypeFrom the attributeWhen the attribute is missing
StringThe text.undefined
NumberNumber(text), so NaN for text that is not a number.undefined
Booleantrue 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 props type 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.

New Sold out

Lifecycle

  • Connecting the element builds the component and mounts it. setup(), render() and onMount() 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 appendChild to 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 defineElement with the same name throws, as customElements.define does.
  • The component runs in the browser. defineElement does not render on a server.
  • A prop with a type other than String, Number or Boolean is a property, not an attribute.