Installation

ilha can be installed with your package manager of choice.

Install

npm
yarn
pnpm
bun
deno
npm install ilha

Templates

If you want to start from a ready-made project instead of wiring everything manually, use one of the official templates.

TemplateCommandSandbox
Vitenpx giget@latest gh:ilhajs/ilha/templates/viteOpen
Hononpx giget@latest gh:ilhajs/ilha/templates/honoOpen
Nitronpx giget@latest gh:ilhajs/ilha/templates/nitroOpen
Elysianpx giget@latest gh:ilhajs/ilha/templates/elysia
Electrobunnpx giget@latest gh:ilhajs/ilha/templates/electrobun

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.

Import

import 
const ilha: IlhaBuilder<Record<string, unknown>, Record<string, never>, Record<string, 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;
    context: <T>(key: string, initial: T) => ContextSignal<...>;
}
ilha
, {
const html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml
html
,
const raw: (value: string) => RawHtml
raw
,
const css: (strings: TemplateStringsArray | string, ...values: (string | number)[]) => string
css
,
const mount: (registry: IslandRegistry, options?: MountOptions) => MountResult
mount
,
const from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null
from
,
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<string, never>, Record<string, 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;
    context: <T>(key: string, initial: T) => ContextSignal<...>;
}
ilha
, {
const html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml
html
} from "ilha";
const
const Counter: Island<Record<string, unknown>, MergeState<Record<string, never>, "count", number>>
Counter
=
const ilha: IlhaBuilder<Record<string, unknown>, Record<string, never>, Record<string, 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;
    context: <T>(key: string, initial: T) => ContextSignal<...>;
}
ilha
.
IlhaBuilder<Record<string, unknown>, Record<string, never>, Record<string, never>>.state<number, "count">(key: "count", init?: StateInit<Record<string, unknown>, number> | undefined): IlhaBuilder<Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>
state
("count", 0)
.
IlhaBuilder<Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>.on<"button@click">(selectorOrCombined: "button@click", handler: (ctx: HandlerContextFor<Record<string, unknown>, MergeState<Record<string, never>, "count", number>, "click", Record<string, never>>) => void | Promise<void>): IlhaBuilder<Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>> (+1 overload)
on
("button@click", ({
state: IslandState<MergeState<Record<string, never>, "count", number>>
state
}) =>
state: IslandState<MergeState<Record<string, never>, "count", number>>
state
.
count: MarkedSignalAccessor
(value: number) => void (+1 overload)
count
(
state: IslandState<MergeState<Record<string, never>, "count", number>>
state
.
count: MarkedSignalAccessor
() => number (+1 overload)
count
() + 1))
.
IlhaBuilder<Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>.render(fn: (ctx: RenderContext<Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>) => string | RawHtml): Island<Record<string, unknown>, MergeState<Record<string, never>, "count", number>>
render
(
({
state: IslandState<MergeState<Record<string, never>, "count", number>>
state
}) =>
const html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml
html
`
<div> <p>Count: ${
state: IslandState<MergeState<Record<string, never>, "count", number>>
state
.
count: MarkedSignalAccessor
() => number (+1 overload)
count
()}</p>
<button>Increment</button> </div> `, );

Server-side rendering

Render the island to an HTML string with toString():

const 
const htmlOutput: string
htmlOutput
=
const Counter: Island<Record<string, unknown>, MergeState<Record<string, never>, "count", number>>
Counter
.
Island<Record<string, unknown>, MergeState<Record<string, never>, "count", number>>.toString(props?: Partial<Record<string, unknown>> | undefined): string
toString
();

If your island uses async derived values, you can also await the island itself:

const 
const htmlOutput: string
htmlOutput
= 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 | null
root
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.getElementById(elementId: string): HTMLElement | null

The 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 | null
root
) {
const
const unmount: () => void
unmount
=
const Counter: Island<Record<string, unknown>, MergeState<Record<string, never>, "count", number>>
Counter
.
Island<Record<string, unknown>, MergeState<Record<string, never>, "count", number>>.mount(host: Element, props?: Partial<Record<string, unknown>> | undefined): () => void
mount
(
const root: HTMLElement
root
);
}

The returned function stops listeners, effects, and other active behavior.