Skip to content

Advanced Topics

We've already covered the foundations of Press: pages, frames, flows, styles, data, and page definitions. In this section, we'll introduce three features that unlock the real power of Press: conditional rendering, repeating over data, and components.

We'll continue building our AI history document from the previous sections.

Conditional Rendering

Sometimes you want to show a section only when certain data is present. Press supports this with the show-if attribute, which can be added to any element.

Let's add an optional author biography to our document. It should only appear when a biography has been provided in the data:

xml
<frame show-if="data.bio">
  <h2>About the Author</h2>
  <p>{{ data.bio }}</p>
</frame>

If data.bio is present and non-empty, the frame will be rendered. If it's absent or empty, the frame is skipped entirely -- it won't take up any space.

You can use show-if on any element: frames, paragraphs, images, table rows, even entire pages:

xml
<img show-if="data.author-photo" src="{{ data.author-photo }}" />

You can also use ternary expressions inside {{ }} to switch between values based on conditions:

xml
<frame width="{{ data.has-sidebar ? '60%' : '100%' }}">
  <flow name="body" />
</frame>

Repeating Over Data

In the previous sections, we manually wrote out our milestones list in the flow. But what if the milestones came from data? The <repeat> directive lets you iterate over an array and render elements for each item.

First, let's move the milestones into our <data> tag:

xml
<data type="json">
{
  "title": "A History of Artificial Intelligence",
  "author": "Mr P. Mill",
  "milestones": [
    { "year": "1950", "event": "Turing Test proposed" },
    { "year": "1956", "event": "Dartmouth Conference" },
    { "year": "1980", "event": "Expert systems boom" },
    { "year": "2012", "event": "Deep learning revolution" }
  ]
}
</data>

Now we can generate the list from data:

xml
<h2>Key Milestones</h2>
<table>
  <tr>
    <th>Year</th>
    <th>Event</th>
  </tr>
  <repeat data="data.milestones" item="milestone">
    <tr>
      <td>{{ milestone.year }}</td>
      <td>{{ milestone.event }}</td>
    </tr>
  </repeat>
</table>

The <repeat> tag takes two attributes:

  • data -- the array to loop over
  • item -- the variable name for each element (defaults to item)

You can nest repeats, and use show-if inside them:

xml
<repeat data="data.milestones" item="milestone">
  <frame>
    <h3>{{ milestone.year }} - {{ milestone.event }}</h3>
    <p show-if="milestone.description">{{ milestone.description }}</p>
  </frame>
</repeat>

Components

As templates grow, you'll find yourself repeating patterns. Components let you define reusable elements with their own structure and accept inputs via attributes and slots.

Let's create a component for the header we've been using on our left and right pages:

xml
<components>
  <page-header text="">
    <frame width="100%" margin-bottom="0.5cm" font-color="grey"
           text-align="{{ attrs.align ? attrs.align : 'left' }}">
      {{ attrs.text }}
    </frame>
  </page-header>
</components>

Now our page definitions become simpler:

xml
<pages>
  <left-page>
    <page-header text="{{ data.author }}" align="right" />
    <flow name="body" />
  </left-page>

  <right-page>
    <page-header text="{{ data.title }}" />
    <flow name="body" />
  </right-page>
</pages>

Components accept attributes through {{ attrs.attribute-name }}. You can set defaults in the component definition -- here, text defaults to an empty string.

Slots

For components that wrap other content, use <slot /> as a placeholder:

xml
<components>
  <info-box title="" background="#f0f0f0">
    <frame background-color="{{ attrs.background }}" padding="12pt"
           border-radius="4pt">
      <h3>{{ attrs.title }}</h3>
      <slot />
    </frame>
  </info-box>
</components>

Then use it anywhere in your document:

xml
<info-box title="Did You Know?" background="#e8f4e8">
  <p>The term "Artificial Intelligence" was coined at the
     Dartmouth Conference in 1956.</p>
</info-box>

Whatever you place inside the <info-box> tags replaces the <slot /> in the component definition.

Putting It All Together

Here's our AI history document with all three features added:

xml
<press>
  <document width="14.8cm" height="21cm" page-margin="1cm">
    <coverpage />
    <repeat flow="body">
      <left-page />
      <right-page />
    </repeat>
  </document>

  <pages>
    <coverpage>
      <frame left="1cm" width="10cm">
        <h1 font-size="fit">{{ data.title }}</h1>
      </frame>
      <frame left="1cm" top="15cm">
        <h2>{{ data.author }}</h2>
      </frame>
    </coverpage>

    <left-page>
      <page-header text="{{ data.author }}" align="right" />
      <flow name="body" />
    </left-page>

    <right-page>
      <page-header text="{{ data.title }}" />
      <flow name="body" />
    </right-page>
  </pages>

  <flows>
    <body>
      <h1>{{ data.title }}</h1>

      <h2>Key Milestones</h2>
      <table>
        <tr>
          <th>Year</th>
          <th>Event</th>
        </tr>
        <repeat data="data.milestones" item="milestone">
          <tr>
            <td>{{ milestone.year }}</td>
            <td>{{ milestone.event }}</td>
          </tr>
        </repeat>
      </table>

      <h2>AI Domains</h2>
      <ul>
        <repeat data="data.domains" item="domain">
          <li>{{ domain }}</li>
        </repeat>
      </ul>

      <info-box title="Looking Forward" show-if="data.conclusion">
        <p>{{ data.conclusion }}</p>
      </info-box>

      <frame show-if="data.bio">
        <h2>About the Author</h2>
        <p>{{ data.bio }}</p>
      </frame>
    </body>
  </flows>

  <components>
    <page-header text="" align="left">
      <frame width="100%" margin-bottom="0.5cm" font-color="grey"
             text-align="{{ attrs.align }}">
        {{ attrs.text }}
      </frame>
    </page-header>

    <info-box title="" background="#f0f0f0">
      <frame background-color="{{ attrs.background }}" padding="12pt"
             border-radius="4pt">
        <h3>{{ attrs.title }}</h3>
        <slot />
      </frame>
    </info-box>
  </components>

  <styles>
    <h1>
      <font-color>grey</font-color>
    </h1>
  </styles>

  <data type="json">
  {
    "title": "A History of Artificial Intelligence",
    "author": "Mr P. Mill",
    "milestones": [
      { "year": "1950", "event": "Turing Test proposed" },
      { "year": "1956", "event": "Dartmouth Conference" },
      { "year": "1980", "event": "Expert systems boom" },
      { "year": "2012", "event": "Deep learning revolution" }
    ],
    "domains": [
      "Machine Learning",
      "Natural Language Processing",
      "Computer Vision",
      "Robotics"
    ],
    "conclusion": "As we stand at the threshold of artificial general intelligence, the next decade promises even more dramatic changes.",
    "bio": "Mr P. Mill is a researcher in artificial intelligence and the author of several papers on the history of computing."
  }
  </data>
</press>

We now have a fully data-driven, conditionally rendered, component-based document. Try removing bio or conclusion from the data to see those sections disappear.

The rest of this documentation covers each of these features -- and many more -- in depth. Head to the Guides section for comprehensive coverage of any topic, or check the Reference section when you need to look up a specific attribute or element.