Effect

Registers a reactive side effect that runs after the island mounts and re-runs automatically whenever any signal it reads changes. Use it to sync state to the outside world — the DOM, browser APIs, timers, or external systems.

Interactive Tutorial

Basic usage

Every time state.title changes, the effect re-runs and updates document.title.

Cleanup

Return a function from the effect to clean up before the next run or on unmount:

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>, "delay", number>>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<number, "delay">(key: "delay", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>, Record<never, never>>state("delay", 1000) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>, Record<never, never>>.effect(fn: (ctx: EffectContext<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>, Record<never, never>>) => (() => void) | void): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>, Record<never, never>>effect(({ state: IslandState<MergeState<Record<never, never>, "delay", number>>state }) => { const const id: numberid = function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setInterval)
setInterval
(() => {
var console: Consoleconsole.Console.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
("tick");
}, state: IslandState<MergeState<Record<never, never>, "delay", number>>state.
delay: MarkedSignalAccessor
() => number (+1 overload)
delay
());
return () => function clearInterval(id: number | undefined): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/clearInterval)
clearInterval
(const id: numberid);
}) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<never, never>, "delay", number>>render(({ state: IslandState<MergeState<Record<never, never>, "delay", number>>state }) => <IntrinsicElements[string]: anyp>Interval: {state: IslandState<MergeState<Record<never, never>, "delay", number>>state.
delay: MarkedSignalAccessor
() => number (+1 overload)
delay
()}ms</IntrinsicElements[string]: anyp>);

The cleanup runs before the effect re-runs with new values, and once more on unmount. This prevents stale timers, subscriptions, or event listeners from accumulating.

Cancelling async work with ctx.signal

Unlike .on(), race-cancellation is the default behaviour for effects (no modifier needed). When a dependency changes, the previous run's signal aborts automatically. Pass signal to async work to bail out of stale invocations:

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<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>>
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<number, "userId">(key: "userId", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "userId", number>, Record<never, never>>state("userId", 1) .
IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "userId", number>, Record<never, never>>.state<{
    name: string;
} | null, "user">(key: "user", init?: StateInit<Record<string, unknown>, {
    name: string;
} | null> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>, Record<never, never>>
state
("user", null as { name: stringname: string } | null)
.
IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", { name: string; } | null>, Record<...>>.effect(fn: (ctx: EffectContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>, Record<never, never>>) => (() => void) | void): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>, Record<never, never>>
effect
(({
state: IslandState<MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>>
state
, signal: AbortSignal
AbortSignal that aborts when the effect re-runs (because a dependency changed) or when the island unmounts. Pass to `fetch` or check `signal.aborted` after `await` boundaries to bail out of stale work without needing a manual cleanup function.
signal
}) => {
function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
fetch
(`/api/users/${
state: IslandState<MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>>
state
.
userId: MarkedSignalAccessor
() => number (+1 overload)
userId
()}`, { RequestInit.signal?: AbortSignal | null | undefined
An AbortSignal to set request's signal.
signal
})
.Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>
Attaches callbacks for the resolution and/or rejection of the Promise.
@paramonfulfilled The callback to execute when the Promise is resolved.@paramonrejected The callback to execute when the Promise is rejected.@returnsA Promise for the completion of which ever callback is executed.
then
((res: Responseres) => {
if (signal: AbortSignal
AbortSignal that aborts when the effect re-runs (because a dependency changed) or when the island unmounts. Pass to `fetch` or check `signal.aborted` after `await` boundaries to bail out of stale work without needing a manual cleanup function.
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;
return res: Responseres.Body.json(): Promise<any>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)
json
();
}) .Promise<any>.then<void, never>(onfulfilled?: ((value: any) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
Attaches callbacks for the resolution and/or rejection of the Promise.
@paramonfulfilled The callback to execute when the Promise is resolved.@paramonrejected The callback to execute when the Promise is rejected.@returnsA Promise for the completion of which ever callback is executed.
then
((data: anydata) => {
if (signal: AbortSignal
AbortSignal that aborts when the effect re-runs (because a dependency changed) or when the island unmounts. Pass to `fetch` or check `signal.aborted` after `await` boundaries to bail out of stale work without needing a manual cleanup function.
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>, "userId", number>, "user", {
    name: string;
} | null>>
state
.
user: MarkedSignalAccessor
(value: {
    name: string;
} | null) => void (+1 overload)
user
(data: anydata as { name: stringname: string });
}) .Promise<void>.catch<void>(onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
Attaches a callback for only the rejection of the Promise.
@paramonrejected The callback to execute when the Promise is rejected.@returnsA Promise for the completion of the callback.
catch
((err: anyerr) => {
if (err: anyerr && err: anyerr.name === "AbortError") return; throw err: anyerr; }); }) .
IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", { name: string; } | null>, Record<...>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>>
render
(({
state: IslandState<MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>>
state
}) => (
<IntrinsicElements[string]: anyp>{
state: IslandState<MergeState<MergeState<Record<never, never>, "userId", number>, "user", {
    name: string;
} | null>>
state
.
user: MarkedSignalAccessor
() => {
    name: string;
} | null (+1 overload)
user
()?.name: string | undefinedname ?? "Loading…"}</IntrinsicElements[string]: anyp>
));

Both the user-supplied cleanup function (if any) and the signal abort fire when the effect re-runs, so you can mix patterns.

Effect context

The effect function receives an EffectContext:

{
  state: IslandState; // reactive state signals
  derived: IslandDerived; // derived signal accessors (same as render / .on())
  input: TInput; // resolved input props
  host: Element; // island root element
  signal: AbortSignal; // aborts when the effect re-runs or the island unmounts
}

Reading derived.name() subscribes the effect, like state. Writing derived.name(value) for optimistic UI does not subscribe — only reads and envelope property access (.loading, .value, .error) are tracked.

Multiple effects

Chain .effect() as many times as needed. Each runs independently with its own dependency tracking:

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<MergeState<Record<never, never>, "title", string>, "color", 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, "title">(key: "title", init?: StateInit<Record<string, unknown>, string> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "title", string>, Record<never, never>>state("title", "Hello") .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "title", string>, Record<never, never>>.state<string, "color">(key: "color", init?: StateInit<Record<string, unknown>, string> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>state("color", "teal") .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>.effect(fn: (ctx: EffectContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>) => (() => void) | void): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>effect(({ state: IslandState<MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>state }) => { var document: Document
**`window.document`** returns a reference to the document contained in the window. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)
document
.Document.title: string
The **`document.title`** property gets or sets the current title of the document. When present, it defaults to the value of the <title>. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/title)
title
= state: IslandState<MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>state.
title: MarkedSignalAccessor
() => string (+1 overload)
title
();
}) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>.effect(fn: (ctx: EffectContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>) => (() => void) | void): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>effect(({ state: IslandState<MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>state }) => { var document: Document
**`window.document`** returns a reference to the document contained in the window. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)
document
.Document.body: HTMLElement
The **`Document.body`** property represents the <body> or <frameset> node of the current document, or null if no such element exists. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/body)
body
.ElementCSSInlineStyle.style: CSSStyleDeclaration
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/style)
style
.CSSStyleProperties.backgroundColor: string
The background-color CSS property sets the background color of an element. [MDN Reference](https://developer.mozilla.org/docs/Web/CSS/Reference/Properties/background-color)
backgroundColor
= state: IslandState<MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>state.
color: MarkedSignalAccessor
() => string (+1 overload)
color
();
}) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>render(({ state: IslandState<MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>state }) => <IntrinsicElements[string]: anyp>{state: IslandState<MergeState<MergeState<Record<never, never>, "title", string>, "color", string>>state.
title: MarkedSignalAccessor
() => string (+1 overload)
title
()}</IntrinsicElements[string]: anyp>);

Implicit batching

Multiple synchronous state writes inside an effect run propagate atomically — dependents see the final state and run once instead of once per write:

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<MergeState<Record<never, never>, "a", number>, "b", number>>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<number, "a">(key: "a", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "a", number>, Record<never, never>>state("a", 0) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "a", number>, Record<never, never>>.state<number, "b">(key: "b", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>, Record<never, never>>state("b", 0) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>, Record<never, never>>.effect(fn: (ctx: EffectContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>, Record<never, never>>) => (() => void) | void): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>, Record<never, never>>effect(({ state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state }) => { // These two writes produce a single re-render. state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
(value: number) => void (+1 overload)
a
(state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
() => number (+1 overload)
a
() + 1);
state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
(value: number) => void (+1 overload)
b
(state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
() => number (+1 overload)
b
() + 1);
var console: Consoleconsole.Console.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
(state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
() => number (+1 overload)
a
(), state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
() => number (+1 overload)
b
());
}) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>render(({ state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state }) => ( <IntrinsicElements[string]: anyp> {state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
() => number (+1 overload)
a
()} {state: IslandState<MergeState<MergeState<Record<never, never>, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
() => number (+1 overload)
b
()}
</IntrinsicElements[string]: anyp> ));

Conditional reads

Dependencies are tracked based on which signals are actually read during a run. Signals inside a branch that does not execute are not tracked:

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<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>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<boolean, "enabled">(key: "enabled", init?: StateInit<Record<string, unknown>, boolean> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "enabled", boolean>, Record<never, never>>state("enabled", false) .IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "enabled", boolean>, Record<never, never>>.state<number, "value">(key: "value", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>, Record<never, never>>state("value", 0) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>, Record<never, never>>.effect(fn: (ctx: EffectContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>, Record<never, never>>) => (() => void) | void): IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>, Record<never, never>>effect(({ state: IslandState<MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>state }) => { if (!state: IslandState<MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>state.
enabled: MarkedSignalAccessor
() => boolean (+1 overload)
enabled
()) return; // if false, state.value is never read
var console: Consoleconsole.Console.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
(state: IslandState<MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>state.
value: MarkedSignalAccessor
() => number (+1 overload)
value
()); // only tracked when enabled is true
}) .IlhaBuilder<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>render(({ state: IslandState<MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>state }) => <IntrinsicElements[string]: anyp>{state: IslandState<MergeState<MergeState<Record<never, never>, "enabled", boolean>, "value", number>>state.
value: MarkedSignalAccessor
() => number (+1 overload)
value
()}</IntrinsicElements[string]: anyp>);

This means the effect only re-runs when state.value changes if state.enabled was true during the last run.

.effect() vs .onMount()

Both run after mount, but they serve different purposes:

.effect().onMount()
Re-runsYes, when dependencies changeNo, runs once
Tracks signalsYesNo
Receives derivedYesYes
Cleanup supportYesYes
Use forReactive sync to external APIsOne-time setup

If you need something to happen only once after mount, use .onMount(). If you need it to stay in sync with state over time, use .effect().

Notes

  • Effects run client-side only. They are not called during SSR.
  • Effects are registered after .onMount() (and after any enter transition). See Transition — mount order.
  • Sync throws from an effect (or its cleanup) go to .onError() with source: "effect". Unhandled promise rejections from fire-and-forget async inside an effect are not caught — use await or .catch() inside the effect.
  • The first effect run happens soon after mount; keep effect bodies fast to avoid blocking rendering.
  • Avoid writing to signals inside an effect that reads those same signals — this creates an infinite loop.