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.
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[]): voidThe **`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: AbortSignalAbortSignal 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 | undefinedAn 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.then((res: Responseres) => {
if (signal: AbortSignalAbortSignal 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: booleanThe **`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.then((data: anydata) => {
if (signal: AbortSignalAbortSignal 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: booleanThe **`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.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: stringThe **`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: HTMLElementThe **`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: stringThe 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[]): voidThe **`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[]): voidThe **`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-runs Yes, when dependencies change No, runs once Tracks signals Yes No Receives derived Yes Yes Cleanup support Yes Yes Use for Reactive sync to external APIs One-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.