Targ Apps Docs
Chroma

Control flow

Show and For - Chroma's reactive conditional and list components, and exactly what they do.

Chroma has no compiler, so plain if and .map() evaluate eagerly, once, outside any reactive scope. Show and For give conditionals and lists the same function-based reactivity as everything else. Signatures, prop tables, and error messages are in the API reference.

Show

Conditional rendering. It mounts both branches once and toggles which one is visible when when changes:

// src/components/Gate.chr
import { e, Show } from "../../pkg/chroma.js";
import { Dashboard } from "./Dashboard.chr";

export function Gate(props) {
  return e(
    Show,
    { when: props.loggedIn, fallback: () => e("p", null, "Log in") },
    () => e(Dashboard),
  );
}
  • when is required and must be a getter. Otherwise Show throws chroma: Show requires props.when to be a getter (function).
  • fallback is an optional function returning what to show when when is falsy.
  • Children are a function returning the content for the truthy state. A non-function child is also accepted and mounted once.
  • Both the fallback and the children are evaluated once, at mount, untracked. Hooks inside them run once, and toggling only flips display on two sibling slots.
  • Truthiness follows JavaScript: null, undefined, false, 0, -0, NaN, and "" are falsy. Everything else is truthy.

Because children are evaluated once, a list read inside Show children freezes at its first value. Put the list in For. See Pitfalls.

For

Reactive list. It re-renders its items when the array returned by each changes:

// src/components/Todos.chr
import { e, For, hookState } from "../../pkg/chroma.js";

export function Todos() {
  const [todos, setTodos] = hookState([{ id: 1, text: "Write docs" }]);
  const add = () => setTodos([...todos(), { id: todos().length + 1, text: "Ship it" }]);

  return e("div", null,
    e("button", { onClick: add }, "Add"),
    e("ul", null, e(For, { each: () => todos(), children: (item) => e("li", null, item.text) })),
  );
}
  • each is required and must be a getter returning an array. Otherwise For throws chroma: For requires props.each to be a getter (function). If it returns something that is not an array, For renders nothing.
  • children is required and must be a function (item) => node. Otherwise For throws chroma: For requires props.children to be a function: (item) => node.
  • There is no key prop. Every change to the tracked array clears and recreates all item nodes, so focus and local DOM state inside items are lost.
  • The item function runs untracked. Only each is subscribed.
  • If the item function throws, that item is skipped silently.

Mutating the array in place does not notify. Write a new array: setTodos([...todos(), item]), not todos().push(item). See Reactivity.

On this page