Skip to content

Data and Templating

Press separates content from presentation, and data is a key part of that separation. The <data> element lets you define structured information that can be referenced throughout your document using {{ }} expressions.

Defining Data

JSON Data

The most commonly used format. Use type="json":

xml
<data type="json">
{
  "company": "Meridian Consulting",
  "date": "15 January 2026",
  "client": {
    "name": "Helena Torres",
    "title": "Operations Director"
  },
  "items": [
    { "description": "Strategy workshop", "hours": 8, "rate": 150 },
    { "description": "Implementation review", "hours": 4, "rate": 150 }
  ]
}
</data>

CSV Data

For tabular content, use type="csv":

xml
<data type="csv" name="table-data">
Month,Revenue,Expenses
January,42000,31000
February,45000,29000
March,51000,33000
</data>

The above CSV data is accessible as data.table-data, where each row is an object with column-name keys. If name is missing, the data can be accessed under data.rows.

XML Data

You can also define data using XML elements:

xml
<data>
  <title>Quarterly Report</title>
  <author>J. Hartfield</author>
  <date>March 2026</date>
</data>

Each child element becomes a data field: {{ data.title }}, {{ data.author }}, etc.

Referencing Data

Use {{ }} expressions to insert data values into your document:

In Text Content

xml
<h1>{{ data.title }}</h1>
<p>Prepared by {{ data.author }} on {{ data.date }}</p>

In Attributes

xml
<img src="{{ data.logo }}" />
<frame width="{{ data.sidebar ? '60%' : '100%' }}">

Nested Paths

Access nested objects with dot notation:

xml
<p>{{ data.client.name }}, {{ data.client.title }}</p>

Array Items

Access array elements by index:

xml
<p>First item: {{ data.items[0].description }}</p>

Ternary Expressions

Use the ternary operator for conditional values:

xml
{{ condition ? valueIfTrue : valueIfFalse }}

Common patterns:

xml
<!-- Default value -->
<frame>{{ data.subtitle ? data.subtitle : 'No subtitle provided' }}</frame>

<!-- Conditional sizing -->
<frame width="{{ data.has-image ? '60%' : '100%' }}">

<!-- Conditional text -->
<span font-style="{{ data.urgent ? 'bold' : 'normal' }}">{{ data.status }}</span>

Data Merging

When using templates with payloads (via the API or platform), both can include <data> elements. Press merges them:

Template data provides defaults:

xml
<!-- template.press -->
<data type="json">
{
  "currency": "£",
  "tax_rate": 20,
  "company": "Default Corp"
}
</data>

Payload data overrides on conflict:

xml
<!-- payload.press -->
<data type="json">
{
  "company": "Meridian Consulting",
  "client": "Helena Torres",
  "total": 1800
}
</data>

Result: currency and tax_rate come from the template, company uses the payload's value, and client and total are added from the payload.

Data in Assets

Define constant data in <assets> for values that should never be overridden by a payload. This is particularly useful for default, fall-back values, e.g.:

xml
<assets>
  <data type="json">
  {
    "defaults": {
      "currency": "$",
      "logo": "https://example.com/logo.png",
      "company": "Meridian Consulting"
    }
  }
  </data>
</assets>

Access with {{ assets.data.defaults.currency }}.

xml
<frame>{{ data.currency ? data.currency : assets.data.defaults.currency }}</frame>

Data in Flows

Flows and flow group members can define their own <data> elements:

xml
<flows>
  <group name="sections">
    <overview type="markdown">
      <data type="json">
        { "section-title": "Overview", "icon": "chart-bar" }
      </data>
      The business performed strongly across all segments...
    </overview>
  </group>
</flows>

This data is accessible when iterating:

xml
<repeat group="sections" item="section">
  <page>
    <h1>{{ section.data.section-title }}</h1>
    <flow name="section" />
  </page>
</repeat>

Example: A Complete Invoice

Here's how data powers a simple invoice:

xml
<press>
  <document format="A4" page-margin="2cm">
    <page>
      <frame direction="row">
        <frame width="50%">
          <frame font-size="10pt">Invoice To:</frame>
          <frame font-size="14pt" font-style="bold">{{ data.to.name }}</frame>
          <frame font-size="10pt">{{ data.to.company }}</frame>
        </frame>
        <frame width="50%">
          <frame font-size="10pt">Invoice From:</frame>
          <frame font-size="14pt" font-style="bold">{{ data.from.name }}</frame>
          <frame font-size="10pt">Date: {{ data.date }}</frame>
        </frame>
      </frame>

      <table>
        <tr>
          <th width="50%">Description</th>
          <th>Hours</th>
          <th>Rate</th>
          <th text-align="right">Total</th>
        </tr>
        <repeat data="data.items" item="line">
          <tr>
            <td>{{ line.description }}</td>
            <td text-align="center">{{ line.hours }}</td>
            <td text-align="center">{{ data.currency }}{{ line.rate }}</td>
            <td text-align="right">{{ data.currency }}{{ line.total }}</td>
          </tr>
        </repeat>
      </table>

      <frame h-align="right" padding-top="12pt">
        <frame font-style="bold" font-size="14pt">
          Total: {{ data.currency }}{{ data.grand-total }}
        </frame>
      </frame>
    </page>
  </document>

  <data type="json">
  {
    "currency": "£",
    "date": "15 January 2026",
    "to": { "name": "Helena Torres", "company": "Apex Industries" },
    "from": { "name": "R. Whitfield", "company": "Meridian Consulting" },
    "items": [
      { "description": "Strategy workshop", "hours": 8, "rate": 150, "total": 1200 },
      { "description": "Implementation review", "hours": 4, "rate": 150, "total": 600 }
    ],
    "grand-total": 1800
  }
  </data>
</press>