Build reactive components once. Render them on the server, mount them in the browser, and ship JavaScript only where it earns its place.
Website · Documentation · Quick start · Templates · LLM docs · Discord
Beta: Ilha is ready to try, but its API may still evolve before 1.0.
Most UI frameworks hydrate an application. Ilha hydrates only the components that need to be interactive.
- Tiny by architecture — static HTML stays static. Your users download code for active regions, not an entire page.
- No virtual DOM — atoms drive local updates and Ilha morphs the existing DOM in place.
- No compiler required — standard JSX/TSX, or
h()when you cannot use JSX. - One component, every environment — the same function renders on the server, mounts in the browser, and hydrates in place.
- A consistent SSR story —
renderToString()waits until idle, then serializes HTML and an optional atom snapshot. - Progressive by default — add one component to server-rendered HTML or compose a complete SPA. You choose the boundary.
- Small API, strong TypeScript — function components,
atom(),watch(),when, and JSX. - Backend and runtime agnostic — web platform primitives and ESM, so it fits existing servers, edge runtimes, static sites, and browsers.
Because Ilha sends only the interactive parts of a page, real applications can ship around 5× less client JavaScript than whole-page hydration. The exact result depends on your component boundaries and dependencies—measure your production bundle, not the slogan.
| Ilha | Typical full-app hydration | |
|---|---|---|
| Client boundary | Each interactive component | The application root |
| Update model | Atoms + direct DOM morphing | Virtual DOM reconciliation |
| Server and client component | The same function | Often separate execution constraints |
| SSR API | renderToString() |
Framework-specific renderer |
| Hydration | Explicit, local, snapshot-aware | Usually application-wide |
| Compiler | Optional | Often required for best results |
| Adoption | One component or a complete app | Usually controls the application |
Ilha is not trying to be a batteries-included platform. It is the small rendering and reactivity layer you can bring to the stack you already have.
npm install ilha effecteffect is a peer dependency.
Configure JSX once:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "ilha"
}
}Write a component:
import { atom } from "ilha";
export const Counter = ({ start = 0 }: { start?: number }) => {
const count = atom(start);
return (
<button
type="button"
onclick={() => count.update((value: number) => value + 1)}
>
Count: {count}
</button>
);
};import { mount } from "ilha";
import { Counter } from "./counter";
mount(document.getElementById("app")!, () => Counter({ start: 0 }));import { renderToString } from "ilha";
import { Counter } from "./counter";
const html = await renderToString(() => Counter({ start: 10 }));import { mount, renderToString } from "ilha";
import { Counter } from "./counter";
const html = await renderToString(() => Counter({ start: 10 }));
const host = document.querySelector("[data-ilha]");
if (host) mount(host, () => Counter({ start: 10 }), { hydrate: true });No duplicate template. No separate client component. No hydration flicker.
import { atom, batch, watch, when, mount, renderToString, h } from "ilha";| Export | Use it for |
|---|---|
atom() |
Component-local reactive slots |
batch() |
Coalesce multiple atom writes |
watch() |
Side effects on atom or stream changes |
when() |
Per-emission generator body (interrupts stale work) |
mount() |
Activate a component in the DOM |
renderToString() |
Serialize a component to HTML |
h / Fragment |
JSX factory |
Ordinary event handlers stay ordinary functions (onclick={handler}). Lists are Streams of data, not atoms of JSX.
Emit a data-ilha host, load your client module, and call mount(). Ilha does not require control over your backend or document.
@ilha/router adds file-system routing for Vite and Rsbuild, nested layouts, and Oxide server islands.
@ilha/astro turns Ilha components into first-class Astro islands with client:load, client:idle, client:visible, client:media, and client:only.
| Template | Create | Try it |
|---|---|---|
| Vite SPA | npx giget@latest gh:ilhajs/ilha/templates/vite-spa |
StackBlitz |
| Oxide SPA | npx giget@latest gh:ilhajs/ilha/templates/oxide-spa |
StackBlitz |
| Package | What it does |
|---|---|
ilha |
Components, atoms, JSX runtime, SSR, hydration, and DOM morphing |
@ilha/router |
Isomorphic SPA routing, file-system routes, and Oxide server islands |
@ilha/astro |
Astro renderer (renderToString + mount) |
- Read the documentation.
- Give an AI coding tool the compact
llms.txt. - Use
llms-full.txtwhen it needs the complete documentation in one file.
Have a question or built something with Ilha? Join Discord, follow Ilha on X, or open an issue.