Learn
Conditional rendering
Show, hide, and switch between pieces of UI with ternaries, logical operators, and early returns.
Use ordinary JavaScript to decide what to render. The patterns from React work unchanged.
export function Status({ online }: { online: boolean }) { return online ? <span>Online</span> : <span>Offline</span>}Supported forms
Vidact recognizes each of these and compiles it into a region of the DOM that can be swapped when the condition changes:
{isLoading ? <Spinner /> : <Results items={items} />}{error && <p role="alert">{error.message}</p>}{title ?? <em>Untitled</em>}{!isEmpty || <EmptyState />}Early returns and switch statements at the top level of a component work too:
export function Page({ state }: { state: PageState }) { if (state.kind === 'loading') return <Spinner /> if (state.kind === 'error') return <ErrorMessage error={state.error} /> return <Article article={state.article} />}export function Icon({ kind }: { kind: 'check' | 'cross' | 'none' }) { switch (kind) { case 'check': return <CheckIcon /> case 'cross': return <CrossIcon /> default: return null }}Render null, undefined, false, or an empty string to render nothing.
When you use early returns, declare your local variables before the first return. A const that sits between two returning branches is reported as UnsupportedControlFlow, because the compiler cannot tell which branch it belongs to.
What happens when the condition changes
Each branch is a separate piece of DOM with its own lifetime. When the condition flips:
- The old branch's DOM is removed, and any components inside it are disposed. Their state is gone, and their effect cleanups run.
- The new branch is constructed from scratch and inserted in the same place.
This matches React's behaviour when the element type at a position changes. It also means a branch that stays selected keeps its DOM, its focus, and its component state untouched, even when other values change.
If you switch between two instances of the same component with different props, Vidact still treats them as two branches: the first instance is disposed and a second is created. If you want a single instance whose props change, render it unconditionally and pass the varying prop.
{compact ? <Card size="small" /> : <Card size="large" />}<Card size={compact ? 'small' : 'large'} />Conditions in attributes
Conditional attribute values do not create branches; they are ordinary bindings.
<button className={active ? 'tab active' : 'tab'} disabled={!enabled}>An attribute set to undefined, null, or false is removed from the element.
Things the compiler cannot follow
Vidact needs to see the shape of your conditional at compile time. A few patterns are rejected because they hide that shape:
- Returning from inside a
try/finally, or throwing inside atrythat also renders. - Building elements with
createElementfrom values that are only known at runtime. - Rendering elements that were created by code Vidact did not compile.
In each case the fix is to move the decision into a ternary, a logical expression, or an early return. The troubleshooting guide lists the diagnostics you may see.