Render lists and conditional content without JavaScript.
<loop data="#items#" as="item"> iterates over an array. Nested properties use dot notation: #item.name#.
<loop data="#products#" as="product">
<div>
<strong>#product.name#</strong>
<span>$#product.price#</span>
</div>
</loop>
Data is fetched via the <what> block and looped over in the template. Here's a hardcoded list of programming languages with their stats:
| Language | Type | Year |
|---|---|---|
| #lang.name# |
|
#lang.year# |
<loop data="#languages#" as="lang">
<tr>
<td>#lang.name#</td>
<td>#lang.type#</td>
<td>#lang.year#</td>
</tr>
</loop>
Use <if ...> to show/hide content. <unless> is the negation. <else/> provides the fallback.
<if user.authenticated>
Welcome, #user.full_name#!
<else/>
Please log in.
</if>
<unless items>
<p>No items found.</p>
</unless>
<!-- Comparisons: quote string values, keep numbers bare -->
<if score gt 90>Grade: A</if>
<if status == "active">Active</if>
<!-- Combine conditions with and / or (and binds tighter) -->
<if user.authenticated and user.role == "admin">Admin tools</if>
<if role == "admin" or role == "editor">Staff area</if>
Always quote string values (status == "active") — a bare word on the right side is treated as another variable. There are no parentheses; nest <if> blocks for complex logic.
cond attribute (legacy, still accepted)
The simplified form above is the recommended syntax. Earlier versions used a cond attribute with #...# wrapping — it still works, so older templates keep rendering:
<!-- Recommended -->
<if active_step == 2>Step 2!</if>
<if count gt 0>There are items.</if>
<unless error>No errors.</unless>
<!-- Legacy long-hand equivalent (still accepted): -->
<if cond="#active_step# == 2">Step 2!</if>
<if cond="#count# > 0">There are items.</if>
<unless cond="#error#">No errors.</unless>
In the simplified form, use keyword operators — a literal > would close the HTML tag:
| Keyword | Equivalent | Meaning |
|---|---|---|
gt |
> |
Greater than |
lt |
< |
Less than |
gte |
>= |
Greater than or equal |
lte |
<= |
Less than or equal |
Toggle a session flag, then refresh the page — conditionals are evaluated on the server each time a page renders, so the switch shows up on the next render.
Loops can be nested. Each loop has its own scope — use distinct as names. Here's a live example with categories and their items:
<loop data="#categories#" as="category">
<h3>#category.name#</h3>
<loop data="#category.items#" as="item">
<span>#item.name#</span>
</loop>
</loop>
<unless items>
<p class="text-gray-500">No items yet.</p>
</unless>
<loop data="#items#" as="item">
<div>#item.name#</div>
</loop>
<loop data="#items#" as="item"> iterates over arrays#item.name#, #item.meta.created_at#<if var>, <else/>, <unless var><if count gt 0> — no cond or #...# neededgt, lt, gte, lteas names for each level