Introduction
Chroma is a VDOM-free web framework whose entire reactivity and DOM engine is written in Rust and compiled to WebAssembly.
What is Chroma
Chroma is a web framework for JavaScript/TypeScript developers whose entire engine — reactivity and DOM manipulation — is implemented in Rust and compiled to WebAssembly. UI is written with direct calls to e(tag, props, ...children), no JSX, no compiler, no build step.
import { hookState, render, e } from "./pkg/chroma.js";
function Counter() {
const [count, setCount] = hookState(0);
return e(
"button",
{ onClick: () => setCount(count() + 1) },
"Count: ",
count,
);
}
render(Counter, document.getElementById("app"));Clicking the button updates only the text node bound to count — no re-render, no diffing, no VDOM.
Core principles
- No VDOM, no diffing, no hydration. Each component runs exactly once. Reactive expressions stay bound to the exact DOM node they must update — fine-grained reactivity, SolidJS style.
- The whole engine in Rust. Signals, dependency tracking,
e,Fragment, and every DOM operation (viaweb-sys) live inside the.wasmbinary. There is no shim or custom JS runtime: the only JavaScript in the package is the loader auto-generated bywasm-bindgen(pkg/chroma.js). - No build step, no transforms. No Babel, no mandatory bundlers, no template compilers. A
<script type="module">is all you need.
The one rule you must know
A child or an attribute is reactive only if you pass it as a function.
e("p", null, count) // ✅ reactive: updates when count changes
e("p", null, count()) // ❌ static: evaluated once, never updates
e("p", null, () => count() * 2) // ✅ reactive derived expressionThere is no re-render that would re-evaluate JSX or expressions. The getter itself is the subscription — that's the entire reactivity model in one sentence. See Reactivity Model for the full mechanics.
What's implemented (v0.1)
- Full hook set:
hookState,useEffect,useLayoutEffect,useMemo,useCallback,useRef,useReducer,createContext/useContext,useDebugValue. - Control-flow components:
Show,For. - Reusable components with variants:
cva,cx. - Reactive text and attribute bindings.
render,e,Fragment.
Out of scope for now: SSR, keyed list reconciliation, DevTools. See Limitations.
Tooling
The engine alone is enough to ship an app (pkg/chroma.js + a static file server), but three sibling projects turn it into a full toolchain:
- CLI (
chroma.sh/chroma.ps1) —chroma install|new|dev|build. Provisions the toolchain, scaffolds astaticordynamicapp template, serves it in development, and bundles it for release. - Chroma Server — the single-purpose static HTTP server the CLI installs and uses for
chroma dev. Loopback-only, one thread per connection, zero dependencies beyond the standard library. - Chroma Desktop — a Tauri shell that imports
chroma buildzip bundles (drag-and-drop or file picker) and runs them locally: each import is extracted into its own folder and served from a local-only port, with previously imported apps listed on relaunch. - Chroma-Fs — a TypeScript storage plugin with IndexedDB and Tauri filesystem backends behind one file API.
Where to go next
Getting Started
Install the toolchain, build the engine, ship your first component.
Architecture
How the Rust modules are organized and how the WASM boundary works.
Reactivity Model
Signals, bindings, and the tracking cycle.
Hooks Guide
Every hook in depth, with dependency-array semantics.
Control Flow
Show and For — Chroma's reactive conditional and list components.
Components & Variants
cva and cx, for reusable components with style variants.
API Reference
Every exported function, signature by signature.
Examples
A full hooks demo and a larger real-world app.
Limitations
What v0.1 deliberately leaves out, and why.
CLI
chroma install|new|dev|build, chroma.json, templates.
Desktop
Import and run chroma build bundles outside the browser.
Server
The static HTTP server behind chroma dev.
Chroma-Fs
Storage plugin — IndexedDB and filesystem backends.