title: "Step 6 — Loops & Conditionals" active_step: 6 languages: [{"name":"Rust","type":"Systems","year":"2015"},{"name":"Go","type":"Systems","year":"2009"},{"name":"TypeScript","type":"Web","year":"2012"},{"name":"Python","type":"Scripting","year":"1991"},{"name":"Elixir","type":"Functional","year":"2012"}] categories: [{"name":"Frontend","items":[{"name":"React"},{"name":"Vue"},{"name":"Svelte"}]},{"name":"Backend","items":[{"name":"Rust"},{"name":"Go"},{"name":"Node"}]},{"name":"Database","items":[{"name":"SQLite"},{"name":"Postgres"}]}]

Step 6 — Loops & Conditionals

Render lists and conditional content without JavaScript.

The loop tag

<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>

Live loop example

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.type# #lang.type# #lang.type# #lang.type# #lang.year#
<loop data="#languages#" as="lang">
  <tr>
    <td>#lang.name#</td>
    <td>#lang.type#</td>
    <td>#lang.year#</td>
  </tr>
</loop>

Conditionals

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.

The 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

Live conditional demo

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.

Pro mode is ON. You see advanced options.
Standard mode. Click the button to switch.

Nested loops

Loops can be nested. Each loop has its own scope — use distinct as names. Here's a live example with categories and their items:

#category.name#
#item.name#
<loop data="#categories#" as="category">
  <h3>#category.name#</h3>
  <loop data="#category.items#" as="item">
    <span>#item.name#</span>
  </loop>
</loop>

Loop with empty state

<unless items>
  <p class="text-gray-500">No items yet.</p>
</unless>

<loop data="#items#" as="item">
  <div>#item.name#</div>
</loop>
What you learned