Conditional rendering
In plain words
How to show or hide a piece of the page on a condition — and why these are components (Show, Dynamic) rather than a plain ternary.
Values in JSX are bindings — they patch text and attributes in place. When the structure of the DOM must change, you declare a dynamic region.
<Show>
import { Show } from "@continuum-js/dom";
<Show when={user} fallback={() => <Login />}>
{(u) => <Profile user={u} />}
</Show>;when takes a State<T | null | undefined | false>. The subtree is rebuilt only when truthiness flips — updates of an already-truthy value do not rebuild anything (the child receives the value as of the flip; pass States inside for live parts).
The functional form for JSX-free code is when(cond, then, else?) with a State<boolean>.
<Dynamic> — switch on a value
When there are more than two branches, key the region by the value itself:
import { Dynamic } from "@continuum-js/dom";
<Dynamic value={state}>
{(s) =>
s.status === "loading" ? (
<Spinner />
) : s.status === "error" ? (
<ErrorBox error={s.error} />
) : s.status === "ok" ? (
<Profile user={s.value} />
) : null
}
</Dynamic>;The region rebuilds when value changes (Object.is). To avoid rebuilding on irrelevant changes, narrow the State first and de-duplicate:
import { dedupe } from "@continuum-js/std";
const status = dedupe(state.map((s) => s.status));dyn — the primitive
Show and Dynamic are sugar over dyn(state, render): a region bounded by comment markers that disposes the old subtree and builds the new one when the state's value changes. Reach for dyn directly when building your own control-flow component — the router's Outlet is exactly that, keyed by route identity.
<Portal>
Structure that must render elsewhere (modals, tooltips) while belonging to the component's lifetime:
<Portal mount={document.body}>
<div class="modal">…</div>
</Portal>The content is removed and disposed with the owning subtree, wherever it was mounted.
<Catch> — error boundary
A throw while building a piece of the page should not kill the whole app. <Catch> shows a fallback instead and lets the user retry:
import { Catch } from "@continuum-js/dom";
<Catch
fallback={(error, reset) => (
<div class="error">
Something broke: {String(error)}
<button onClick={reset}>Try again</button>
</div>
)}
>
{() => <RiskyWidget />}
</Catch>;- Children must be a thunk (
{() => …}) — eager JSX would run (and throw) beforeCatchgets control. - Caught: a throw while building the children, and a throw during any nested
Show/Dynamic/dynrebuild inside the boundary. The failed subtree's ownership is disposed — no leaked subscriptions or timers. resetrebuilds the children from scratch; an error thrown by the fallback itself escalates to the next boundary up.- Not caught: throws inside binding
mapfunctions (keep them pure). Async/IO errors never throw at all —performandresourcedeliver them as data (Result,Async), which you render like any value.
Unfamiliar term? Every piece of jargon in these docs is explained in the glossary.