-
Notifications
You must be signed in to change notification settings - Fork 0
Macros
Twig macros are a powerful way to create reusable templates and components. They work like functions that can output template content.
Macros are defined using the {% macro %}
tag:
{% macro input(name, value = '', type = 'text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" />
{% endmacro %}
Once defined, macros can be used directly in the same template:
{{ input('username', 'johndoe') }}
{{ input('password', '', 'password') }}
{{ input('submit', 'Login', 'submit') }}
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') }}
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') }}
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') }}
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) }}
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') }}
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')) }}
Common errors to watch for:
- Undefined macros: Ensure macros are defined before calling them
- Parameter mismatches: Check parameter names and types
- 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)
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