Skip to main content

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 configure it from the block's sidebar panel — pick a query type, set the arguments, and set a variable name. You can fill the arguments field by field with the Query Builder or write them as a PHP array in code.

A Loop block: a post loop rendered on the canvas, beside its sidebar showing the query name, data alias, query type, and the visual query builder.

Query types​

Pick a query type in the sidebar:

Query typeWhat it queriesArgumentsReference
PostPosts, pages, CPTsPHP arrayQuery Posts
TermCategories, tags, taxonomiesPHP arrayQuery Terms
UserUsers by rolePHP arrayQuery Users
ItemStatic data, repeatersJSON, expression, or variableQuery Items

With the Post query type, turn on Inherit Query to use the main WordPress query instead — for archive pages, search results, and custom post type archives. The query settings are then ignored.

Any argument value can be a dynamic expression instead of a static value — for example post.id or post.categories[0].slug.

Data alias​

The Data Alias field sets the variable name for the current item inside the loop. If you're querying products, set it to product — you'll then access product.title, product.price, etc.

You can name it anything: the alias is only a name. The query type and the item decide which provider fields are available:

Left empty, the alias is the query type: post, term, user or item. Names keep lowercase letters, digits and underscores only. Write two names separated by a comma, key, value, to also get each item's key: see Key-value pairs.

Query name​

The Query Name field assigns a name to the query results so you can reference them outside the loop — for pagination, total counts, or empty state checks.

If you set the query name to products, you can use products.found_posts and products.pagination after the loop. See Pagination for details.

Template syntax​

In the HTML inspector, the Loop block displays as a {% for %} tag. The sidebar settings map directly:

Sidebar fieldInspector syntax
Data alias: product, Query type: Post{% for product in get_posts() %}
Data alias: term, Query type: Term{% for term in get_terms() %}
Data alias: key, value, Query type: Item{% for key, value in get_items() %}
Inherit Query enabled{% for post in posts %}

You can edit the template directly in the inspector. The query arguments stay in the sidebar, so the inspector shows the function with empty parentheses. An expression source shows in full, such as get_posts(post.meta('query')), or post.meta('features') for items; a variable source shows as its name:

{% for product in get_posts() %}
<h2>{{ product.title }}</h2>
<span>{{ product.price|currency }}</span>
{% endfor %}

Loop variable​

Inside a loop, the loop variable gives you metadata about the current iteration:

PropertyTypeDescription
loop.indexintCurrent iteration (1-based)
loop.index0intCurrent iteration (0-based)
loop.revindexintIterations left (1-based)
loop.revindex0intIterations left (0-based)
loop.firstboolTrue if first iteration
loop.lastboolTrue if last iteration
loop.lengthintNumber of items
{{ loop.index }}. {{ post.title }}
{{ loop.first ? 'first-item' : '' }}
{{ term.name }}{{ loop.last ? '' : ', ' }}

loop.index counts iterations, never keys. To read each item's key, name it in the tag: {% for key, value in … %} gives an object's property names, or the position from 0 in a list. See Key-value pairs.

Empty state​

When a loop returns no items, its content doesn't render. Use a Condition block after the loop to show a fallback.

Set a query name on the Loop block (e.g. events), then check the result:

{% if events.found_posts == 0 %}
<p>No events found.</p>
{% endif %}

Nested loops​

Loops can be nested — for example, list categories with their posts. Use different data aliases 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 %}
Common mistake

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.

Next steps​