.on()
Register a delegated or host event listener with a CSS selector, island-host listener, full handler context, and optional modifiers.
.on() registers an event listener for a CSS selector, the island host, or both, with the full island handler context. Use it when a native event prop cannot express the target — a selector that matches conditionally rendered elements, an island-host listener, or combined modifiers.
Prefer lowercase native JSX event props when one rendered element owns the event. Use .on() when you need a selector, a host listener, the full handler context, or multiple listener modifiers.
Selector syntax
The first argument combines an optional selector and event name with @:
.on("button@click", handler) // descendant buttons
.on("@click", handler) // island host
.on("button@click:once:capture", handler) // combined modifiers
Combined with actions
Declare actions before .on() to expose them through the handler context:
import { const ilha: RootBuilder & DirectIslandFactory & {
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;
... 6 more ...;
onUncaughtError: typeof onUncaughtError;
}
ilha } from "ilha";
const const Registration: Island<RootInput, RootState>Registration = const ilha: RootBuilder & DirectIslandFactory & {
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;
... 6 more ...;
onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.action<"registerUser", SubmitEvent, Promise<void>>(key: "registerUser", fn: (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<void>): IlhaBuilder<RootInput, RootState, RootDerived, RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<void>>>action(
"registerUser",
async (event: SubmitEventevent: SubmitEvent, { signal: AbortSignalsignal }) => {
event: SubmitEventevent.Event.preventDefault(): voidThe **`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();
const const form: HTMLFormElementform = event: SubmitEventevent.Event.currentTarget: EventTarget | nullThe **`currentTarget`** read-only property of the Event interface identifies the element to which the event handler has been attached.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget)currentTarget as HTMLFormElement;
await function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)fetch("/api/register", {
RequestInit.method?: string | undefinedA string to set request's method.method: "POST",
RequestInit.body?: BodyInit | null | undefinedA BodyInit object or null to set request's body.body: new var FormData: new (form?: HTMLFormElement, submitter?: HTMLElement | null) => FormDataThe **`FormData`** interface provides a way to construct a set of key/value pairs representing form fields and their values, which can be sent using the fetch(), XMLHttpRequest.send() or navigator.sendBeacon() methods. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData)FormData(const form: HTMLFormElementform),
RequestInit.signal?: AbortSignal | null | undefinedAn AbortSignal to set request's signal.signal,
});
},
)
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<...>>>.on<"form@submit">(selectorOrCombined: "form@submit", handler: (ctx: HandlerContextFor<RootInput, RootState, "submit", RootDerived, RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<void>>>) => void | Promise<void>): IlhaBuilder<RootInput, RootState, RootDerived, RootActions & Record<...>>on("form@submit", ({ action: IslandActions<RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<void>>>action, event: SubmitEventevent }) =>
action: IslandActions<RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<void>>>action.registerUser: (props: SubmitEvent) => voidregisterUser(event: SubmitEventevent),
)
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<...>>>.render(fn: (ctx: RenderContext<RootInput, RootState, RootDerived, RootActions & Record<"registerUser", (props: SubmitEvent, ctx: ActionContext<RootInput, RootState, RootDerived>) => Promise<void>>>) => string | RawHtml): Island<RootInput, RootState>render(() => (
<"form": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLFormElement> & FormAttributes>form>
<"input": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLInputElement> & InputAttributes & {
popovertarget?: string;
popoverTarget?: string;
popovertargetaction?: "hide" | "show" | "toggle";
popoverTargetAction?: "hide" | "show" | "toggle";
}>
input name?: string | RawHtml | undefinedname="email" type?: "number" | RawHtml | "submit" | "reset" | "button" | "search" | "time" | "image" | "text" | "hidden" | "email" | "checkbox" | "color" | "date" | "datetime-local" | "file" | "month" | "password" | "radio" | "range" | "tel" | "url" | "week" | undefinedtype="email" />
<"button": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLButtonElement> & ButtonAttributes & {
popovertarget?: string;
popoverTarget?: string;
popovertargetaction?: "hide" | "show" | "toggle";
popoverTargetAction?: "hide" | "show" | "toggle";
}>
button>Register</"button": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLButtonElement> & ButtonAttributes & {
popovertarget?: string;
popoverTarget?: string;
popovertargetaction?: "hide" | "show" | "toggle";
popoverTargetAction?: "hide" | "show" | "toggle";
}>
button>
</"form": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLFormElement> & FormAttributes>form>
));
Handler context
The handler receives state, derived, action, input, host, target, event, and signal. Synchronous signal writes are batched, listeners clean up on unmount, and thrown errors route through .onError().
Ilha resolves selectors again after each morph. Use .on() when template composition prevents a direct native event interpolation or when matching elements appear conditionally.
Error handling
A throw or rejection in an .on() handler reaches the island error sink with source: "on":
- Local
.onError()handlers - Global
onUncaughtError()handlers console.error
An AbortError caused by cancellation (via :abortable race-cancel or unmount) is filtered out and does not reach the sink.
Notes
- Prefer native event props for element-owned events.
.on()also runs client-only — it never runs during SSR.AbortErrorrejections are filtered, not treated as errors.