#Input
Declares the island's external props and their types. Two forms are supported: a type-only generic for when you just need TypeScript inference, and a Standard Schema-compatible validator (Zod, Valibot, ArkType, etc.) for runtime validation too.
#Basic usage
Type-only — TypeScript inference, no runtime validation:
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 from "ilha";
const const Greeting: Island<{
name: string;
}, Record<string, never>>
Greeting = 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>>.input<{
name: string;
}>(): IlhaBuilder<{
name: string;
}, Record<string, never>, Record<string, never>> (+1 overload)
input <{ name: string name : string }>()
.IlhaBuilder<{ name: string; }, Record<string, never>, Record<string, never>>.render(fn: (ctx: RenderContext<{
name: string;
}, Record<string, never>, Record<string, never>>) => string | RawHtml): Island<{
name: string;
}, Record<string, never>>
render (({ input: {
name: string;
}
input }) => `<p>Hello, ${input: {
name: string;
}
input .name: string name }!</p>`);
const Greeting: Island<{
name: string;
}, Record<string, never>>
Greeting .Island<{ name: string; }, Record<string, never>>.toString(props?: Partial<{
name: string;
}> | undefined): string
toString ({ name?: string | undefined name : "ilha" }); // → <p>Hello, ilha!</p>With a schema — inference plus runtime validation and coercion:
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 from "ilha";
import { import z z } from "zod";
const const Greeting: Island<{
name: string;
} & Record<string, unknown>, Record<string, never>>
Greeting = 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>>.input<z.ZodObject<{
name: z.ZodDefault<z.ZodString>;
}, z.core.$strip>>(schema: z.ZodObject<{
name: z.ZodDefault<z.ZodString>;
}, z.core.$strip>): IlhaBuilder<{
name: string;
} & Record<string, unknown>, Record<string, never>, Record<string, never>> (+1 overload)
input (import z z .function object<{
name: z.ZodDefault<z.ZodString>;
}>(shape?: {
name: z.ZodDefault<z.ZodString>;
} | undefined, params?: string | {
error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
name: z.ZodDefault<z.ZodString>;
}, z.core.$strip>
object ({ name: z.ZodDefault<z.ZodString> name : import z z .function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload) string ().ZodType<any, any, $ZodStringInternals<string>>.default(def: string): z.ZodDefault<z.ZodString> (+1 overload) default ("World") }))
.IlhaBuilder<{ name: string; } & Record<string, unknown>, Record<string, never>, Record<string, never>>.render(fn: (ctx: RenderContext<{
name: string;
} & Record<string, unknown>, Record<string, never>, Record<string, never>>) => string | RawHtml): Island<{
name: string;
} & Record<string, unknown>, Record<string, never>>
render (({ input: {
name: string;
} & Record<string, unknown>
input }) => `<p>Hello, ${input: {
name: string;
} & Record<string, unknown>
input .name: string name }!</p>`);
const Greeting: Island<{
name: string;
} & Record<string, unknown>, Record<string, never>>
Greeting .Island<{ name: string; } & Record<string, unknown>, Record<string, never>>.toString(props?: Partial<{
name: string;
} & Record<string, unknown>> | undefined): string
toString ({ name?: string | undefined name : "ilha" }); // → <p>Hello, ilha!</p>
const Greeting: Island<{
name: string;
} & Record<string, unknown>, Record<string, never>>
Greeting .Island<{ name: string; } & Record<string, unknown>, Record<string, never>>.toString(props?: Partial<{
name: string;
} & Record<string, unknown>> | undefined): string
toString (); // → <p>Hello, World!</p>#Why use .input()
Without .input(), any props passed to an island are untyped and unvalidated. Adding a type or schema gives you:
- Full TypeScript inference for
inputinside.state(),.render(),.on(),.effect(), and every other builder method. - Runtime validation and coercion on every call, including during SSR and hydration (schema form only).
- Default values handled by the schema itself, so the island works without props (schema form only).
#Choosing a form
.input<T>() | .input(schema) | |
|---|---|---|
| TypeScript inference | ✓ | ✓ |
| Runtime validation | — | ✓ |
| Default values | — | ✓ (via schema) |
| Extra dependency | — | ✓ (Zod, Valibot, etc.) |
Use .input<T>() for simple islands where you control the call sites and don't need validation. Use .input(schema) when you need defaults, coercion, or validation — especially for islands that are hydrated from serialized server props.
#Using defaults
Defaults are defined in the schema, not in ilha. Any Standard Schema validator that supports defaults will apply them automatically when a prop is omitted.
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 from "ilha";
import { import z z } from "zod";
const const Card: Island<{
title: string;
accent: string;
} & Record<string, unknown>, Record<string, never>>
Card = 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>>.input<z.ZodObject<{
title: z.ZodString;
accent: z.ZodDefault<z.ZodString>;
}, z.core.$strip>>(schema: z.ZodObject<{
title: z.ZodString;
accent: z.ZodDefault<z.ZodString>;
}, z.core.$strip>): IlhaBuilder<{
title: string;
accent: string;
} & Record<string, unknown>, Record<string, never>, Record<string, never>> (+1 overload)
input (
import z z .function object<{
title: z.ZodString;
accent: z.ZodDefault<z.ZodString>;
}>(shape?: {
title: z.ZodString;
accent: z.ZodDefault<z.ZodString>;
} | undefined, params?: string | {
error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
title: z.ZodString;
accent: z.ZodDefault<z.ZodString>;
}, z.core.$strip>
object ({
title: z.ZodString title : import z z .function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload) string (),
accent: z.ZodDefault<z.ZodString> accent : import z z .function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload) string ().ZodType<any, any, $ZodStringInternals<string>>.default(def: string): z.ZodDefault<z.ZodString> (+1 overload) default ("teal"),
}),
)
.IlhaBuilder<{ title: string; accent: string; } & Record<string, unknown>, Record<string, never>, Record<string, never>>.render(fn: (ctx: RenderContext<{
title: string;
accent: string;
} & Record<string, unknown>, Record<string, never>, Record<string, never>>) => string | RawHtml): Island<{
title: string;
accent: string;
} & Record<string, unknown>, Record<string, never>>
render (({ input: {
title: string;
accent: string;
} & Record<string, unknown>
input }) => `<div style="color:${input: {
title: string;
accent: string;
} & Record<string, unknown>
input .accent: string accent }">${input: {
title: string;
accent: string;
} & Record<string, unknown>
input .title: string title }</div>`);#State initialized from input
Once you have typed input, you can use it to initialize state:
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 from "ilha";
import { import z z } from "zod";
const const Counter: Island<{
start: number;
} & 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>>.input<z.ZodObject<{
start: z.ZodDefault<z.ZodNumber>;
}, z.core.$strip>>(schema: z.ZodObject<{
start: z.ZodDefault<z.ZodNumber>;
}, z.core.$strip>): IlhaBuilder<{
start: number;
} & Record<string, unknown>, Record<string, never>, Record<string, never>> (+1 overload)
input (import z z .function object<{
start: z.ZodDefault<z.ZodNumber>;
}>(shape?: {
start: z.ZodDefault<z.ZodNumber>;
} | undefined, params?: string | {
error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
start: z.ZodDefault<z.ZodNumber>;
}, z.core.$strip>
object ({ start: z.ZodDefault<z.ZodNumber> start : import z z .function number(params?: string | z.core.$ZodNumberParams): z.ZodNumber number ().ZodType<any, any, $ZodNumberInternals<number>>.default(def: number): z.ZodDefault<z.ZodNumber> (+1 overload) default (0) }))
.IlhaBuilder<{ start: number; } & Record<string, unknown>, Record<string, never>, Record<string, never>>.state<number, "count">(key: "count", init?: StateInit<{
start: number;
} & Record<string, unknown>, number> | undefined): IlhaBuilder<{
start: number;
} & Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>
state ("count", ({ start: number start }) => start: number start )
.IlhaBuilder<{ start: number; } & Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>.render(fn: (ctx: RenderContext<{
start: number;
} & Record<string, unknown>, MergeState<Record<string, never>, "count", number>, Record<string, never>>) => string | RawHtml): Island<{
start: number;
} & Record<string, unknown>, MergeState<Record<string, never>, "count", number>>
render (({ state: IslandState<MergeState<Record<string, never>, "count", number>> state }) => `<p>${state: IslandState<MergeState<Record<string, never>, "count", number>> state .count: MarkedSignalAccessor
() => number (+1 overload)
count ()}</p>`);The initializer function receives the resolved input object, so state stays in sync with whatever props were passed in. This works identically with both forms.
#Async schemas
Async schemas are not supported. If your validator's validate() method returns a Promise, ilha will throw at runtime. Keep schemas synchronous.
#Notes
.input()must be called before any other builder method if you want the input type to flow through the chain.- Calling
.input()resets the builder — any previously chained.state()or other methods are not carried over. - If
.input()is omitted entirely, props are accepted asRecord<string, unknown>with no validation.