Skip to main content
Prefab UIs aren’t static. When a user clicks a button, drags a slider, or types into an input, the interface can respond instantly — updating text, toggling visibility, reformatting values, computing totals — all without waiting for a server call. This client-side interactivity is powered by a small expression language that runs inside the renderer. Expressions evaluate against the current state, and the UI re-renders whenever that state changes. Combined with actions that modify state in response to user interaction, this covers the common cases for reactive UIs: counters, toggles, conditional displays, formatted output, and form-driven content. Try clicking the buttons in this counter — it works entirely in the browser, with no server involved: Clicking ”+” triggers SetState("count", "{{ count + 1 }}"). The expression count + 1 evaluates against the current state, updates the count key, and every {{ count }} reference in the UI re-renders with the new value.

What Expressions Can Do

Expressions handle display-time computation — formatting values, computing derived text, and making rendering decisions:
  • Arithmetic{{ price * quantity }}, {{ count + 1 }}
  • Comparisons{{ score > 90 }}, {{ status == 'active' }}
  • Conditional text{{ active ? 'Online' : 'Offline' }}
  • String building{{ first + ' ' + last }}
  • Formatting{{ price | currency }}, {{ name | upper }}
  • Conditional renderingIf("inventory > 0") / Elif / Else

What They Can’t

Expressions are deliberately restricted to evaluating scalar values — numbers, strings, booleans. They cannot construct objects, call functions, make network requests, or produce side effects. Prefab splits interactive behavior into three layers, each with a clear scope:
LayerScopeExamples
ExpressionsCompute display valuesArithmetic, formatting, conditional text, comparisons
ActionsMutate client stateSetState, ToggleState, ShowToast
ToolCallRun server logicData fetching, business logic, complex transformations
If you find yourself wanting to write application logic inside a {{ }} template, that computation belongs in a ToolCall instead.

Where Expressions Appear

The same expression language is used in three places, each with a slightly different purpose: Templates embed expressions inside string props using {{ }} delimiters. The renderer evaluates the expression and substitutes the result. Every string prop on every component supports this — labels, placeholders, URLs, CSS classes, action arguments, anything.
Text("{{ price * quantity | currency }}")
Badge("{{ status | upper }}", variant="secondary")
Conditional rendering uses If, Elif, and Else blocks to show or hide components based on expression results. Each condition evaluates to a truthy or falsy value, and the first matching branch renders.
with If("inventory == 0"):
    Alert("Out of stock", variant="destructive")
with Elif("inventory < 10"):
    Alert("Low stock")
with Else():
    Badge("In stock")

Grammar

The complete expression grammar, from lowest to highest precedence:
expr       -> pipe
pipe       -> ternary ( '|' ( ident ( ':' arg )? | literal ) )*
ternary    -> or ( '?' expr ':' expr )?
or         -> and ( '||' and )*
and        -> not ( '&&' not )*
not        -> '!' not | comp
comp       -> add ( ( '==' | '!=' | '>' | '<' | '>=' | '<=' ) add )?
add        -> mul ( ( '+' | '-' ) mul )*
mul        -> unary ( ( '*' | '/' ) unary )*
unary      -> ( '-' | '+' ) unary | primary
primary    -> '(' expr ')' | number | string | 'true' | 'false' | 'null' | ident
ident      -> name ( '.' name )*
Strings use single quotes inside expressions: {{ status == 'active' }}. Numbers can be integers or floats. Identifiers resolve as dot-paths against the current context.