Skip to content
Ilha
Esc
navigateopen⌘Jpreview
On this page

Introduction

An introduction to ilha, a tiny isomorphic UI library with signal reactivity and no virtual DOM.

ilha is a tiny, isomorphic UI library for building reactive components.

You render on the server and mount in the browser with signal-based updates. There is no virtual DOM and no compiler. Markup stays close to HTML.

What ilha is

A component is a function, async function, or generator that returns a view. The same component produces HTML through renderToString() and activates in the browser through mount().

You write a function component. You declare atom() values inside it. Reads during render subscribe that component; writes rerun it and morph the host.

Why it exists

Most UI stacks force you to choose between simplicity and interactivity. ilha keeps both close together: a small API, direct DOM updates, and one component for server and client.

This makes ilha a good fit when you want:

  • Server-rendered markup.
  • Small interactive regions.
  • Explicit state.
  • No virtual DOM.

How it feels to use

A typical component reads like a small HTML-aware module:

The same function can render to a string on the server and mount into the DOM on the client.

Choose the smallest component form

Start with Use it when
const View = () => JSX Reusable markup inside a parent component
const View = function* You need yield Stream.map(...) or yield* when(...)
mount(el, View) The function is the root you hydrate or mount

Start plain:

const const Status: () => ViewStatus = () => <JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>Ready</JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>;

Add local state with atom():

import { const atom: AtomFnatom } from "ilha";

const const Status: () => ViewStatus = () => {
  const const message: AtomHandle<string>message = atom<string>(init: string | Atom<string> | Effect<string, unknown, AtomRegistry> | Stream<string, unknown, AtomRegistry>): AtomHandle<string>atom("Ready");
  return <JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>{const message: AtomHandle<string>message}</JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>;
};

A nested function without its own mount() / renderToString() belongs to the parent component.

Core ideas

Isomorphic rendering

You produce HTML on the server and activate the same function in the browser.

Fine-grained reactivity

Atoms keep updates local. Changing one value does not rerender an entire application tree.

Function components

You write functions that return JSX. Generators yield streams and yield* Effects. Async functions can await before they return a view.

JSX-first authoring

Prefer JSX. It escapes interpolations. Use h() when you cannot use JSX.

When to use ilha

Use ilha when you want server HTML plus a few interactive regions, not a full client app by default.

Basic mental model

  1. Write a component that returns JSX.
  2. Declare atom() values you read in the view.
  3. Call renderToString(component) on the server.
  4. Call mount(element, component, { hydrate: true }) in the browser.

Was this page helpful?