<what>
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"}]}]
</what>
<h1 class="text-3xl font-bold mb-2" style="color: #111827; letter-spacing: -0.02em;">Step 6 — Loops & Conditionals</h1>
<p class="text-gray-500 text-sm mb-8">Render lists and conditional content without JavaScript.</p>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">The loop tag</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
<code><loop data="#items#" as="item"></code> iterates over an array. Nested properties use dot notation: <code>#item.name#</code>.
</p>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><loop data="#products#" as="product">
<div>
<strong>#product.name#</strong>
<span>$#product.price#</span>
</div>
</loop></code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Live loop example</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
Data is fetched via the <code><what></code> block and looped over in the template. Here's a hardcoded list of programming languages with their stats:
</p>
<div class="card mb-4">
<div class="card-body" style="padding: 0;">
<table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
<thead>
<tr style="background: #f9fafb; border-bottom: 1px solid #e5e7eb;">
<th style="text-align: left; padding: 0.75rem 1rem; font-weight: 600; color: #374151;">Language</th>
<th style="text-align: left; padding: 0.75rem 1rem; font-weight: 600; color: #374151;">Type</th>
<th style="text-align: left; padding: 0.75rem 1rem; font-weight: 600; color: #374151;">Year</th>
</tr>
</thead>
<tbody>
<loop data="#languages#" as="lang">
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.625rem 1rem; font-weight: 500; color: #111827;">#lang.name#</td>
<td style="padding: 0.625rem 1rem;">
<if lang.type == "Systems">
<span class="badge" style="background: #ede9fe; color: #5b21b6;">#lang.type#</span>
</if>
<if lang.type == "Web">
<span class="badge" style="background: #dbeafe; color: #1e40af;">#lang.type#</span>
</if>
<if lang.type == "Scripting">
<span class="badge" style="background: #fef3c7; color: #92400e;">#lang.type#</span>
</if>
<if lang.type == "Functional">
<span class="badge" style="background: #d1fae5; color: #065f46;">#lang.type#</span>
</if>
</td>
<td style="padding: 0.625rem 1rem; color: #6b7280;">#lang.year#</td>
</tr>
</loop>
</tbody>
</table>
</div>
</div>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><loop data="#languages#" as="lang">
<tr>
<td>#lang.name#</td>
<td>#lang.type#</td>
<td>#lang.year#</td>
</tr>
</loop></code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Conditionals</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
Use <code><if ...></code> to show/hide content. <code><unless></code> is the negation. <code><else/></code> provides the fallback.
</p>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><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></code></pre>
<p class="text-gray-600 mb-3" style="line-height: 1.7; margin-top: 0.75rem;">
Always quote string values (<code>status == "active"</code>) — a bare word on the right side is treated as another variable. There are no parentheses; nest <code><if></code> blocks for complex logic.
</p>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">The <code>cond</code> attribute (legacy, still accepted)</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
The simplified form above is the recommended syntax. Earlier versions used a <code>cond</code> attribute with <code>#...#</code> wrapping — it still works, so older templates keep rendering:
</p>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><!-- 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></code></pre>
<p class="text-gray-600 mb-3" style="line-height: 1.7; margin-top: 0.75rem;">
In the simplified form, use keyword operators — a literal <code>></code> would close the HTML tag:
</p>
<div class="card mb-4">
<div class="card-body">
<table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
<thead>
<tr style="border-bottom: 1px solid #e5e7eb;">
<th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Keyword</th>
<th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Equivalent</th>
<th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Meaning</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>gt</code></td>
<td style="padding: 0.5rem 0.75rem;"><code>></code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Greater than</td>
</tr>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>lt</code></td>
<td style="padding: 0.5rem 0.75rem;"><code><</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Less than</td>
</tr>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>gte</code></td>
<td style="padding: 0.5rem 0.75rem;"><code>>=</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Greater than or equal</td>
</tr>
<tr>
<td style="padding: 0.5rem 0.75rem;"><code>lte</code></td>
<td style="padding: 0.5rem 0.75rem;"><code><=</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Less than or equal</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Live conditional demo</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
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.
</p>
<div class="card mb-4">
<div class="card-body">
<if session.tut_mode == "pro">
<div class="alert" style="background: #ede9fe; border: 1px solid #c4b5fd; color: #5b21b6; border-radius: 0.5rem; padding: 0.75rem 1rem; margin-bottom: 1rem;">
Pro mode is ON. You see advanced options.
</div>
</if>
<unless session.tut_mode == "pro">
<div class="alert" style="background: #f3f4f6; border: 1px solid #e5e7eb; color: #374151; border-radius: 0.5rem; padding: 0.75rem 1rem; margin-bottom: 1rem;">
Standard mode. Click the button to switch.
</div>
</unless>
<div class="flex gap-2">
<button class="btn btn-outline btn-sm" w-set="session.tut_mode = pro">Enable Pro</button>
<button class="btn btn-outline btn-sm" w-set="session.tut_mode = standard">Standard</button>
</div>
</div>
</div>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Nested loops</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
Loops can be nested. Each loop has its own scope — use distinct <code>as</code> names. Here's a live example with categories and their items:
</p>
<div class="card mb-4">
<div class="card-body">
<loop data="#categories#" as="category">
<div style="margin-bottom: 0.75rem;">
<div class="text-sm font-semibold text-gray-900 mb-1">#category.name#</div>
<div style="display: flex; gap: 0.375rem; flex-wrap: wrap;">
<loop data="#category.items#" as="item">
<span class="badge" style="background: #eef2ff; color: #4338ca;">#item.name#</span>
</loop>
</div>
</div>
</loop>
</div>
</div>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><loop data="#categories#" as="category">
<h3>#category.name#</h3>
<loop data="#category.items#" as="item">
<span>#item.name#</span>
</loop>
</loop></code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Loop with empty state</h2>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><unless items>
<p class="text-gray-500">No items yet.</p>
</unless>
<loop data="#items#" as="item">
<div>#item.name#</div>
</loop></code></pre>
</section>
<div class="card" style="border-color: #bbf7d0; background: #f0fdf4;">
<div class="card-body">
<div class="text-sm font-semibold mb-2" style="color: #14532d;">What you learned</div>
<ul class="text-sm space-y-1" style="color: #166534; padding-left: 1.25rem; list-style: disc;">
<li><code><loop data="#items#" as="item"></code> iterates over arrays</li>
<li>Nested data access: <code>#item.name#</code>, <code>#item.meta.created_at#</code></li>
<li><code><if var></code>, <code><else/></code>, <code><unless var></code></li>
<li>Simplified: <code><if count gt 0></code> — no <code>cond</code> or <code>#...#</code> needed</li>
<li>Keyword operators: <code>gt</code>, <code>lt</code>, <code>gte</code>, <code>lte</code></li>
<li>Nested loops: use distinct <code>as</code> names for each level</li>
<li>Loops and conditionals compose — conditionals work inside loops</li>
</ul>
</div>
</div>