Loop
{% for %}Repeats inner blocks for each item in a data source.The Loop block repeats its inner blocks for each item in a data source. You select a query type and arguments in the block interface, and Unblock renders the inner blocks once per item.
Data sources
| Function | What it queries |
|---|---|
posts | Inherit the main WordPress query (archives, search) |
get_posts({...}) | Posts, pages, or any custom post type |
get_terms({...}) | Categories, tags, or custom taxonomies |
get_users({...}) | Site users by role |
get_items([...]) | A static array or dynamic data |
Data alias
The Data Alias field in the block interface defines the variable name you use to access the current item inside the loop:
{% for post in get_posts({...}) %} {# post.title, post.excerpt… #}
{% for term in get_terms({...}) %} {# term.name, term.count… #}
{% for user in get_users({...}) %} {# user.name, user.email… #}
{% for item in get_items([...]) %} {# item.name, item.url… #}
You can name the variable anything — it doesn't have to match the function:
{% for product in get_posts({'post_type': 'product'}) %}
{{ product.title }}
{{ product.meta('price') }}
{% endfor %}
The data alias exposes all fields of the corresponding provider: Post, Term, or User.
Loop variable
The loop variable gives you metadata about the current iteration:
| Property | Type | Description |
|---|---|---|
loop.index | int | Current iteration (1-based) |
loop.index0 | int | Current iteration (0-based) |
loop.revindex | int | Iterations from the end (1-based) |
loop.revindex0 | int | Iterations from the end (0-based) |
loop.first | bool | True if first iteration |
loop.last | bool | True if last iteration |
loop.length | int | Total number of items |
{{ loop.index }}. {{ post.title }}
{{ loop.first ? 'first-item' : '' }}
{{ term.name }}{{ loop.last ? '' : ', ' }}
Empty state
When a loop returns no items, its content doesn't render. Use a Condition block after the loop to show a fallback:
{% for event in get_posts({'post_type': 'event'}) %}
<div class="event">{{ event.title }}</div>
{% endfor %}
{% if not events|length %}
<p>No events found.</p>
{% endif %}
Here events is the query name set in the Loop block interface. You can also use {% if events.found_posts == 0 %}.
Nested loops
Loops can be nested. Use different variable names to avoid conflicts:
{% for term in get_terms({'taxonomy': 'category'}) %}
<h2>{{ term.name }}</h2>
{% for post in get_posts({'category_name': term.slug}) %}
<a href="{{ post.link }}">{{ post.title }}</a>
{% endfor %}
{% endfor %}
If you nest two loops with the same variable name (e.g. two for post in ...), the inner loop shadows the outer one — you lose access to the outer item. Always use different variable names for nested loops.
Pagination
The Loop block can assign a query name so you can reference pagination after the loop. If you need pagination or a total count before the loop, use a Variable block instead.
{% set products = get_posts({'post_type': 'product', 'posts_per_page': 12}) %}
<p>{{ products.found_posts }} products</p>
{% for post in products %}
<h2>{{ post.title }}</h2>
{% endfor %}
{{ products.pagination }}
See Pagination for the full reference — query properties, page objects, and custom markup.
Next steps
- Pagination — prev/next links and page numbers
- get_posts() — query posts, pages, and custom post types
- get_terms() — query categories, tags, and taxonomies
- get_users() — query users by role
- get_items() — loop over static data and repeaters
- Variable block — store queries for reuse