Skip to content

.input()

Declare typed external props with optional Standard Schema runtime validation and coercion.

Updated View as Markdown

Declare the island’s external props and their types. Use a type-only generic for TypeScript inference or a Standard Schema-compatible validator for runtime validation and coercion.

Use ilha<Props>(({ input }) => JSX) when typed props are the only capability you need. Expand to .input<Props>().render(...) when you add defaults, runtime validation, or other builder capabilities.

Basic usage

Type-only — TypeScript inference, no runtime validation:

Default props object — inference plus runtime defaults (shallow merge, no validator):

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<{
    name: string;
}, Record<never, never>>
Greeting
=
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>.input<{
    name: string;
}>(defaults: {
    name: string;
}): IlhaBuilder<{
    name: string;
}, Record<never, never>, Record<never, never>, Record<never, never>> (+2 overloads)
input
({ name: stringname: "World" })
.
IlhaBuilder<{ name: string; }, Record<never, never>, Record<never, never>, Record<never, never>>.render(fn: (ctx: RenderContext<{
    name: string;
}, Record<never, never>, Record<never, never>, Record<never, never>>) => string | RawHtml): Island<{
    name: string;
}, Record<never, never>>
render
(({
input: {
    name: string;
}
input
}) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>Hello, {
input: {
    name: string;
}
input
.name: stringname}!</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);
const Greeting: Island<{
    name: string;
}, Record<never, never>>
Greeting
.
Island<{ name: string; }, Record<never, never>>.toString(props?: Partial<{
    name: string;
}> | undefined): string
toString
(); // → <p>Hello, World!</p>
const Greeting: Island<{
    name: string;
}, Record<never, never>>
Greeting
.
Island<{ name: string; }, Record<never, never>>.toString(props?: Partial<{
    name: string;
}> | undefined): string
toString
({ name?: string | undefinedname: "ilha" }); // → <p>Hello, ilha!</p>
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 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>.input<{
    name: string;
}>(defaults: {
    name: string;
}): IlhaBuilder<{
    name: string;
}, Record<never, never>, Record<never, never>, Record<never, never>> (+2 overloads)
input
<{ name: stringname: string }>({ name: stringname: "World" })
.
IlhaBuilder<{ name: string; }, Record<never, never>, Record<never, never>, Record<never, never>>.render(fn: (ctx: RenderContext<{
    name: string;
}, Record<never, never>, Record<never, never>, Record<never, never>>) => string | RawHtml): Island<{
    name: string;
}, Record<never, never>>
render
(({
input: {
    name: string;
}
input
}) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>{
input: {
    name: string;
}
input
.name: stringname}</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);

With a schema — inference plus runtime validation and coercion:

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";
import { import zz } from "zod"; const
const Greeting: Island<{
    name: string;
} & Record<string, unknown>, Record<never, never>>
Greeting
=
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>.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<never, never>, Record<never, never>, Record<never, never>> (+2 overloads)
input
(import zz.
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 zz.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<never, never>, Record<never, never>, Record<never, never>>.render(fn: (ctx: RenderContext<{
    name: string;
} & Record<string, unknown>, Record<never, never>, Record<never, never>, Record<never, never>>) => string | RawHtml): Island<{
    name: string;
} & Record<string, unknown>, Record<never, never>>
render
(({
input: {
    name: string;
} & Record<string, unknown>
input
}) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>Hello, {
input: {
    name: string;
} & Record<string, unknown>
input
.name: stringname}!</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);
const Greeting: Island<{
    name: string;
} & Record<string, unknown>, Record<never, never>>
Greeting
.
Island<{ name: string; } & Record<string, unknown>, Record<never, never>>.toString(props?: Partial<{
    name: string;
} & Record<string, unknown>> | undefined): string
toString
({ name?: string | undefinedname: "ilha" }); // → <p>Hello, ilha!</p>
const Greeting: Island<{
    name: string;
} & Record<string, unknown>, Record<never, never>>
Greeting
.
Island<{ name: string; } & Record<string, unknown>, Record<never, never>>.toString(props?: Partial<{
    name: string;
} & Record<string, unknown>> | undefined): string
toString
(); // → <p>Hello, World!</p>

Why use .input()

Without .input<T>() or the ilha<T>() shorthand, props are untyped and unvalidated. Adding a type or schema gives you:

  • Full TypeScript inference for input inside .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&lt;T&gt;() .input({ … }) / .input&lt;T&gt;({ … }) .input(schema)
TypeScript inference
Runtime validation
Default values ✓ (shallow merge) ✓ (via schema)
Extra dependency ✓ (Zod, Valibot, etc.)

Use .input&lt;T&gt;() when callers always pass props. Use .input({ … }) for lightweight defaults without a schema (same idea as store({ … })). Use .input(schema) when you need coercion or validation — especially for islands hydrated from serialized server props.

Using defaults

POJO form — pass a defaults object to .input({ … }). Ilha shallow-merges { ...defaults, ...props } on each call.

Schema form — defaults live in the schema (Zod .default(), etc.). Validation runs on the merged props object.

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";
import { import zz } from "zod"; const
const Card: Island<{
    title: string;
    accent: string;
} & Record<string, unknown>, Record<never, never>>
Card
=
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>.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<never, never>, Record<never, never>, Record<never, never>> (+2 overloads)
input
(
import zz.
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.ZodStringtitle: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string(), accent: z.ZodDefault<z.ZodString>accent: import zz.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<never, never>, Record<never, never>, Record<never, never>>.render(fn: (ctx: RenderContext<{
    title: string;
    accent: string;
} & Record<string, unknown>, Record<never, never>, Record<never, never>, Record<never, never>>) => string | RawHtml): Island<{
    title: string;
    accent: string;
} & Record<string, unknown>, Record<never, never>>
render
(({
input: {
    title: string;
    accent: string;
} & Record<string, unknown>
input
}) => (
<"div": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLDivElement>>div style?: string | RawHtml | StyleProps | undefinedstyle={`color:${
input: {
    title: string;
    accent: string;
} & Record<string, unknown>
input
.accent: stringaccent}`}>{
input: {
    title: string;
    accent: string;
} & Record<string, unknown>
input
.title: stringtitle}</"div": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLDivElement>>div>
));

State initialized from input

Once you have typed input, you can use it to initialize state:

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";
import { import zz } from "zod"; const
const Counter: Island<{
    start: number;
} & Record<string, unknown>, MergeState<Record<never, never>, "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>.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<never, never>, Record<never, never>, Record<never, never>> (+2 overloads)
input
(import zz.
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 zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber().ZodType<any, any, $ZodNumberInternals<number>>.default(def: number): z.ZodDefault<z.ZodNumber> (+1 overload)default(0) }))
.
IlhaBuilder<{ start: number; } & Record<string, unknown>, Record<never, never>, Record<never, never>, Record<never, never>>.state<number, "count">(key: "count", init?: StateInit<{
    start: number;
} & Record<string, unknown>, number> | undefined): IlhaBuilder<{
    start: number;
} & Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>, Record<never, never>>
state
("count", ({ start: numberstart }) => start: numberstart)
.
IlhaBuilder<{ start: number; } & Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>, Record<never, never>>.render(fn: (ctx: RenderContext<{
    start: number;
} & Record<string, unknown>, MergeState<Record<never, never>, "count", number>, Record<never, never>, Record<never, never>>) => string | RawHtml): Island<{
    start: number;
} & Record<string, unknown>, MergeState<Record<never, never>, "count", number>>
render
(({ state: IslandState<MergeState<Record<never, never>, "count", number>>state }) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>{state: IslandState<MergeState<Record<never, never>, "count", number>>state.
count: MarkedSignalAccessor
() => number (+1 overload)
count
()}</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>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 as Record<string, unknown> with no validation.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close