Custom elements
Register a component as a custom element in a few lines.
You register a component as a custom element with mount(). Copy this helper into your app:
import { const mount: (el: Element, fn: Component, opts?: MountOptions) => (() => void)mount } from "ilha";
import type { type Component = (() => View | undefined | Promise<View | undefined>) | GeneratorFnComponent } from "ilha";
/** Mount `component` in every `<name>` element; hydrate when it has `data-ilha`. */
export const const define: (name: string, component: Component) => voidMount `component` in every `<name>` element; hydrate when it has `data-ilha`.define = (name: stringname: string, component: Componentcomponent: type Component = (() => View | undefined | Promise<View | undefined>) | GeneratorFnComponent): void => {
var customElements: CustomElementRegistryThe **`customElements`** read-only property of the Window interface returns a reference to the CustomElementRegistry object, which can be used to register new custom elements and get information about previously registered custom elements.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/customElements)customElements.CustomElementRegistry.define(name: string, constructor: CustomElementConstructor, options?: ElementDefinitionOptions): voidThe **`define()`** method of the CustomElementRegistry interface adds a definition for a custom element to the custom element registry, mapping its name to the constructor which will be used to create it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomElementRegistry/define)define(
name: stringname,
class extends var HTMLElement: {
new (): HTMLElement;
prototype: HTMLElement;
}
The **`HTMLElement`** interface represents any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement)HTMLElement {
#unmount: (() => void) | undefined;
function (Anonymous class).connectedCallback(): voidconnectedCallback(): void {
this.#unmount = function mount(el: Element, fn: Component, opts?: MountOptions): (() => void)mount(this, component: Componentcomponent, {
MountOptions.hydrate?: boolean | undefinedhydrate: this.Element.matches(selectors: string): boolean (+3 overloads)The **`matches()`** method of the Element interface tests whether the element would be selected by the specified CSS selector.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/matches)matches("[data-ilha]"),
});
}
function (Anonymous class).disconnectedCallback(): voiddisconnectedCallback(): void {
this.#unmount?.();
this.#unmount = var undefinedundefined;
}
}
);
};
Then register components with it:
import { const atom: AtomFnatom } from "ilha";
import { const define: (name: string, component: Component) => voiddefine } from "./define";
function define(name: string, component: Component): voiddefine("ilha-counter", () => {
const const count: AtomHandle<number>count = atom<number>(init: number | Atom<number>, options?: AtomOptions<number> | undefined): AtomHandle<number> (+1 overload)An existing Effect atom, or a plain value. Store a function with
`atom.lazy(() => fn)`; the `ilha/oxlint` rule `function-in-atom` flags
`atom(fn)`.atom(0);
return (
<JSX.IntrinsicElements.button: ButtonHTMLAttributesbutton type?: string | undefinedtype="button" EventProps<HTMLButtonElement>.onclick?: ((event: Targeted<HTMLButtonElement, MouseEvent>) => void) | undefinedonclick={() => const count: AtomHandle<number>count.AtomHandle<number>.update: (f: (current: number) => number) => voidupdate((n: numbern) => n: numbern + 1)}>
{const count: AtomHandle<number>count}
</JSX.IntrinsicElements.button: ButtonHTMLAttributesbutton>
);
});
<ilha-counter></ilha-counter>
Connecting the element mounts the component; disconnecting unmounts it. Add data-ilha to a server-rendered element to hydrate its markup.