Skip to content
semihalev edited this page Mar 11, 2025 · 1 revision

Macros and Reusability

Twig macros are a powerful way to create reusable templates and components. They work like functions that can output template content.

Defining Macros

Macros are defined using the {% macro %} tag:

{% macro input(name, value = '', type = 'text') %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" />
{% endmacro %}

Using Macros

Once defined, macros can be used directly in the same template:

{{ input('username', 'johndoe') }}
{{ input('password', '', 'password') }}
{{ input('submit', 'Login', 'submit') }}

Macros with Default Parameters

Macros can have default parameter values:

{% macro button(text, type = 'button', class = 'btn') %}
    <button type="{{ type }}" class="{{ class }}">{{ text }}</button>
{% endmacro %}

{# Using with defaults #}
{{ button('Click Me') }}

{# Overriding defaults #}
{{ button('Submit', 'submit', 'btn btn-primary') }}

Importing Macros from Other Templates

Macros can be defined in one template and imported into another:

{# In forms.twig #}
{% macro input(name, value = '', type = 'text') %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" />
{% endmacro %}

{# In template.twig #}
{% import "forms.twig" as forms %}

{{ forms.input('username') }}

Nested Macros

Macros can call other macros:

{% macro field(name, value = '', type = 'text', label = '') %}
    <div class="field">
        {% if label %}
            <label for="{{ name }}">{{ label }}</label>
        {% endif %}
        {{ input(name, value, type) }}
    </div>
{% endmacro %}

{% macro input(name, value = '', type = 'text') %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" />
{% endmacro %}

{{ field('username', user.username, 'text', 'Username') }}

Macro Variable Scope

Macros have their own variable scope, separate from the template that calls them:

{% set name = 'Global' %}

{% macro greet(name = 'Default') %}
    {# This only sees the 'name' parameter, not the global 'name' #}
    Hello, {{ name }}\!
{% endmacro %}

{{ greet() }}                 {# Output: Hello, Default\! #}
{{ greet('Local') }}          {# Output: Hello, Local\! #}
{{ name }}                    {# Output: Global #}

To access the template's context from within a macro, pass the special _context variable:

{% macro listItems(items, _context) %}
    {% for item in items %}
        <li>{{ item }} (from {{ _context.templateName }})</li>
    {% endfor %}
{% endmacro %}

{% set templateName = 'product-list' %}
{{ listItems(products, _context) }}

Self-Referencing with _self

You can reference macros from the same template using the _self variable:

{% macro input(name, value) %}<input name="{{ name }}" value="{{ value }}">{% endmacro %}
{% macro form(action) %}
    <form action="{{ action }}">
        {{ _self.input('username', '') }}
        <button type="submit">Submit</button>
    </form>
{% endmacro %}

{{ _self.form('/submit') }}

Organizing Macro Libraries

For larger applications, organize macros into component libraries:

{# components/forms.twig #}
{% macro input(name, value = '') %}...{% endmacro %}
{% macro textarea(name, value = '') %}...{% endmacro %}

{# components/layout.twig #}
{% macro card(title, content) %}...{% endmacro %}
{% macro panel(title, content) %}...{% endmacro %}

{# template.twig #}
{% import "components/forms.twig" as forms %}
{% import "components/layout.twig" as layout %}

{{ layout.card('Login', forms.input('username')) }}

Error Handling in Macros

Common errors to watch for:

  1. Undefined macros: Ensure macros are defined before calling them
  2. Parameter mismatches: Check parameter names and types
  3. Scope issues: Remember macros can't access parent scope without explicitly passing context

When debugging, enable debug mode to get detailed error messages:

engine.SetDebug(true)

Performance Considerations

According to benchmarks, imported macros perform 27% better than direct macro usage due to optimizations in the caching system:

Macro Usage Type Time (µs/op) Relative Performance
Direct 3.16 1.00x
Imported 2.30 0.73x (27% faster)
Nested 2.98 0.94x (6% faster)

For high-performance applications:

  • Prefer imported macros over direct macro usage
  • Group related macros in dedicated template files
  • Use the import cache efficiently by importing each macro file once
Clone this wiki locally