Targ Apps Docs
Chroma

Limitations & Roadmap

What Chroma v0.1 deliberately leaves out, and why.

Current version

Chroma is at v0.1.0. Implemented: the 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/attribute bindings, render, e, Fragment. See API Reference for the full surface.

Out of scope for v0.1: SSR/hydration, keyed list reconciliation, and DevTools.

Out of scope for now

  • SSR / hydration. By design there is nothing to hydrate — components run once, directly against the real DOM, so there's no server-rendered markup to reconcile against.
  • Keyed list reconciliation. For rebuilds its child nodes wholesale on every change to the tracked array (see Control Flow); it does not diff by key. Every write to the source array recreates all list items.
  • DevTools. There is no browser extension or inspector. useDebugValue writes to the console when globalThis.__CHROMA_DEBUG__ is set, which is the only introspection available today.

Known trade-offs

  • The WASM boundary has a cost. Every DOM operation crosses WASM → JS. This is an explicit design decision — the goal is the architecture ("the whole engine lives in Rust"), not winning benchmarks against pure-JS frameworks. See Architecture.
  • Show's truthiness isn't exactly JS truthiness. null, undefined, false, 0, and "" are falsy, matching JS — but NaN is treated as truthy (the Rust check is n == 0.0, which is false for NaN), unlike real JS where Boolean(NaN) is false. Avoid relying on NaN as a falsy when value.
  • The npm name chroma is taken by an existing color library. Local usage is unaffected; publishing to npm would require a scope (e.g. @vmaspad/chroma) or a different package name.
  • No direct .wasm import. Browsers don't yet ship ESM integration for WebAssembly, and WASM only exchanges numbers with JS — strings, objects, and callbacks need the wasm-bindgen bridge (pkg/chroma.js), which is why that loader exists and is generated rather than hand-written.
  • No error boundaries. Errors thrown inside effects, reducers, and Provider children are caught and logged to the console rather than crashing the app, but there's no component-level mechanism to catch and render a fallback UI for them.

Why these trade-offs

Chroma's stated goal is to prove that a fine-grained reactive engine — signals, dependency tracking, and DOM manipulation — can live entirely in Rust/WASM with the JS surface reduced to a thin, auto-generated bridge. Features that would require re-introducing JS-side bookkeeping (keyed diffing, hydration, a DevTools protocol) are deliberately deferred rather than bolted on, so the core architecture stays legible.

Where to go next

On this page