Appearance
Visibility System
Type: Explanation | Audience: Interface builder user | Goal: Understand how conditional visibility works
Three Levels of Visibility
Conditional visibility can be applied at three levels, each controlling a different scope:
Block-level
Show or hide an individual block within a row. Other blocks in the same row can remain visible. Block visibility is configured in the Visibility sub-tab of Block Settings in the sidebar.
Row-level
Show or hide an entire row, including all its columns and blocks. Row visibility is configured in Row Settings → Conditional Visibility.
Area-level
Show or hide a named group of consecutive rows. You pick which rows the area covers when you create it. When the area is hidden, every row inside it disappears. Area visibility takes the highest priority — if an area is hidden, its rows are hidden regardless of their individual row or block visibility settings. Area visibility is configured in Area Settings → Conditional Visibility.
How Conditions Are Evaluated
A Condition is a string that the builder resolves at render time. After Data Picker placeholders are replaced with their live values, the result is treated as plain text — there is no expression engine. The visibility check is therefore a simple string comparison:
- An empty string (or only whitespace) means hidden. The builder flags this state with an "Always hidden" warning so it doesn't ship by accident — see the how-to for what the indicator looks like.
- The string
"false"means hidden. - Any other value means visible.
When the Conditional Visibility toggle is off, the element is always visible — whatever the Condition says.
Comparisons need functions, not operators
Operators in conditions are read as plain text
You cannot write {{deal.value}} > 1000 and expect a comparison — that resolves to the literal string "4000 > 1000", which is non-empty and not "false", so the element would always be visible.
Use Data Picker functions for any non-trivial check:
| Goal | Condition |
|---|---|
| Show when a value is set | isNotEmpty(deal.value) |
| Show when value greater than 1000 | gt(deal.value, 1000) |
| Show when stage equals "Negotiation" | equals(deal.stage_name, "Negotiation") |
| Hide when no products on deal | isNotEmpty(deal.products) |
| Show only one of several branches | if(equals(deal.status, "won"), "show", "") |
| Show when a value has any substance | boolean(deal.value) |
These all return either "true" / a non-empty string (visible) or "false" / "" (hidden). See the Functions reference for the full list.
boolean and the plain logic functions read an empty list differently. if, and, or and not follow JavaScript, where an empty array counts as true, so if(deal.products; "show"; "") shows the element for a deal with no products. boolean calls an empty array and an empty object false, so boolean(deal.products) hides it. Use boolean whenever the condition asks "is there anything here".
Hide vs. Remove
When a condition evaluates to hidden, the When hidden choice decides what happens to the space the element was taking. The control is labelled Hide (keep space) or Remove on every level (block, row, area).
Hide (keep space)
The element becomes invisible but keeps its space in the layout. The row or block still occupies its normal height and width — it is just not rendered visually. Layout never shifts when an element toggles.
Remove
The element is removed from the layout entirely. For rows and areas, that means no vertical space at all. For blocks, Remove has two sub-modes (the radio group beneath Remove):
- Keep original sizes — the block is gone, but its column stays at its original width as empty space. Other blocks stay in their positions.
- Redistribute space (Recommended) — the block is gone and its column's width is shared out among the remaining visible blocks in the row. For example, if a 33% column is removed, each of the two remaining 33% columns grows to roughly 50%. Widths are only redistributed among columns that use the same unit: percentages with percentages, pixels with pixels, and auto (
1fr) with auto.
If every block in a row is hidden with Remove + Redistribute space, the whole row collapses out of the layout.
Priority
When multiple visibility levels apply to the same content, the evaluation order is:
- Area visibility (checked first — highest priority)
- Row visibility (checked second)
- Block visibility (checked last)
If an area is hidden, its rows are hidden regardless of row-level or block-level settings. If a row is hidden, its blocks are hidden regardless of block-level settings.
Visibility and Looping
Area visibility runs before loop expansion. If a looping area has its visibility condition evaluate to hidden, the loop is skipped entirely — zero iterations are rendered, just as if the target array were empty. When the area is visible, the rows inside it are duplicated once per iteration in the normal way, and per-row or per-block visibility conditions are then evaluated against each iteration's data.
Use Cases
- Role-based content: show admin controls only when is_admin is
"true"— boolean fields resolve to either"true"or"false", which the visibility check handles directly. - Stage-dependent displays: branch on a specific stage with equals(deal.stage_id, "5") rather than just checking emptiness.
- Threshold checks: hide an area when the deal value is below a number, e.g. lt(deal.value, 1000).
- Empty data handling: hide a chart or table when no data is available, e.g. isNotEmpty(custom.results).
- Progressive forms: show extra fields based on earlier selections (carried via search params or variables) using if(equals(searchparams.tier, "enterprise"), "show", "").