On

Attaches a DOM event listener to the island host or any descendant element. Listeners are set up at mount time and cleaned up automatically on unmount.

Interactive Tutorial

Basic usage

Selector syntax

The first argument combines a CSS selector and an event name using @ as a separator:

"cssSelector@eventName"

Omit the selector to target the island host element itself:

.on("@click", handler)              // host click
.on("button@click", handler)        // any <button> inside the island
.on("input.search@input", handler)  // input with class "search"
.on("#submit@click", handler)       // element with id="submit"

Event modifiers

Append modifiers after the event name with : as a separator:

ModifierEquivalentDescription
once{ once: true }Listener fires only once, then removes itself
capture{ capture: true }Listens in the capture phase
passive{ passive: true }Hints the browser this handler won't call preventDefault
abortablectx.signal aborts when the same listener fires again
.on("button@click:once", handler)
.on("@scroll:passive", handler)
.on("button@click:once:capture", handler)

Multiple modifiers can be combined in any order.

Handler context

The handler receives a HandlerContext with everything needed to respond to the event:

{
  state: IslandState; // reactive state signals
  derived: IslandDerived; // current derived values
  input: TInput; // resolved input props
  host: Element; // island root element
  target: Element; // element that fired the event
  event: Event; // the native DOM event
  signal: AbortSignal; // aborts on unmount, and on next fire if :abortable
}

Both target and event are typed when the event name is a known HTML event:

import 
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha from "ilha"; const const Island: Island<Record<string, unknown>, MergeState<Record<never, never>, "value", string>>Island =
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>>.state<string, "value">(key: "value", init?: StateInit<Record<string, unknown>, string> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "value", string>, Record<never, never>>state("value", "") .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "value", string>, Record<never, never>>.on<"input@input">(selectorOrCombined: "input@input", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "value", string>, "input", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "value", string>, Record<never, never>>on("input@input", ({ state: IslandState<MergeState<Record<never, never>, "value", string>>state, event: InputEventevent }) => { state: IslandState<MergeState<Record<never, never>, "value", string>>state.
value: MarkedSignalAccessor
(value: string) => void (+1 overload)
value
((event: InputEventevent.Event.target: EventTarget | null
The read-only **`target`** property of the Event interface is a reference to the object onto which the event was dispatched. It is different from Event.currentTarget when the event handler is called during the bubbling or capturing phase of the event. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target)
target
as HTMLInputElement).HTMLInputElement.value: string
The **`value`** property of the HTMLInputElement interface represents the current value of the <input> element as a string. [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/value)
value
);
}) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "value", string>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<never, never>, "value", string>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<never, never>, "value", string>>render(({ state: IslandState<MergeState<Record<never, never>, "value", string>>state }) => <IntrinsicElements[string]: anyinput value: stringvalue={state: IslandState<MergeState<Record<never, never>, "value", string>>state.
value: MarkedSignalAccessor
() => string (+1 overload)
value
()} />);

Cancelling async work with ctx.signal

Every handler receives an AbortSignal on ctx.signal. It aborts when the island unmounts, so you can pass it directly to fetch or any abort-aware API to cancel stale work automatically:

import 
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha from "ilha"; const const Island: Island<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>>Island =
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>>.state<never[], "results">(key: "results", init?: StateInit<Record<string, unknown>, never[]> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>, Record<never, never>>state("results", []) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>, Record<never, never>>.on<"button@click">(selectorOrCombined: "button@click", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>, "click", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>, Record<never, never>>on("button@click", async ({ state: IslandState<MergeState<Record<never, never>, "results", never[]>>state, signal: AbortSignal
AbortSignal that fires when the island unmounts. If the handler's selector was registered with the `:abortable` modifier, the signal is also aborted when the same listener fires again on the same target.
signal
}) => {
const const res: Responseres = await function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
fetch
("/api/data", { RequestInit.signal?: AbortSignal | null | undefined
An AbortSignal to set request's signal.
signal
});
state: IslandState<MergeState<Record<never, never>, "results", never[]>>state.
results: MarkedSignalAccessor
(value: never[]) => void (+1 overload)
results
(await const res: Responseres.Body.json(): Promise<any>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)
json
());
}) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<never, never>, "results", never[]>>render(() => ( <> <IntrinsicElements[string]: anybutton>Load</IntrinsicElements[string]: anybutton> <IntrinsicElements[string]: anyul></IntrinsicElements[string]: anyul> </> ));

Race-cancellation with :abortable

When the same listener fires again on the same target, the previous invocation's signal aborts. This is opt-in via the :abortable modifier:

import 
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha from "ilha"; const const Search: Island<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>Search =
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>>.state<string, "query">(key: "query", init?: StateInit<Record<string, unknown>, string> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "query", string>, Record<never, never>>state("query", "") .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "query", string>, Record<never, never>>.state<never[], "results">(key: "results", init?: StateInit<Record<string, unknown>, never[]> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>, Record<never, never>>state("results", []) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>, Record<never, never>>.on<"input@input:abortable">(selectorOrCombined: "input@input:abortable", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>, "input", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>, Record<never, never>>on( "input@input:abortable", async ({ state: IslandState<MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>state, event: InputEventevent, signal: AbortSignal
AbortSignal that fires when the island unmounts. If the handler's selector was registered with the `:abortable` modifier, the signal is also aborted when the same listener fires again on the same target.
signal
}) => {
const const q: stringq = (event: InputEventevent.Event.target: EventTarget | null
The read-only **`target`** property of the Event interface is a reference to the object onto which the event was dispatched. It is different from Event.currentTarget when the event handler is called during the bubbling or capturing phase of the event. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target)
target
as HTMLInputElement).HTMLInputElement.value: string
The **`value`** property of the HTMLInputElement interface represents the current value of the <input> element as a string. [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/value)
value
;
state: IslandState<MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>state.
query: MarkedSignalAccessor
(value: string) => void (+1 overload)
query
(const q: stringq);
const const res: Responseres = await function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
fetch
(`/search?q=${const q: stringq}`, { RequestInit.signal?: AbortSignal | null | undefined
An AbortSignal to set request's signal.
signal
});
if (signal: AbortSignal
AbortSignal that fires when the island unmounts. If the handler's selector was registered with the `:abortable` modifier, the signal is also aborted when the same listener fires again on the same target.
signal
.AbortSignal.aborted: boolean
The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (true) or not (false). [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)
aborted
) return;
state: IslandState<MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>state.
results: MarkedSignalAccessor
(value: never[]) => void (+1 overload)
results
(await const res: Responseres.Body.json(): Promise<any>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)
json
());
}, ) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>render(({ state: IslandState<MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>state }) => ( <> <IntrinsicElements[string]: anyinput value: stringvalue={state: IslandState<MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>state.
query: MarkedSignalAccessor
() => string (+1 overload)
query
()} />
<IntrinsicElements[string]: anyul> {state: IslandState<MergeState<MergeState<Record<never, never>, "query", string>, "results", never[]>>state.
results: MarkedSignalAccessor
() => never[] (+1 overload)
results
().Array<never>.map<JSX.Element>(callbackfn: (value: never, index: number, array: never[]) => JSX.Element, thisArg?: any): JSX.Element[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((r: neverr) => (
<IntrinsicElements[string]: anyli>{r: neverr}</IntrinsicElements[string]: anyli> ))} </IntrinsicElements[string]: anyul> </> ));

Race-cancellation is scoped per-target — clicking button A does not cancel an in-flight handler on button B.

Async handlers and errors

Async errors (and sync throws) are caught automatically and routed through the error sink: per-island .onError(), then onUncaughtError(), then console.error. AbortError rejections from cancelled work are filtered out and do not reach any of those handlers.

import 
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha from "ilha"; const const Form: Island<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>>Form =
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>>.state<boolean, "loading">(key: "loading", init?: StateInit<Record<string, unknown>, boolean> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>, Record<never, never>>state("loading", false) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>, Record<never, never>>.on<"form@submit">(selectorOrCombined: "form@submit", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>, "submit", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>, Record<never, never>>on("form@submit", async ({ state: IslandState<MergeState<Record<never, never>, "loading", boolean>>state, event: SubmitEventevent, signal: AbortSignal
AbortSignal that fires when the island unmounts. If the handler's selector was registered with the `:abortable` modifier, the signal is also aborted when the same listener fires again on the same target.
signal
}) => {
event: SubmitEventevent.Event.preventDefault(): void
The **`preventDefault()`** method of the Event interface tells the user agent that the event is being explicitly handled, so its default action, such as page scrolling, link navigation, or pasting text, should not be taken. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault)
preventDefault
();
state: IslandState<MergeState<Record<never, never>, "loading", boolean>>state.
loading: MarkedSignalAccessor
(value: boolean) => void (+1 overload)
loading
(true);
try { await function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
fetch
("/api/submit", { RequestInit.method?: string | undefined
A string to set request's method.
method
: "POST", RequestInit.signal?: AbortSignal | null | undefined
An AbortSignal to set request's signal.
signal
});
} finally { state: IslandState<MergeState<Record<never, never>, "loading", boolean>>state.
loading: MarkedSignalAccessor
(value: boolean) => void (+1 overload)
loading
(false);
} }) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<never, never>, "loading", boolean>>render(({ state: IslandState<MergeState<Record<never, never>, "loading", boolean>>state }) => ( <IntrinsicElements[string]: anyform> <IntrinsicElements[string]: anybutton type: stringtype="submit" disabled: booleandisabled={state: IslandState<MergeState<Record<never, never>, "loading", boolean>>state.
loading: MarkedSignalAccessor
() => boolean (+1 overload)
loading
()}>
{state: IslandState<MergeState<Record<never, never>, "loading", boolean>>state.
loading: MarkedSignalAccessor
() => boolean (+1 overload)
loading
() ? "Submitting…" : "Submit"}
</IntrinsicElements[string]: anybutton> </IntrinsicElements[string]: anyform> ));

Multiple listeners

Chain .on() as many times as needed. Each call adds an independent listener:

import 
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha from "ilha"; const const Counter: Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>Counter =
const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
    html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
    raw: (value: string) => RawHtml;
    mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
    from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
    ... 4 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>>.state<number, "count">(key: "count", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>state("count", 0) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>.on<"[data-action=increment]@click">(selectorOrCombined: "[data-action=increment]@click", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, "click", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>on("[data-action=increment]@click", ({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) => state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
(value: number) => void (+1 overload)
count
(state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
() => number (+1 overload)
count
() + 1),
) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>.on<"[data-action=decrement]@click">(selectorOrCombined: "[data-action=decrement]@click", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, "click", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>on("[data-action=decrement]@click", ({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) => state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
(value: number) => void (+1 overload)
count
(state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
() => number (+1 overload)
count
() - 1),
) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>.on<"[data-action=reset]@click">(selectorOrCombined: "[data-action=reset]@click", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, "click", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>on("[data-action=reset]@click", ({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) => state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
(value: number) => void (+1 overload)
count
(0),
) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>render(({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) => ( <IntrinsicElements[string]: anydiv> <IntrinsicElements[string]: anyp>{state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
() => number (+1 overload)
count
()}</IntrinsicElements[string]: anyp>
<IntrinsicElements[string]: anybutton data-action: stringdata-action="increment">+</IntrinsicElements[string]: anybutton> <IntrinsicElements[string]: anybutton data-action: stringdata-action="decrement"></IntrinsicElements[string]: anybutton> <IntrinsicElements[string]: anybutton data-action: stringdata-action="reset">Reset</IntrinsicElements[string]: anybutton> </IntrinsicElements[string]: anydiv> ));

Implicit batching

Multiple synchronous state writes inside a single handler produce one re-render, not one per write:

.on("@click", ({ state }) => {
  state.a(1);
  state.b(2);
  state.c(3); // → one render, not three
})

Dev mode warnings

In development, if a selector matches no elements at mount time, ilha logs a warning. This is not an error — the element may not exist yet if it is rendered conditionally. The warning is suppressed in production.

Notes

  • Listeners are attached to the island host and use standard addEventListener under the hood — there is no event delegation layer.
  • Selectors are evaluated with querySelectorAll at mount time and after each re-render. If new matching elements appear after mount, they are picked up automatically on the next re-render cycle.
  • The once modifier tracks fired listeners per entry. If the island re-renders before a once listener fires, the listener is still considered active and will not be re-attached.