I have a Neo field that contains blocks for hero, body, button etc. For every block except hero, I want them to be wrapped in a containing element. So I have this:

{% for module in entry.pageBuilder.all() if module.level == 1 %}
  {% if module.type == 'moduleHero' %}
    {% include '_partials/page-builder/_moduleHero' %}
  {% else %}
    {% if loop.index == 2 %}
      <div class="container mx-auto">
        <div class="px-4 py-12">
          <div class="content clearfix">
      {% endif %}

          {% include '_partials/page-builder/_' ~ module.type %}

      {% if loop.last %}
    {% endif %}
  {% endif %}
{% endfor %}

I only want the containing element at the beginning and end of the loop.

But with this code I get:

The "loop.last" variable is not defined when looping with a condition.

Which I don't understand. Can someone explain why that is?

2 Answers 2


Just to provide a slight improvement to @Matanya's solution: Instead of filtering the array of Neo blocks, you can limit your query to the blocks you actually want. This way, you're not loading blocks you don't actually need. For example, to only get level one blocks:

{% for module in entry.pageBuilder.level(1).all() %}

See the Templating docs for Neo for details.

  • 1
    Thanks @MoritzLost. This is definitely better
    – Matanya
    Nov 30, 2021 at 9:20
  • 1
    I was looking in completely the wrong spot for the conditional that was causing the problem. I was looking at the inner conditional and had code blindness for the one on the for loop itself. :/ Thanks both for pointing me in the right direction! 👍
    – Tyssen
    Dec 1, 2021 at 1:10

This is a Twig exception. When you are using an if condition on the for loop itself Twig has no way of knowing how many elements will be iterated over, or whether it is the first or last element.

See: https://github.com/twigphp/Twig/issues/687

I see that support for using if on for loops was removed altogether on v3: https://github.com/twigphp/Twig/commit/dca3f74e49d8a2a3ffebeba826daeb8de014d286#diff-ecec88c33adb7591ee6aa88e29b62ad52ef443611cba5e0f0ecac9b5725afdbaR3

Craft appears to be using v2, which is why this is still supported. https://github.com/craftcms/cms/blob/4330199e5b7001ca85b9d919d81e465dcc275742/composer.json#L52

A possible solution would be to use a filter instead, so rather than pruning irrelevant records during iteration, you serve twig the full pre-filtered array in advance. https://twig.symfony.com/doc/2.x/filters/filter.html

{% for module in entry.pageBuilder.all() | filter(module=>module.level == 1) %}

Not tested of course

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.