Skip to content

match()

Render one of several UI branches based on which predicate is true. Use match() when when() would chain three or more times — the canonical example is the resource() loading/error/data tri-state.

type MatchRender = () => WhisqNode | string | null;
type MatchBranch = readonly [() => boolean, MatchRender];
function match(...branches: MatchBranch[]): () => Child;
function match(...args: [...MatchBranch[], MatchRender]): () => Child;

Each branch is a tuple [predicate, render]. An optional trailing bare render function (not wrapped in a tuple) acts as a fallback when no predicate matches.

ParamTypeDescription
branchesMatchBranch[][() => boolean, () => WhisqNode | string | null] tuples
fallbackMatchRender (trailing, optional)Rendered when no branch predicate is truthy

() => Child — a reactive child that re-evaluates its branches on every read. Pass it as a child to any element.

  • First-true-wins. Branches are evaluated top-to-bottom; the first with a truthy predicate renders. Later branches are skipped.
  • Fallback is optional. Without one, match() renders null when no branch matches.
  • Reactive. Like any function child, match() re-evaluates whenever any signal it touches changes.
import { match, resource, div, p, button, ul, each, li } from "@whisq/core";
const users = resource(() => fetch("/api/users").then((r) => r.json()));
div(
match(
[() => users.loading(), () => p("Loading…")],
[() => !!users.error(), () => div(
p(() => `Error: ${users.error()!.message}`),
button({ onclick: () => users.refetch() }, "Retry"),
)],
[() => !!users.data(), () => ul(each(() => users.data()!, (u) => li(u.name)))],
),
);

Ordering matters — put the most specific predicate first:

import { signal, match, p } from "@whisq/core";
const count = signal(0);
match(
[() => count.value > 10, () => p("More than ten")],
[() => count.value > 0, () => p("Some")], // skipped when count > 10
() => p("None"), // fallback
);

See Conditional Rendering for the full narrative arc.