Installation
ilha can be installed with your package manager of choice. For a beta project, start from a template when possible so SSR, mounting, and deployment wiring are already in place.
Install
Install with your package manager:
Templates
If you want to start from a ready-made project instead of wiring everything manually, use one of the official templates.
| Template | Command | Sandbox |
|---|---|---|
| Vite | npx giget@latest gh:ilhajs/ilha/templates/vite | Open |
| Hono | npx giget@latest gh:ilhajs/ilha/templates/hono | Open |
| Nitro | npx giget@latest gh:ilhajs/ilha/templates/nitro | Open |
| Elysia | npx giget@latest gh:ilhajs/ilha/templates/elysia | — |
Templates are the fastest way to get a working project structure for SSR, routing, and deployment targets without setting everything up from scratch.
Requirements
ilha is designed for modern JavaScript and TypeScript projects.
- Use it in apps that can run ESM modules.
- Use TypeScript if you want the best editor support.
- Use a browser environment for mounting and hydration.
- Keep props and hydration snapshots JSON-serializable when rendering on the server.
Import
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, {
const html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtmlhtml,
const raw: (value: string) => RawHtmlraw,
const css: (strings: TemplateStringsArray | string, ...values: (string | number)[]) => stringcss,
const mount: (registry: IslandRegistry, options?: MountOptions) => MountResultmount,
const from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | nullfrom,
const context: <T>(key: string, initial: T) => ContextSignal<T>context,
} from "ilha";
Use the default export for the builder chain, and named exports for helpers such as html, raw, and mount.
Minimal example
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 Counter: Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>Counter = 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, "count">(key: "count", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>state("count", 0)
.IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>.on<"button@click">(selectorOrCombined: "button@click", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, "click", Record<never, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>on("button@click", ({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) =>
state: IslandState<MergeState<Record<never, never>, "count", number>>state.count: MarkedSignalAccessor
(value: number) => void (+1 overload)
count(state: IslandState<MergeState<Record<never, never>, "count", number>>state.count: MarkedSignalAccessor
() => number (+1 overload)
count() + 1),
)
.IlhaBuilder<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>render(({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) => (
<IntrinsicElements[string]: anydiv>
<IntrinsicElements[string]: anyp>Count: {state: IslandState<MergeState<Record<never, never>, "count", number>>state.count: MarkedSignalAccessor
() => number (+1 overload)
count()}</IntrinsicElements[string]: anyp>
<IntrinsicElements[string]: anybutton>Increment</IntrinsicElements[string]: anybutton>
</IntrinsicElements[string]: anydiv>
));
Server-side rendering
Render the island to an HTML string with toString():
const const htmlOutput: stringhtmlOutput = const Counter: Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>Counter.Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>.toString(props?: Partial<Record<string, unknown>> | undefined): stringtoString();
If your island uses async derived values, you can also await the island itself:
const const htmlOutput: stringhtmlOutput = await const Counter: Island
(props?: Partial<Record<string, unknown>> | undefined) => string | Promise<string>
Counter();
Client-side mounting
Mount the island into a DOM element:
const const root: HTMLElement | nullroot = 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.getElementById(elementId: string): HTMLElement | nullThe **`getElementById()`** method of the Document interface returns an Element object representing the element whose id property matches the specified string. Since element IDs are required to be unique if specified, they're a useful way to get access to a specific element quickly.getElementById("app");
if (const root: HTMLElement | nullroot) {
const const unmount: () => voidunmount = const Counter: Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>Counter.Island<Record<string, unknown>, MergeState<Record<never, never>, "count", number>>.mount(host: Element, props?: Partial<Record<string, unknown>> | undefined): () => voidmount(const root: HTMLElementroot);
}
The returned function stops listeners, effects, and other active behavior. Call it when removing the host element manually or when integrating ilha into another router.