Skip to content

.effect()

Register reactive side effects that run after mount and re-run when signal dependencies change.

Updated View as Markdown

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: 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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
from "ilha";
const const Island: Island<RootInput, MergeState<RootState, "delay", number>>Island =
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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.state<number, "delay">(key: "delay", init?: StateInit<RootInput, number> | undefined): IlhaBuilder<RootInput, MergeState<RootState, "delay", number>, RootDerived, RootActions>state("delay", 1000) .IlhaBuilder<RootInput, MergeState<RootState, "delay", number>, RootDerived, RootActions>.effect(fn: (ctx: EffectContext<RootInput, MergeState<RootState, "delay", number>, RootDerived, RootActions>) => (() => void) | void): IlhaBuilder<RootInput, MergeState<RootState, "delay", number>, RootDerived, RootActions>effect(({ state: IslandState<MergeState<RootState, "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<RootState, "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<RootInput, MergeState<RootState, "delay", number>, RootDerived, RootActions>.render(fn: (ctx: RenderContext<RootInput, MergeState<RootState, "delay", number>, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, MergeState<RootState, "delay", number>>render(({ state: IslandState<MergeState<RootState, "delay", number>>state }) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>Interval: {state: IslandState<MergeState<RootState, "delay", number>>state.
delay: MarkedSignalAccessor
() => number (+1 overload)
delay
()}ms</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);

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: 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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
from "ilha";
const
const Island: Island<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>>
Island
=
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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.state<number, "userId">(key: "userId", init?: StateInit<RootInput, number> | undefined): IlhaBuilder<RootInput, MergeState<RootState, "userId", number>, RootDerived, RootActions>state("userId", 1) .
IlhaBuilder<RootInput, MergeState<RootState, "userId", number>, RootDerived, RootActions>.state<{
    name: string;
} | null, "user">(key: "user", init?: StateInit<RootInput, {
    name: string;
} | null> | undefined): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>, RootDerived, RootActions>
state
("user", null as { name: stringname: string } | null)
.
IlhaBuilder<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", { name: string; } | null>, RootDerived, RootActions>.effect(fn: (ctx: EffectContext<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>, RootDerived, RootActions>) => (() => void) | void): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>, RootDerived, RootActions>
effect
(({
state: IslandState<MergeState<MergeState<RootState, "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<RootState, "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<RootState, "userId", number>, "user", {
    name: string;
} | null>>
state
.
user: MarkedSignalAccessor
(value: SignalSetter<{
    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<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", { name: string; } | null>, RootDerived, RootActions>.render(fn: (ctx: RenderContext<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>>
render
(({
state: IslandState<MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>>
state
}) => (
<"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>{
state: IslandState<MergeState<MergeState<RootState, "userId", number>, "user", {
    name: string;
} | null>>
state
.
user: MarkedSignalAccessor
() => {
    name: string;
} | null (+1 overload)
user
()?.name: string | undefinedname ?? "Loading…"}</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>
));

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())
  action: IslandActions; // callable actions and their reactive status
  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: 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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
from "ilha";
const const Island: Island<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>>Island =
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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.state<string, "title">(key: "title", init?: StateInit<RootInput, string> | undefined): IlhaBuilder<RootInput, MergeState<RootState, "title", string>, RootDerived, RootActions>state("title", "Hello") .IlhaBuilder<RootInput, MergeState<RootState, "title", string>, RootDerived, RootActions>.state<string, "color">(key: "color", init?: StateInit<RootInput, string> | undefined): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>state("color", "teal") .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>.effect(fn: (ctx: EffectContext<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>) => (() => void) | void): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>effect(({ state: IslandState<MergeState<MergeState<RootState, "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<RootState, "title", string>, "color", string>>state.
title: MarkedSignalAccessor
() => string (+1 overload)
title
();
}) .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>.effect(fn: (ctx: EffectContext<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>) => (() => void) | void): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>effect(({ state: IslandState<MergeState<MergeState<RootState, "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<RootState, "title", string>, "color", string>>state.
color: MarkedSignalAccessor
() => string (+1 overload)
color
();
}) .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>.render(fn: (ctx: RenderContext<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, MergeState<MergeState<RootState, "title", string>, "color", string>>render(({ state: IslandState<MergeState<MergeState<RootState, "title", string>, "color", string>>state }) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>{state: IslandState<MergeState<MergeState<RootState, "title", string>, "color", string>>state.
title: MarkedSignalAccessor
() => string (+1 overload)
title
()}</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);

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: 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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
from "ilha";
const const Island: Island<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>>Island =
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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.state<number, "a">(key: "a", init?: StateInit<RootInput, number> | undefined): IlhaBuilder<RootInput, MergeState<RootState, "a", number>, RootDerived, RootActions>state("a", 0) .IlhaBuilder<RootInput, MergeState<RootState, "a", number>, RootDerived, RootActions>.state<number, "b">(key: "b", init?: StateInit<RootInput, number> | undefined): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>, RootDerived, RootActions>state("b", 0) .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>, RootDerived, RootActions>.effect(fn: (ctx: EffectContext<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>, RootDerived, RootActions>) => (() => void) | void): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>, RootDerived, RootActions>effect(({ state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state }) => { // These two writes produce a single re-render. state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
(value: SignalSetter<number>) => void (+1 overload)
a
((a: numbera) => a: numbera + 1);
state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
(value: SignalSetter<number>) => void (+1 overload)
b
((b: numberb) => b: numberb + 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<RootState, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
() => number (+1 overload)
a
(), state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
() => number (+1 overload)
b
());
}) .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>, RootDerived, RootActions>.render(fn: (ctx: RenderContext<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, MergeState<MergeState<RootState, "a", number>, "b", number>>render(({ state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state }) => ( <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p> {state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state.
a: MarkedSignalAccessor
() => number (+1 overload)
a
()} {state: IslandState<MergeState<MergeState<RootState, "a", number>, "b", number>>state.
b: MarkedSignalAccessor
() => number (+1 overload)
b
()}
</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p> ));

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: 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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
from "ilha";
const const Island: Island<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>>Island =
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;
    ... 5 more ...;
    onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.state<boolean, "enabled">(key: "enabled", init?: StateInit<RootInput, boolean> | undefined): IlhaBuilder<RootInput, MergeState<RootState, "enabled", boolean>, RootDerived, RootActions>state("enabled", false) .IlhaBuilder<RootInput, MergeState<RootState, "enabled", boolean>, RootDerived, RootActions>.state<number, "value">(key: "value", init?: StateInit<RootInput, number> | undefined): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>, RootDerived, RootActions>state("value", 0) .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>, RootDerived, RootActions>.effect(fn: (ctx: EffectContext<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>, RootDerived, RootActions>) => (() => void) | void): IlhaBuilder<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>, RootDerived, RootActions>effect(({ state: IslandState<MergeState<MergeState<RootState, "enabled", boolean>, "value", number>>state }) => { if (!state: IslandState<MergeState<MergeState<RootState, "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<RootState, "enabled", boolean>, "value", number>>state.
value: MarkedSignalAccessor
() => number (+1 overload)
value
()); // only tracked when enabled is true
}) .IlhaBuilder<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>, RootDerived, RootActions>.render(fn: (ctx: RenderContext<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, MergeState<MergeState<RootState, "enabled", boolean>, "value", number>>render(({ state: IslandState<MergeState<MergeState<RootState, "enabled", boolean>, "value", number>>state }) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>{state: IslandState<MergeState<MergeState<RootState, "enabled", boolean>, "value", number>>state.
value: MarkedSignalAccessor
() => number (+1 overload)
value
()}</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);

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.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close