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

- `<loop data="#items#" as="item">` iterates over arrays
- Nested data access: `#item.name#`, `#item.meta.created_at#`
- `<if var>`, `<else/>`, `<unless var>`
- Simplified: `<if count gt 0>` — no `cond` or `#...#` needed
- Keyword operators: `gt`, `lt`, `gte`, `lte`
- Nested loops: use distinct `as` names for each level
- Loops and conditionals compose — conditionals work inside loops