Skip to content
Ilha
Esc
↑↓navigate↵open⌘Jpreview
On this page

Choose an API

One canonical API for each task, plus the patterns you never write in Ilha.

Ilha gives you exactly one way to do each task. Find your task in the table and use that API.

Task to API

Task Use Guide
Local state atom(0) State management
Derived value atom(Atom.map(source.atom, fn)) State management
Derived from several atoms atom(Atom.transform(source.atom, (get) => ...)) State management
One-time init atom.lazy(() => expensive()) State management
React to a change watch(source, fn) State management
Mount-only effect watch.once(fn) State management
Async data atom(Effect), read as an AsyncResult Data fetching
Live data yield a Stream from a generator component Streams
Per-emission interruptible flow yield* when(stream, body) Streams
Values shared down the tree createContext() and context() Context and errors
Failures in a subtree <ErrorBoundary fallback={...}> Context and errors
Server render await renderToString(Component) Render and hydrate
Client render mount(host, Component) Render and hydrate
Hydrate server HTML mount(host, Component, { hydrate: true }) Render and hydrate
Raw, trusted HTML unsafe(html) Templating

Example

State, a derived value, and a reaction use three different APIs. Each task has one.

import * as import AtomAtom from "effect/reactivity/Atom";
import { const atom: AtomFnatom, const watch: WatchFnwatch } from "ilha";

const const Cart: () => ViewCart = () => {
  const 
const items: AtomHandle<{
    id: string;
    n: number;
}[]>
items
=
atom<{
    id: string;
    n: number;
}[]>(init: {
    id: string;
    n: number;
}[] | Atom.Atom<{
    id: string;
    n: number;
}[]>, options?: AtomOptions<{
    id: string;
    n: number;
}[]> | undefined): AtomHandle<{
    id: string;
    n: number;
}[]> (+1 overload)
An existing Effect atom, or a plain value. Store a function with `atom.lazy(() => fn)`; the `ilha/oxlint` rule `function-in-atom` flags `atom(fn)`.
atom
([
{ id: stringid: "a", n: numbern: 1 }, { id: stringid: "b", n: numbern: 2 }, ]); const const total: AtomHandle<number>total = atom<number>(init: number | Atom.Atom<number>, options?: AtomOptions<number> | undefined): AtomHandle<number> (+1 overload)
An existing Effect atom, or a plain value. Store a function with `atom.lazy(() => fn)`; the `ilha/oxlint` rule `function-in-atom` flags `atom(fn)`.
atom
(
import AtomAtom.
const map: <Atom.Atom<{
    id: string;
    n: number;
}[]>, number>(self: Atom.Atom<{
    id: string;
    n: number;
}[]>, f: (_: {
    id: string;
    n: number;
}[]) => number) => Atom.Atom<number> (+1 overload)
Maps the current value of an atom with a pure function. **Details** When the source atom is writable, the returned atom remains writable and keeps the source atom's write input type.
@stabilityunstable@categorycombinators@since4.0.0
map
(
const items: AtomHandle<{
    id: string;
    n: number;
}[]>
items
.
AtomHandle<{ id: string; n: number; }[]>.atom: Atom.Atom<{
    id: string;
    n: number;
}[]>
atom
, (
list: {
    id: string;
    n: number;
}[]
list
) =>
list: {
    id: string;
    n: number;
}[]
list
.
Array<{ id: string; n: number; }>.reduce<number>(callbackfn: (previousValue: number, currentValue: {
    id: string;
    n: number;
}, currentIndex: number, array: {
    id: string;
    n: number;
}[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum,
item: {
    id: string;
    n: number;
}
item
) => sum: numbersum +
item: {
    id: string;
    n: number;
}
item
.n: numbern, 0))
); watch<number>(source: Atom.Atom<number> | AtomHandle<number> | Stream<number, unknown, unknown>, fn: WatchCallback<number>): Instruction<void>
Run `fn` with the current value of `source` now and on every change.
watch
(const total: AtomHandle<number>total, (value: numbervalue) => {
var localStorage: Storage
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
localStorage
.Storage.setItem(key: string, value: string): void
The **`setItem()`** method of the Storage interface, when passed a key name and value, will add that key to the given Storage object, or update that key's value if it already exists. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Storage/setItem)
setItem
("cart:total",
var String: StringConstructor
(value?: any) => string
Allows manipulation and formatting of text strings and determination and location of substrings within strings.
String
(value: numbervalue));
}); return <JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>{const total: AtomHandle<number>total}</JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>; };

atom() holds state, Atom.map derives the total, and watch() reacts when it changes. Pass items.atom to Effect APIs, then wrap the result in atom().

Never do

  • Never call atom(), watch(), or when() conditionally, in a loop, or after an early return. Slots are order-based, and a changed count triggers ILHA002.
  • Never use React hooks (useState, useEffect, useMemo). Ilha has no hook layer.
  • Never read an atom with .value or .get(). Call count(), or pass {count} in JSX.
  • Never use an atom handle from another island. It throws ILHA003.
  • Never wrap Effect Atom APIs (Atom.map, Atom.transform, Atom.fn) in your own helper. Pass handle.atom to them.
  • Never fetch inside watch.once() and copy the result into an atom. Write atom(Effect) instead.
  • Never pass untrusted input to unsafe(). It skips escaping.
  • Never import removed APIs: resource, invalidate, fromEventSource, ilha/define, or structuralEqual. See MIGRATIONS.md in the ilha package.

Check your work

Agents get machine-readable feedback at every step:

  • Each misuse raises an IlhaError with a stable ILHA00N code, a fix line, and a link to Errors.
  • ilha/test renders a component and returns its tree and atoms.
  • ilha/debug inspects live islands as JSON.
  • The Oxlint plugin catches conditional primitives before you run the code.
  • The ilha package ships AGENTS.md, llms.txt, and MIGRATIONS.md, and the site publishes /llms.txt.

Was this page helpful?