| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
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.
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) |
Have a question or built something with Ilha? Join Discord, follow Ilha on X, or open an issue.
| Back | FazBrowse Home | New Git URL |