Macros are one of the most important features of the Twig template language to avoid repetitive contents. In Twig 2.11, usage of macros was simplified and other features were added to help you work with macros.

Automatic macro import

Fabien Potencier

Contributed by
Fabien Potencier
in #3012.

Macros are similar to PHP functions because you can pass arguments to them and the contents generated inside the macro are returned to include them in the place where the macro is called.

On symfony.com we use macros for example to display the "contributor box" in several places to highlight our amazing Symfony code and docs contributors:

1
2
3
4
5
6
{% macro contributor_details(contributor, vertical_layout = false) %}
    <div class="d-flex">
        <img class="avatar {{ vertical_layout ? 'm-b-15' }}" src="...">
        Thanks {{ contributor.name }} for being a Symfony contributor.
    </div>
{% endmacro %}

Before calling to a macro in a template you must import it, even if the macro is defined in the same template. This behavior always felt confusing to some people and made using macros a bit annoying. Starting from Twig 2.11, we've fixed that and macros defined in the same template are imported automatically under the special variable _self:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{# templates/some-template.html.twig #}
{% macro contributor_details(contributor, vertical_layout = false) %}
    <div class="d-flex">
        <img class="avatar {{ vertical_layout ? 'm-b-15' }}" src="...">
        Thanks {{ contributor.name }} for being a Symfony contributor.
    </div>
{% endmacro %}

{# ... #}

{% for contributor in contributors %}
    {# you don't have to import the macro before using it #}
    {{ _self.contributor_details(contributor) }}
{% endfor %}

This automatic import also works for macros themselves, so you can call to a macro inside another macro of the same template without importing it explicitly (this also works for any macro imported globally in the template):

1
2
3
4
5
6
7
8
9
{% macro contributor_details(contributor, vertical_layout = false) %}
    {# ... #}
{% endmacro %}

{% macro contributor_list(contributors) %}
    {% for contributor in contributors %}
        {{ _self.contributor_details(contributor) }}
    {% endfor %}
{% endmacro %}

Checking for macro existence

Fabien Potencier

Contributed by
Fabien Potencier
in #3014.

Another new feature introduced in Twig 2.11 is the support for checking the existence of macros before calling to them thanks to the is defined test. It works both for macros imported explicitly and for auto-imported macros:

1
2
3
4
5
{% import 'templates/_macros.html.twig' as macros %}

{% if macros.contributor_details is defined %}
    {# ... #}
{% endif %}

Macros Scoping

Fabien Potencier

Contributed by
Fabien Potencier
in #3009.

The scoping rules that define which macros are available inside each element of each template have been clearly defined as of Twig 2.11 as follows:

  • Imported macros are available in all blocks and other macros defined in the current template, but they are not available in included templates or child templates (you need to explicitly re-import macros in each template).
  • Macros imported inside a {% block %} tag are only defined inside that block and override other macros with the same name imported in the template. Same for macros imported explicitly inside a {% macro %} tag.

Better macro errors

Recent Twig versions have fixed some edge-cases related to macros (e.g. calling a macro imported in a block from a nested block, etc.) and improved other error messages to make them crystal clear and help you debug issues more easily.

Published in #Twig