Skip to content

Installation

Install ilha and start building with official templates for Vite SPA, Nitro SSR, and Nitro + oRPC SPA.

Updated View as Markdown

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:

npm i ilha

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 SPA npx giget@latest gh:ilhajs/ilha/templates/vite-spa Open
Nitro SSR npx giget@latest gh:ilhajs/ilha/templates/nitro-ssr Open
Nitro + oRPC SPA npx giget@latest gh:ilhajs/ilha/templates/nitro-orpc-spa Open

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: 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
, { 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>) & {
    delete(key: string): boolean;
    clear(): void;
}
context
} from "ilha";

Use the default export to create an island with ilha(() => JSX) or a configured builder chain. Use named exports for helpers such as raw and mount.

Minimal example

Create an island directly when it does not need builder configuration:

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 Greeting: Island<RootInput, RootState>Greeting = ilha<RootInput>(fn: (ctx: RenderContext<RootInput, RootState, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, RootState>ilha(() => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>Hello, ilha!</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);

Start with a plain function for markup owned by another island, then wrap it with ilha() when it needs an independent lifecycle. Expand the shorthand into a builder chain when you need local state or actions:

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 Counter: Island<RootInput, MergeState<RootState, "count", number>>Counter =
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, "count">(key: "count", init?: StateInit<RootInput, number> | undefined): IlhaBuilder<RootInput, MergeState<RootState, "count", number>, RootDerived, RootActions>state("count", 0) .IlhaBuilder<RootInput, MergeState<RootState, "count", number>, RootDerived, RootActions>.action<"increment", undefined, void>(key: "increment", fn: (props: undefined, ctx: ActionContext<RootInput, MergeState<RootState, "count", number>, RootDerived>) => void): IlhaBuilder<RootInput, MergeState<RootState, "count", number>, RootDerived, RootActions & Record<"increment", (props: undefined, ctx: ActionContext<RootInput, MergeState<RootState, "count", number>, RootDerived>) => void>>action("increment", (_: undefined_, { state: IslandState<MergeState<RootState, "count", number>>state }) => { state: IslandState<MergeState<RootState, "count", number>>state.
count: MarkedSignalAccessor
(value: SignalSetter<number>) => void (+1 overload)
count
((count: numbercount) => count: numbercount + 1);
}) .IlhaBuilder<RootInput, MergeState<RootState, "count", number>, RootDerived, RootActions & Record<"increment", (props: undefined, ctx: ActionContext<...>) => void>>.render(fn: (ctx: RenderContext<RootInput, MergeState<RootState, "count", number>, RootDerived, RootActions & Record<"increment", (props: undefined, ctx: ActionContext<RootInput, MergeState<RootState, "count", number>, RootDerived>) => void>>) => string | RawHtml): Island<RootInput, MergeState<RootState, "count", number>>render(({ state: IslandState<MergeState<RootState, "count", number>>state, action: IslandActions<RootActions & Record<"increment", (props: undefined, ctx: ActionContext<RootInput, MergeState<RootState, "count", number>, RootDerived>) => void>>action }) => ( <
"button": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLButtonElement> & ButtonAttributes & {
    popovertarget?: string;
    popoverTarget?: string;
    popovertargetaction?: "hide" | "show" | "toggle";
    popoverTargetAction?: "hide" | "show" | "toggle";
}>
button
onclick?: NativeEventHandler<PointerEvent & {
    readonly currentTarget: HTMLButtonElement;
}> | undefined
onclick
={action: IslandActions<RootActions & Record<"increment", (props: undefined, ctx: ActionContext<RootInput, MergeState<RootState, "count", number>, RootDerived>) => void>>action.increment: ActionAccessor<undefined, void>increment}>
Count: {state: IslandState<MergeState<RootState, "count", number>>state.
count: MarkedSignalAccessor
() => number (+1 overload)
count
()}
</
"button": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLButtonElement> & ButtonAttributes & {
    popovertarget?: string;
    popoverTarget?: string;
    popovertargetaction?: "hide" | "show" | "toggle";
    popoverTargetAction?: "hide" | "show" | "toggle";
}>
button
>
));

Server-side rendering

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

const const htmlOutput: stringhtmlOutput = const Counter: Island<RootInput, MergeState<RootState, "count", number>>Counter.Island<RootInput, MergeState<RootState, "count", number>>.toString(props?: Partial<RootInput> | 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<RootInput> | 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 | 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 | nullroot) { const const unmount: () => voidunmount = const Counter: Island<RootInput, MergeState<RootState, "count", number>>Counter.Island<RootInput, MergeState<RootState, "count", number>>.mount(host: Element, props?: Partial<RootInput> | 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.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close