## 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](/docs/buttons/interface-button/interface-builder/how-to/visibility/conditional-visibility#empty-condition-warning) 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

:::caution[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](/docs/buttons/interface-button/interface-builder/reference/data-binding/functions) 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:

1. **Area visibility** (checked first — highest priority)
2. **Row visibility** (checked second)
3. **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](/docs/buttons/interface-button/interface-builder/how-to/layout/areas#looping-an-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 `{{user.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", "")}}`.
