I have tried to setup the following Macro to reuse code for product teasers across my site. The macros looks like this;

{% macro teaser(imgSize, className) %}
    <div class="product-teaser{% if className %} // {{ className }}{% endif %}">
        {% import '_macros/_img' as m_img %}
        {% set thumbAsset = product.productImage.first() %}
        <a href="{{ product.url }}">
            {{ m_img.fixedSize(thumbAsset, imgSize, {alt: product.title, class: 'product-teaser__img' }) }}
        <h3 class="product-teaser__title{% if className %} // {{ className }}__title{% endif %}">{{ product.title }}</h3>
        <p class="product-teaser__price{% if className %} // {{ className }}__price{% endif %}">{{ product.defaultPrice|currency(cart.currency) }}</p>

            <a href="{{ product.url }}" class="btn // btn--sub">View Product</a>
            {# <a href="" class="btn // btn--base">Canine Range</a> #}
{% endmacro %}

and an example to call it like this:

{% set products = craft.commerce.products.relatedTo(category)%}
{% for product in products %}
    {% import '_macros/_product' as product %}
    {{ product.teaser(300, "product-teaser--in-list") }}
{% endfor %}

However, the error I get back is

Variable "product" does not exist

Any idea what I am doing wrong?

EDIT: just to add a bit more feedback. The full error is: enter image description here


product is not defined inside your macro. As the twig macro documentation says:

as with PHP functions, macros don't have access to the current template variables.

So you will need to pass this one in:

{% macro teaser(product, imgSize, className) %}

And when you call it:

{{ m_product.teaser(product, 300, "product-teaser--in-list") }}

I would also add sensible defaults (if there are any that are sensible) for the macro arguments:

{% macro teaser(product, imgSize=300, className='product-teaser--in-list') %}
| improve this answer | |
  • Fantastic. Thanks Marion. This was the issue. I also had to pass in cart too. In this instance there is no sensible default, but this is a good idea to set this when it is required. Thanks again! – Terry Upton Jul 22 '16 at 15:23
  • Out of interest is a macro the best use case for this situation, or would I have been better with an include and extend with and some vars? – Terry Upton Jul 22 '16 at 16:10
  • I personally would probably use a macro, but an include could work well too. – Marion Newlevant Jul 22 '16 at 19:39

Yes! You have two things called product:

{% for product in products %}


{% import '_macros/_product' as product %}

What I would do is

{% import '_macros/_product' as m_product %}
{{ m_product.teaser(300, "product-teaser--in-list") }}
| improve this answer | |
  • Thanks Marion. However, having changed this as suggested. I still get the same error I am afraid. – Terry Upton Jul 22 '16 at 14:52
  • I still think this is a good idea, but as Terry says, not at all his problem – Marion Newlevant Jul 22 '16 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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