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, enter arguments, and set a variable name.

Loop block panel showing query type, data alias, and query arguments

Query types

Select a query type from the dropdown 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 arrayQuery Items

Enable Inherit Query instead to use the main WordPress query — for archive pages, search results, and custom post type archives.

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 determines which provider fields are available:

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() %}
Inherit Query enabled{% for post in posts %}

You can edit the template directly in the inspector. The query arguments appear inside the function call using Twig hash syntax:

{% for product in get_posts({'post_type': 'product', 'posts_per_page': 12}) %}
<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.firstboolTrue if first iteration
loop.lastboolTrue if last iteration
loop.lengthintTotal 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.

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