Loops
A loop repeats a block of markup once for every item in a collection. Write the opening tag with the collection to walk through and a name for the current item, then close the loop with {/each}. Between the tags you read the current item's fields with {alias.field}.
{each nav_links as=link}
<a href="{link.url}">{link.label}</a>
{/each}
The runtime renders the block once per item and drops the item name when the loop ends, so the same alias can be reused in a later loop.
Reading fields from the current item
Use {alias.field} for any field the collection exposes, and add more segments to read a value nested inside a field. Only the current item is in scope, so {group.label} works inside a loop declared with as=group.
{each nav_groups as=group}
<section class="menu-group">
<h3>{group.label}</h3>
</section>
{/each}
A field that resolves to nothing renders as an empty string, exactly like a token used outside a loop.
Closing the loop
{/each} closes the most recent loop. When blocks are long or nested you can add the collection name as a label, for example {/each nav_links}. The label is for readability, and it should match the collection named in the opening tag.
<ul>
{each nav_links as=link limit=4}
<li><a href="{link.url}">{link.label}</a></li>
{/each nav_links}
</ul>
Limiting results with limit
Add limit=<n> to the opening tag to render at most n items, starting from the first. Use it to keep a preview short without changing the collection itself.
{each nav_links as=link limit=4}
<li><a href="{link.url}">{link.label}</a></li>
{/each}
limit is a whole number greater than zero. Omitting it renders every item in the collection.
Nested loops
A loop can contain another loop. The inner loop runs in full for each item of the outer loop. Give each loop a different alias so both scopes stay readable.
<table>
<thead>
<tr><th>Group</th><th>Link</th></tr>
</thead>
<tbody>
{each nav_groups as=group}
{each group.links as=link limit=3}
<tr>
<td>{group.label}</td>
<td><a href="{link.url}">{link.label}</a></td>
</tr>
{/each group.links}
{/each}
</tbody>
</table>
Form field groups
Login, signup, and settings forms expose their fields as groups. The for=<name> part selects which form definition to use, and each group exposes {group.columns} together with a fields collection.
{each form.fieldGroups for=settings as=group}
<div class="grid grid-cols-{group.columns}">
{each group.fields as=field}
<label for="{field.id}">{field.label}</label>
<input id="{field.id}" name="{field.id}" placeholder="{field.placeholder}" type="{field.type}">
{/each group.fields}
</div>
{/each form.fieldGroups}
A select field carries its own options collection. Loop over it inside the field loop to render each choice.
{each field.options as=opt}
<option value="{opt.value}">{opt.label}</option>
{/each field.options}
Handling an empty collection
An {each} over an empty collection renders nothing, which can leave an empty section behind. Wrap the loop in {if <collection>} to show the markup only when there is at least one item, and use {else} for an empty state.
{if nav_links}
<nav aria-label="Main navigation">
<ul>
{each nav_links as=link}
<li><a href="{link.url}">{link.label}</a></li>
{/each}
</ul>
</nav>
{else}
<p>No navigation links yet.</p>
{/if}
The same guard works for any collection, such as a list of catalog templates.
{if templates.items}
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{each templates.items as=template limit=6}
<article>
<h3>{template.name}</h3>
<p>{template.description}</p>
</article>
{/each templates.items}
</div>
{/if}
Collections you can loop over
| Collection | What it yields | Example opening tag |
|---|---|---|
nav_links, nav_links_footer, nav_groups, nav_groups_footer | Site navigation links and their groups | {each nav_links as=link} |
templates.items, template.tags | Catalog templates and their tags | {each templates.items as=template} |
form.fieldGroups, group.fields, field.options | Form groups, fields, and select options | {each form.fieldGroups for=login as=group} |
Collections that come from an extension are documented with that extension. See Commerce extension templating, Portfolio extension templating, and Blog extension templating.
Syntax at a glance
| Syntax | Meaning |
|---|---|
{each <collection> as=<alias>} | Starts a loop over the collection and names the current item |
{alias.field} | Reads a field from the current item |
{/each} | Closes the most recent loop |
{/each <collection>} | Closes the loop with a readability label |
limit=<n> | Renders at most n items |
for=<form> | Selects the form definition for form.fieldGroups |
{if <collection>} | Runs the block only when the collection has items |

