Targ Apps Docs
Chroma

API Reference

Every function exported by Chroma, with signatures and examples.

Everything is imported from the package generated by ./build.sh:

import {
    e, Fragment, render,
    hookState,
    useEffect, useLayoutEffect,
    useMemo, useCallback,
    useRef, useReducer,
    createContext, useContext,
    useDebugValue,
    Show, For,
    cva, cx,
} from "./pkg/chroma.js";

Core

e(tag, props, ...children)

Creates a node.

  • tag string | Function — an HTML tag name, or a component function.
  • props object | null — attributes, events, and special props.
  • children — any number of children (see child semantics below).
  • Returns: a DOM Element (string tag) or whatever the component returns.
  • Throws chroma: e() expects a string tag or a component function if tag is neither.

When tag is a string, an HTML element is created and each prop is applied:

Prop shapeBehavior
onClick, onInput, … (on* + function)addEventListener("click" / "input" / …)
ref (object)Sets ref.current to the created element
ref (function)Called with the created element
function (non-event, non-ref)Reactive attribute — re-evaluated when its signals change
false / null / undefinedAttribute omitted (or removed on update)
anything elseStatic attribute, set once

Prop-name aliases: classNameclass, htmlForfor.

When tag is a function (component), it is invoked exactly once with props; children are merged into props.children (a single child stays unwrapped, multiple become an array).

Child semantics:

ChildBehavior
DOM NodeAppended as-is
functionReactive child — mounted in a display: contents slot, re-evaluated when its signals change; may return text or DOM nodes
arrayFlattened recursively
null / undefined / booleanRenders nothing
string / number / otherText node (objects go through JSON.stringify)

Fragment

Groups children without a wrapper element:

e(Fragment, null, e("h1", null, "Title"), e("p", null, "Body"))

Returns a DocumentFragment.

render(component, container)

Runs component once and appends the resulting tree to container (a DOM Element). After mounting, all queued layout effects run synchronously — before the browser paints.

render(App, document.getElementById("app"));
  • Errors with chroma: render() requires an Element as the container (did the selector return null?) if container is not an Element.
  • Errors with chroma: the root component did not return a DOM node if the component's return value isn't a DOM node.

State

hookState(initial)

The primary state hook. Returns [getter, setter].

const [count, setCount] = hookState(0);

count();            // read current value (subscribes if inside a reactive expression)
setCount(5);         // write; notifies only if the value changed (Object.is)
setCount(c => c+1);  // updater form: receives the current value
  • The getter, called inside a reactive expression (reactive child, reactive attribute, effect, memo), registers a dependency on the signal.
  • The setter re-runs only the bindings that depend on that signal. If the new value is Object.is-equal to the old one, nothing is notified.

useReducer(reducer, initial)

Reducer-driven state. Returns [getter, dispatch].

const [state, dispatch] = useReducer((s, action) => {
    switch (action.type) {
        case "inc": return { n: s.n + 1 };
        default:    return s;
    }
}, { n: 0 });

dispatch({ type: "inc" });

dispatch(action) computes reducer(current, action) and writes the result to the underlying signal (same change-detection rules as hookState). If reducer throws, the error is logged to the console and the state is left unchanged.

Effects

Dependency arrays carry getters, not values: [count], not [count()]. See the Hooks Guide for the full semantics.

useEffect(fn, deps?)

Reactive side effect.

  • First run is deferred to a microtask (after the component mounts).
  • Re-runs are synchronous whenever a dependency changes.
  • If fn returns a function, it is used as cleanup, invoked before each re-run.
useEffect(() => {
    document.title = `count: ${count()}`;
    return () => console.log("cleanup");
}, [count]);

useLayoutEffect(fn, deps?)

Identical to useEffect, except the first run is synchronous: it fires as soon as render finishes mounting the tree, before paint. The DOM (and refs) are already available.

Derived values

useMemo(calc, deps?)

Cached derived value. Returns a reactive getter; the computation re-runs only when its dependencies change, not on every read.

const doubled = useMemo(() => count() * 2, [count]);
e("p", null, doubled); // reactive child bound to the memo

useCallback(fn, deps?)

Identity function. Without re-renders, components run once and functions are never recreated — this hook exists purely for API compatibility with React-style code.

Refs

useRef(initial)

Non-reactive mutable reference: { current: initial }. Pass it as the ref prop of an element to receive the DOM node:

const input = useRef(null);
e("input", { ref: input });
useLayoutEffect(() => input.current.focus(), []);

Context

createContext(defaultValue)

Creates a context object { Provider, defaultValue }.

Because there is no VDOM, children are normally evaluated before their parent. The Provider therefore receives its children as a function, so they can be evaluated with the context active:

const Theme = createContext("light");

e(Theme.Provider, { value: "dark" }, () => e(Child))

useContext(ctx)

Returns the value of the nearest active Provider, or defaultValue if there is none.

function Child() {
    const theme = useContext(Theme);
    return e("p", null, `theme: ${theme}`);
}
  • Throws chroma: useContext expects a context created with createContext if ctx wasn't produced by createContext.

Debugging

useDebugValue(value, formatter?)

Debug label for custom hooks. Logs to the console (via console.debug) only if globalThis.__CHROMA_DEBUG__ is truthy. If formatter is provided, it is applied to value before logging.

globalThis.__CHROMA_DEBUG__ = true;
useDebugValue("useCounter");

Control flow components

Show({ when, fallback?, children })

For({ each, children })

See Control Flow for Show and For in depth, including prop requirements and error messages.

Reusable components & variants

cva(config)

cx(...parts)

See Components & Variants for cva and cx in depth — the tools for reusing one e()-built component function across multiple looks (a Button with primary/ghost variants, a Badge with sizes) without hand-rolling class-string concatenation.

On this page