Skip to content

Conditional Rendering ​

Press lets you show or hide any element based on whether data is present. This is how you build templates that adapt to different inputs -- optional sections, fallback values, and dynamic layouts.

The show-if Attribute ​

Add show-if to any element. The element is rendered only when the condition is truthy (present and non-empty):

xml
<frame show-if="data['executive-summary']">
  <h2>Executive Summary</h2>
  <p>{{ data['executive-summary'] }}</p>
</frame>

If data['executive-summary'] is missing, empty, or null, the entire frame is skipped -- it takes up no space in the layout.

What Counts as Truthy? ​

ValueRendered?
"some text"Yes
123Yes
trueYes
[ ... ] (any array)Yes
{ ... } (any object)Yes
"" (empty string)No
falseNo
"false"Yes
nullNo
Missing fieldNo

An empty array or object is truthy, so show-if="data.items" renders even when there is nothing to show. Test a list for emptiness with count(data.items) == 0.

Once your data has been merged, a null reads the same way a missing field does: it is falsy in show-if, and it renders as nothing when interpolated with {{ }}.

The two differ in the merge itself. A field you leave out falls back to the default in your template; a field you send as null replaces that default. If your template defaults title to "Quarterly Report" and you send "title": null, the title renders empty.

Where an element needs a value to iterate or plot, a null leaves it nothing to work with. Sending "rows": null to a <repeat data="data.rows"> makes Papermill reject the document with message Cannot iterate over 'data.rows'. Should it be an array?.

Using show-if on Different Elements ​

On Frames ​

xml
<frame show-if="data.disclaimer" padding="12pt" background-color="#fef3c7">
  <p font-style="italic">{{ data.disclaimer }}</p>
</frame>

On Images ​

xml
<img show-if="data['company-logo']" src="{{ data['company-logo'] }}" max-height="60pt" />

On Table Rows ​

xml
<table>
  <tr>
    <td>Subtotal</td>
    <td>{{ data.subtotal }}</td>
  </tr>
  <tr show-if="data.discount">
    <td>Discount</td>
    <td>-{{ data.discount }}</td>
  </tr>
  <tr show-if="data.tax">
    <td>Tax ({{ data['tax-rate'] }}%)</td>
    <td>{{ data.tax }}</td>
  </tr>
  <tr>
    <td font-style="bold">Total</td>
    <td font-style="bold">{{ data.total }}</td>
  </tr>
</table>

On Text Inline ​

xml
<p>
  Contact: {{ data.contact.name }}
  <span show-if="data.contact.phone"> | {{ data.contact.phone }}</span>
  <span show-if="data.contact.email"> | {{ data.contact.email }}</span>
</p>

Combining Conditions ​

Use &&, || and ! to test more than one thing in a single condition:

xml
<frame show-if="data.account.active && !data.account.suspended">
  <p>In good standing</p>
</frame>

&& binds tighter than ||, and both bind looser than a comparison, so data.role == 'admin' && data.active groups the way it reads. ! binds tightest of all — tighter than a comparison — so negate a comparison with !(data.count == 0), not !data.count == 0.

In XML an & starts an entity, so a conjunction is written &amp;&amp; inside a Press file. Papermill also accepts a bare &&. || and ! need no escaping.

Ternary Expressions ​

For switching between two values based on a condition, use ternary expressions inside {{ }}:

xml
{{ condition ? valueIfTrue : valueIfFalse }}

Dynamic Sizing ​

xml
<frame width="{{ data.sidebar ? '65%' : '100%' }}">
  <flow name="body" />
</frame>
<frame show-if="data.sidebar" width="30%">
  <flow name="sidebar" />
</frame>

Conditional Text ​

xml
<frame font-color="{{ data.overdue ? '#dc2626' : '#16a34a' }}">
  Status: {{ data.overdue ? 'Overdue' : 'Current' }}
</frame>

Conditional Styling ​

xml
<frame background-color="{{ data.priority ? '#fef2f2' : '#f9fafb' }}"
       border-color="{{ data.priority ? '#fca5a5' : '#e5e7eb' }}"
       border-weight="1pt" padding="12pt">
  <h3>{{ data.title }}</h3>
</frame>

The <show-if> Directive ​

In addition to the attribute form, Press also supports a <show-if> wrapper element with a condition attribute:

xml
<show-if condition="data.subtitle">
  <frame font-size="14pt" font-color="#6b7280">
    {{ data.subtitle }}
  </frame>
  <hr />
</show-if>

This is useful when you want to conditionally render multiple sibling elements together without wrapping them in an extra frame.

Common Patterns ​

Conditional Page Types ​

Use data to select different page layouts:

xml
<repeat group="sections" item="section">
  <page name="{{ section.data['page-type'] ? section.data['page-type'] : 'default-page' }}" />
</repeat>

Default Values ​

xml
<frame>{{ data.currency || '£' }}{{ data.total }}</frame>

|| falls back on any falsy value, so a 0 or an empty string takes the default too. Where zero is a value worth printing, test for it instead: {{ data.total == 0 ? '0.00' : data.total }}.

Or using asset defaults:

xml
<img src="{{ data.logo ? data.logo : assets.data.defaults.logo }}" />