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

2 Answers 2


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') %}
  • 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! Jul 22, 2016 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? Jul 22, 2016 at 16:10
  • I personally would probably use a macro, but an include could work well too. Jul 22, 2016 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") }}
  • Thanks Marion. However, having changed this as suggested. I still get the same error I am afraid. Jul 22, 2016 at 14:52
  • I still think this is a good idea, but as Terry says, not at all his problem Jul 22, 2016 at 15:10

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.