Skip to content

Content Flows

Flows are Press's way of separating content from layout. A flow is a named container of content that can be painted into one or more frames across one or more pages. This separation is what makes Press templates truly reusable.

Defining Flows

Flows live inside the <flows> top-level element. Each flow has a tag name that becomes its identifier:

xml
<flows>
  <body>
    <h1>Annual Report</h1>
    <p>This year we achieved significant milestones...</p>
  </body>

  <sidebar>
    <h3>Key Figures</h3>
    <p>Revenue: $2.4M</p>
    <p>Growth: 18%</p>
  </sidebar>
</flows>

Referencing Flows

Inside a page definition, use <flow name="..." /> to place content from a flow:

xml
<document format="A4" page-margin="2cm">
  <page>
    <flow name="body" />
  </page>
</document>

Press fills the frame containing the flow reference with as much content as will fit.

Multi-Column Layouts

Reference the same flow multiple times to create columns. Press fills the first frame, then continues into the second:

xml
<page>
  <frame direction="row" width="100%" height="100%">
    <frame width="48%">
      <flow name="body" />
    </frame>
    <frame width="4%" />
    <frame width="48%">
      <flow name="body" />
    </frame>
  </frame>
</page>

The body content flows from the left column into the right column automatically.

Flowing Across Pages

To continue a flow across multiple pages, use <repeat flow="...">:

xml
<document format="A4" page-margin="2cm">
  <repeat flow="body">
    <page>
      <flow name="body" />
    </page>
  </repeat>
</document>

Press keeps generating pages and filling them with body content until the flow is fully consumed.

Alternating Page Layouts

Place multiple pages inside a repeat for alternating designs:

xml
<repeat flow="body">
  <page>
    <frame margin-bottom="0.5cm" font-color="grey" text-align="right">
      {{ data.author }}
    </frame>
    <flow name="body" />
  </page>
  <page>
    <frame margin-bottom="0.5cm" font-color="grey">
      {{ data.title }}
    </frame>
    <flow name="body" />
  </page>
</repeat>

This alternates between a right-aligned author header and a left-aligned title header, similar to how printed books alternate left and right pages.

Markdown Flows

Set type="markdown" on a flow to write content in markdown instead of Press XML:

xml
<flows>
  <body type="markdown">
    # Quarterly Review

    Our team delivered several key improvements this quarter:

    - Reduced processing time by **40%**
    - Launched three new product features
    - Expanded to two new markets

    ## Financial Summary

    Revenue grew steadily across all segments. The operations
    team maintained costs within budget while scaling output.
  </body>
</flows>

Press automatically strips leading indentation from markdown content.

Mixing Markdown and Press

You can embed Press elements directly inside a markdown flow when you need more control:

xml
<flows>
  <body type="markdown">
    # Project Status

    The project is on track for delivery in Q2.

    <frame direction="row" space-before-desired="12pt" space-after-desired="12pt">
      <frame width="fill" background-color="#f0fdf4" padding="12pt" border-radius="4pt">
        <h3 font-color="#16a34a">Completed</h3>
        <p>12 tasks</p>
      </frame>
      <frame width="8pt" />
      <frame width="fill" background-color="#fef2f2" padding="12pt" border-radius="4pt">
        <h3 font-color="#dc2626">Remaining</h3>
        <p>4 tasks</p>
      </frame>
    </frame>

    ## Next Steps

    The team will focus on integration testing next sprint.
  </body>
</flows>

Flow Groups

Use <group> to organise a flow into named sections. Groups are essential for documents with repeating section structures:

xml
<flows>
  <group name="chapters">
    <introduction type="markdown">
      <data type="json">
        { "chapter-title": "Introduction", "chapter-number": 1 }
      </data>
      This report examines market conditions across three regions...
    </introduction>

    <methodology type="markdown">
      <data type="json">
        { "chapter-title": "Methodology", "chapter-number": 2 }
      </data>
      Data was collected from public records spanning 2020 to 2025...
    </methodology>

    <findings type="markdown">
      <data type="json">
        { "chapter-title": "Findings", "chapter-number": 3 }
      </data>
      The analysis revealed consistent growth in urban centres...
    </findings>
  </group>
</flows>

Then iterate over the groups in your document:

xml
<document format="A4" page-margin="2cm">
  <repeat group="chapters" item="chapter">
    <page>
      <h1>{{ chapter.data.chapter-number }}. {{ chapter.data.chapter-title }}</h1>
      <flow name="chapter" />
    </page>
  </repeat>
</document>

Flow Data

Each flow (or group member) can include its own <data> element. This data is accessible within the flow and on any page that renders it:

xml
<flows>
  <group name="properties">
    <unit-a>
      <data type="json">
        { "address": "14 Maple Street", "bedrooms": 3, "price": "£425,000" }
      </data>
      <p>A charming three-bedroom property with garden views...</p>
    </unit-a>

    <unit-b>
      <data type="json">
        { "address": "7 Oak Avenue", "bedrooms": 2, "price": "£310,000" }
      </data>
      <p>Modern two-bedroom flat in a converted Victorian terrace...</p>
    </unit-b>
  </group>
</flows>

On the page, access the flow's data through the item variable:

xml
<repeat group="properties" item="property">
  <page>
    <h1>{{ property.data.address }}</h1>
    <p>{{ property.data.bedrooms }} bedrooms - {{ property.data.price }}</p>
    <flow name="property" />
  </page>
</repeat>

Anonymous Flows

You can define a flow directly inside a page without giving it a name. This is useful for one-off content:

xml
<page>
  <flow>
    <h1>Disclaimer</h1>
    <p>This document is for informational purposes only...</p>
  </flow>
</page>

It's also possible to give anonymous flows a name and have the content repeat over multiple pages. This is especially useful for things like long, multi-page table of contents:

xml
<!-- table of contents -->
<page flow="toc" repeat="true" deferred="true">
    <!-- in anon flow so heading only appears on first page as flow is then empty -->
    <flow name="toc-heading">
        <h1 outline="meta" padding-bottom="0.7cm" font-color="primary">Table of Contents</h1>
    </flow>
    <frame>
        <!--
            another anonymous flow to allow table of contents
            to be more than one page long
          -->
        <flow name="toc">
            <repeat outline="heading" item="h1">
                <!-- render h1s -->
                {{ h1.index }}. {{ h1.text-content }} - Page <value pad="2">{{ h1.page-number }}</value>
            </repeat>
        </flow>
    </frame>
</page>