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),
);
}whenis required and must be a getter. OtherwiseShowthrowschroma: Show requires props.when to be a getter (function).fallbackis an optional function returning what to show whenwhenis 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
displayon 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) })),
);
}eachis required and must be a getter returning an array. OtherwiseForthrowschroma: For requires props.each to be a getter (function). If it returns something that is not an array,Forrenders nothing.childrenis required and must be a function(item) => node. OtherwiseForthrowschroma: For requires props.children to be a function: (item) => node.- There is no
keyprop. 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
eachis 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.