Explicit by designNo VDOMNo compilerNo dependencies~5KB gzipped

Build fast UIs with zero magic.

Vani is a Web‑Standards‑first UI runtime with explicit updates, anchor‑owned subtrees, and a clear and predictable rendering model across SPA and static sites.

Simple by design

Web‑standard HTML props

Use real attributes, styles, and events—no special syntax.

JS-first, zero compiler

Just TypeScript functions and DOM APIs, plus an optional JSX adapter.

SPA, SSR, and SSG

Same runtime, multiple delivery modes with explicit control.

Features

Everything you need, nothing you don’t.

A minimal runtime with powerful primitives.

⚡️

Explicit Updates

Only re-render when you call handle.update(). Predictable without magic.

🔦

Signals (optional)

Opt-in fine-grained updates with signal(), text(), and attr().

🧱

Subtree Ownership

Each component owns a DOM range. Updates never touch parents or siblings.

🧪

Runtime‑First

JS-first and transpiler-free, with an optional JSX adapter.

🌊

SSR + Hydration

Anchor‑based hydration with deterministic behavior and no heuristics.

🏝️

Client‑Only Islands

Mark subtrees as clientOnly for interactive islands.

🧠

Async Components

Promise‑based components with fallbacks, still explicit.

🧩

Incremental Adoption

Mount Vani widgets inside existing apps without much effort.

📦

ESM‑First Portability

Ships small ESM modules for browsers, Node, Deno, or Bun.

Principles

Explicit by design.

Vani keeps rendering local, deterministic, and easy to reason about.

SPA, SSR, SSG

Use renderToDOM for SPA, renderToString for SSR/SSG, hydrateToDOM for activation.

No Hidden Work

No diffing and no implicit reactivity by default. Signals are opt-in.

Leaf‑Only Updates

Rendering cost scales with subtree size, not app size.

DOM Is Yours

Access the rendered DOM subtree directly—no hidden layer between you and the DOM.

Simple Debugging

Stack traces and behavior are readable and obvious.

API

Tiny API surface, huge control.

Install

Choose your package manager:

Install skills

Supercharge your AI Agents with Vani skills:

Explicit updates

State changes only re-render when you call handle.update().

Count: 0

Signals (optional)

Fine-grained updates with signal(), text(), and attr().

Count: 0

JSX mode counter

Use JSX syntax with the Vani JSX runtime.

Count: 0

JSX inside JS-first

Render JSX components inside element-helper trees.

Mixed render:

JSX componentinside a non-JSX component.

Conditional rendering

Use normal control flow to show or hide UI based on state.

Forms with explicit submit

Input changes do not re-render until the user submits.

Keyed lists

Keep item identity stable and update explicitly.

Global state

Subscribe to shared state and update explicitly.

Global count: 0

Controlled inputs

Update on every keystroke while preserving focus.

Live: ...

SVG components

Import SVGs with ?vani and render them as components.

Import any SVG with ?vani and use it as a component.

Client-only islands

Mark islands with clientOnly: true (verify by disabling JS and reloading).

Loading...

Transitions

Defer non-urgent UI work to keep interactions snappy.

  • apples
  • oranges
  • pears
  • grapes

Async components

Return a Promise of a render function with a fallback.

Async content loaded.

DOM refs

Access DOM nodes and control focus explicitly.

Value: ...

Live Playground

Default export must be a component.

Code
Preview