0.1.0 - Alpha
ilha
0.1.0 — 2026-04-14
Initial release of ilha — a tiny, isomorphic island framework for building reactive UI components. Renders to HTML strings on the server and mounts as fine-grained reactive islands in the browser. No virtual DOM. No compiler. Powered by alien-signals.
- Adds fluent builder API:
.input(schema)— typed external props via any Standard Schema validator (Zod, Valibot, ArkType, or the built-intype()helper); async schemas are not supported - Adds fluent builder API:
.state(key, init?)— reactive signal; init can be a static value or a function receiving resolved input - Adds fluent builder API:
.derived(key, fn)— sync or async derived value with{ loading, value, error }envelope; re-runs reactively; supportsAbortSignalcancellation on re-run - Adds fluent builder API:
.on(selector@event[modifiers], handler)— delegated event listener withonce,capture,passivemodifiers; omit selector to target the host; handler receives{ state, derived, input, host, target, event } - Adds fluent builder API:
.effect(fn)— reactive effect that runs after mount and re-runs when tracked signals change; supports cleanup return - Adds fluent builder API:
.onMount(fn)— runs once after mount; receives{ state, derived, input, host, hydrated }; supports cleanup return; skipped whensnapshot.skipOnMountis set - Adds fluent builder API:
.bind(selector, stateKey | externalSignal)— two-way binds form elements (input, textarea, select, checkbox, radio, number) to a state key or a context signal - Adds fluent builder API:
.slot(name, island)— embeds a child island as a named slot; inline SSR output and independent client activation - Adds fluent builder API:
.transition({ enter, leave })— async enter/leave callbacks; leave is awaited before teardown - Adds fluent builder API:
.render(fn)— finalises the builder and returns anIsland - Adds island interface:
island(props?)/island.toString(props?)— synchronous HTML string render; async derived values render asloading: true - Adds island interface:
await island(props?)— async render that awaits all derived values - Adds island interface:
island.mount(host, props?)— mounts into a DOM element; readsdata-ilha-propsanddata-ilha-stateautomatically; returns anunmount()function - Adds island interface:
island.hydratable(props, options)— async SSR helper wrapping output in adata-ilhahydration container with optional state snapshot - Adds helpers and utilities:
mount(registry, options?)— auto-discoversdata-ilhaelements and mounts matching islands; supportsrootscoping andlazyIntersectionObserver activation - Adds helpers and utilities:
from(selector | Element, island, props?)— mounts a single island into the first matching element; returnsunmountornull - Adds helpers and utilities:
context(key, initial)— global named reactive signal shared across all islands; same key always returns the same signal instance; SSR-safe - Adds helpers and utilities:
html`...`— XSS-safe template tag; HTML-escapes by default; supportsraw(), signal accessors, arrays, and nestedhtmlresults - Adds helpers and utilities:
raw(value)— marks a string as trusted raw HTML, bypassing escaping insidehtml - Adds helpers and utilities:
type(coerce?)— lightweight Standard Schema validator for.input()without a full validation library
@ilha/form
0.1.0 — 2026-04-14
Initial release of @ilha/form — a tiny, typed form binding library for ilha islands. Binds a Standard Schema validator to a native <form> element; wires up typed submission, per-field error tracking, and dirty state using native DOM events only. No virtual DOM. No external runtime dependencies. Async schema validation is not supported.
- Adds
createForm(options)— creates a form binding instance; does not attach any listeners until.mount()is called; acceptsel,schema,onSubmit,onError, andvalidateOn - Adds
createFormoptiononSubmit(values, event)— called with fully typed schema output on valid submission; not called if validation fails - Adds
createFormoptiononError(issues, event)— called with structuredStandardSchemaV1.Issue[]on invalid submission - Adds
createFormoptionvalidateOn— controls when live validation runs against field changes:"submit"(default, submission only),"change"(after a field loses focus with a new value), or"input"(every keystroke) - Adds
form.mount()— attaches event listeners to the form; returns a cleanup function equivalent to callingform.unmount() - Adds
form.unmount()— removes all event listeners; idempotent, safe to call multiple times - Adds
form.values— reads and validates current form values synchronously against the schema; returns a discriminated union{ ok: true, data }or{ ok: false, issues }; never throws; can be called before mount - Adds
form.errors— returns the per-field error map from the last validation run asRecord<string, string[]>; keys are dot-separated field paths (e.g.user.email); empty object before first validation; returns a copy — mutations do not affect internal state - Adds
form.isDirty—trueif any field value has changed since mount was called (detected viachangeevents); staystrueafter unmount - Adds
form.submit()— programmatically triggers the validate /onSubmit/onErrorcycle; dispatches a realSubmitEventwhen mounted, runs validation directly when not mounted - Adds exported helper
issuesToErrors(issues)— converts aStandardSchemaV1.Issue[]array into aFormErrorsmap; useful for wiringonErrorinto island state - Adds exported types:
FormResult<T>,FormErrors,ValidateOn,Form<S>,CreateFormOptions<S>
@ilha/router
0.1.1 — 2026-04-14
Initial release of @ilha/router — a lightweight, isomorphic router for ilha islands. Runs in the browser with full signal reactivity and on the server as a synchronous HTML string renderer. Pairs natively with Nitro and includes a Vite plugin for file-system based routing. Powered by rou3, the same matching engine used by Nitro.
- Adds
router()— creates a newRouterBuilderinstance with a fresh route registry; always call fresh, never share instances across server requests - Adds
RouterBuilder.route(pattern, island)— registers a route; patterns are matched in declaration order, first match wins; supports static segments (about), named params (:id), and wildcards (...slug); static segments take priority over params - Adds
RouterBuilder.render(url)— resolves the URL against the registry and returns a synchronous HTML string; populates all route signals; decodes percent-encoded params automatically; renders<div data-router-empty>when no route matches - Adds
RouterBuilder.renderHydratable(url, registry, options?)— async SSR variant that wraps the active island indata-ilhahydration markers; falls back to plain SSR with a warning if the island is not found in the registry; supportssnapshotoption (defaulttrue) - Adds
RouterBuilder.mount(target, options?)— mounts the router into a DOM element or CSS selector; sets uppopstatelistening and intercepts internal<a>clicks automatically;hydrate: truepreserves existing SSR DOM on first mount; no-op with warning outside a browser environment - Adds
RouterBuilder.prime()— primes route context signals fromwindow.locationbeforeilha.mountruns; prevents signal mismatch that would destroy hydrated bindings; must be called after all routes are registered - Adds
RouterBuilder.hydrate(registry, options?)— convenience method combining.prime(),ilha.mount(), and.mount()into a single call; recommended client entry point; returns an unmount function - Adds
navigate(to, options?)— programmatically navigates to a path; updates URL, history stack, and all reactive signals; duplicate navigations are no-ops;replace: trueuseshistory.replaceState; no-op on the server - Adds
prime()— standalone export of the same signal-priming logic as.prime()on the builder; useful when managing the priming step separately - Adds
useRoute()— returns reactive signal accessors{ path, params, search, hash }for the current route state; safe inside any island render on both client and server - Adds
routePath,routeParams,routeSearch,routeHash— raw context signals for direct access outside of islands - Adds
isActive(pattern)— returnstrueif the current path matches the given registered pattern; uses O(1) reverse island lookup internally - Adds
enableLinkInterception(root?)— attaches a delegated click listener that intercepts internal<a>navigations; skips external links,target="_blank", anchor-only hashes, and modifier-key clicks; returns a cleanup function; called automatically by.mount(); no-op on the server - Adds
RouterView— outlet island that wraps the active route island in<div data-router-view>; renders<div data-router-empty>when no route matches; usable standalone for SSR (.toString()) and client (.mount()) - Adds
RouterLink— declarative link island that callsnavigate()on click; acceptshrefandlabelstate - Adds
wrapLayout(layout, page)— wraps a page island with a layout handler; used by the Vite plugin codegen and available for manual composition - Adds
wrapError(handler, page)— wraps a page island with an error boundary; catches errors during SSR.toString()and client.mount(); the nearest boundary wins; re-throwing escalates to the next outer boundary - Adds Vite plugin
pages(options?)from@ilha/router/vite— scanssrc/pages, resolves layout and error boundary chains, and generates a ready-to-use router; exposes virtual modulesilha:pages(pageRouter) andilha:registry(registry); regenerates only on structural changes to avoid unnecessary HMR invalidation; supportsdirandgeneratedoptions - Adds file-system route conventions:
index.ts→/,about.ts→/about,[id].ts→/:id,[...slug].ts→/:slug;layout.tswraps all pages in its directory and subdirectories;error.tscatches render errors in its directory and subdirectories;.test.ts,.spec.ts, and.d.tsfiles are excluded automatically - Adds exported types:
RouteRecord,RouteSnapshot,AppError,LayoutHandler,ErrorHandler,NavigateOptions,MountOptions,HydrateOptions,HydratableRenderOptions,RouterBuilder
@ilha/store
0.1.0 — 2026-04-14
Initial release of @ilha/store — a zustand-shaped reactive store for ilha islands. Backed by alien-signals, the same engine powering ilha core state, for shared global state that lives outside any single island. Use it when state needs to be shared across multiple islands, updated from outside an island (e.g. a WebSocket handler), or persisted globally.
- Adds
createStore(initialState, actions?)— creates a reactive store; optionally accepts an actions creator for encapsulating state mutations; the actions creator receivesset,get, andgetInitialState - Adds
store.getState()— returns the current state snapshot; returns a stable reference when state has not changed - Adds
store.getInitialState()— returns the frozen initial state as it was at construction time; unaffected by subsequentsetStatecalls; useful for implementing reset actions - Adds
store.setState(update)— merges a partial state update shallowly; accepts a plain object or an updater function receiving the latest state - Adds
store.subscribe(listener)— subscribes to all state changes; listener receives(nextState, prevState); does not fire on initial subscription; returns an unsubscribe function - Adds
store.subscribe(selector, listener)— slice subscription variant; fires only when the selected value changes (compared withObject.is); listener receives(newSlice, prevSlice); returns an unsubscribe function - Adds
store.bind(el, render)— reactively renders a store-driven HTML string into a DOM element whenever state changes; render function receives the full state and may return a plain string or anhtmltagged template result; renders immediately on bind; returns an unsubscribe function - Adds
store.bind(el, selector, render)— slice bind variant; re-renders only when the selected slice changes; render function receives only the selected slice - Adds
effectScope— re-exported fromalien-signals; groups multiplesubscribeandbindcalls so they can all be torn down with a singlestop()call - Adds exported types:
StoreApi<T>,SetState<T>,GetState<T>,Listener<T>,SliceListener<T, S>,RenderResult,Unsub