We use cookies to make your viewing experience better. By accepting you consent, you agree to our Cookie policy

Accept
Improve your Craft CMS skills

Using Macros With Craft CMS

10 min read
Andy Golpys Made By Shape Founder

Craft CMS's powerful Twig templating engine enables developers to create reusable macros for streamlining projects. But many struggle to implement macros effectively. This guide provides actionable insights on real-world macro techniques - from basic snippets to advanced logic. Learn how macros can simplify templates, reduce errors, enforce standards, and optimize performance.

Macros in Craft CMS templates allow encapsulating reusable code snippets like navigation menus, image galleries, web forms, and more. When used effectively, macros can reduce duplicated code, enforce standards, simplify templates, improve maintainability, and optimize performance. Proper syntax, structure, caching, and best practices help avoid common errors and pitfalls when implementing macros.

Creating Basic Macros in Craft

Macro Syntax and Structure

Defining macros in Craft CMS requires using specific Twig syntax and structure. Macros are defined using the {% macro %} and {% endmacro %} tags. Between these tags goes the macro name and any parameters. For example:

{% macro hello(name) %}

Hello {{ name }}!

{% endmacro %}

The macro name should be lowercase, with underscores for spaces. Parameters are defined similarly to functions, with variable names inside parentheses.

When calling a macro later in a template, the name and parameters must match the original definition. Macro names are case-sensitive in Twig. Following naming conventions will help avoid errors.

Calling and Using Macros

To call a macro in a Craft CMS template, use {{ macro_name() }} syntax. Pass any required parameters inside the parentheses. For example:

{{ hello('John') }}


Would output:

Hello John!


Macro calls can be assigned to variables as well:

{% set greeting = hello('Mary') %}

{{ greeting }}


The variable will contain the macro output. When calling a macro, ensure the name and parameters match the original definition to avoid errors.

Basic Use Cases and Examples

Macros are useful for repeatable template code like copyright notices, contact info, social media links, etc. Some examples of basic macros:

Copyright notice

{% macro copyright() %}

© {{ "now"|date("Y") }} My Company Ltd.

{% endmacro %}


Phone number

{% macro phone() %}

555-123-4567

{% endmacro %}


Facebook link

{% macro facebook() %}

<a href="">Facebook</a>

{% endmacro %}


These can then be called anywhere in a template:

{{ copyright() }}


Call us at {{ phone() }}


Follow us on {{ facebook() }}


Macros are invaluable for keeping reusable snippets DRY (Don't Repeat Yourself). They promote consistency and maintainability in Craft CMS templates.

Intermediate Macros in Craft

Looping Macros

Looping macros allow iterating over arrays, datasets, and Craft entries to generate repetitive output. For example:

{% macro articleList(entries) %}

{% for entry in entries %}

<h2>{{ entry.title }}</h2>

{{ entry.summary }}

{% endfor %}

{% endmacro %}


This macro accepts an array of entries, loops through them, and outputs the title and summary for each.

When looping in macros, consider pagination to prevent huge datasets. Cache the results to improve performance. For example:

{% macro paginatedEntries(entries, page, limit) %}


{% set total = entries|length %}

{% set start = (page - 1) * limit %}

{% set end = start + limit %}

{% set paginatedEntries = entries|slice(start, end) %}

{# Macro output using paginatedEntries #}


{% endmacro %}


This paginates the dataset before looping over it. The slice filter grabs just the subset we need for the given page.

Conditional Macros

Conditionals allow controlling macro logic and output. For example:

{% macro greeting(name) %}


{% if name %}

Hello {{ name }}!

{% else %}

Hello stranger!

{% endif %}


{% endmacro %}


This macro checks if a name was passed, and outputs a personalized or generic greeting accordingly. The ternary operator is handy for simple conditionals:

{% set message = name ? 'Hi ' ~ name : 'Hello!' %}

Conditionals are useful for toggling output based on user input, environment, etc.

Macro Parameters

Parameters allow passing dynamic data into macros:

{% macro article(title, summary, body) %}


<h1>{{ title }}</h1>

<p>{{ summary }}</p>

{{ body }}


{% endmacro %}


Parameters can have default values, be optional, or use validation methods. For example:

{% macro sayHello(name = 'friend') %}

Hello {{ name }}!

{% endmacro %}


Sets a default value. Optional parameters can improve reusability:

{% macro fullAddress(street, city, region = '', zip) %}


{{ street }}

{{ city }}, {{ region }} {{ zip }}


{% endmacro %}


Region is optional. Validation ensures correct data types:

{% macro calculateTotal(prices) %}


{% if prices is not iterable %}

{% throw "Prices must be iterable!" %}

{% endif %}

{# Calculate total #}


{% endmacro %}


Parameters give macros dynamic capabilities. They should be used judiciously to preserve reusability.

Debugging Macros in Craft CMS

Logging and Debugging

When a macro isn't outputting as expected, logging and debugging statements can help uncover issues.

The {{ log }} tag allows logging variables, messages, and more:

{% macro hello(name) %}


{{ log(name) }}

Hello {{ name }}!


{% endmacro %}


This logs the name parameter. Twig's debug tag shows values during rendering:

{{ dump(myVar) }}

Logging and debugging provide visibility into macro execution. Use judiciously in production to avoid performance impacts.

Testing and Profiling

PHPStorm combined with Xdebug allows setting breakpoints and stepping through Craft CMS macro code.

The Retcon plugin is invaluable for profiling macro performance. For example, wrapping a macro call in Retcon tags:

{% retcon "Profile Search" %}


{{ searchResults(query) }}


{% endretcon %}


Provides detailed performance data on the macro execution. Retcon reveals slow queries, memory usage, and more.


Profiling macros periodically helps optimize performance and avoid bottlenecks as functionality is added.

Common Errors and Fixes

Syntax errors like missing {% endmacro %} tags, incorrect parameter names, and invalid Twig can cause cryptic exceptions in macros.

Undefined variables are another common issue - ensure all variables are passed in or defined before use.

Side effects from modifying parameters or global state can lead to unexpected behavior. Avoid where possible.

Naming conflicts can occur if macros are defined multiple times. Prefix custom macros with a namespace to avoid collisions.

Testing macros thoroughly and checking logic on updates will help surface issues early and avoid bugs down the road.

Craft CMS Macro Best Practices

Performance Considerations

Well-structured macros avoid performance pitfalls like excessive processing or memory usage.

Favor immutability - don't modify inputs or global state. Return new data instead:

{% macro transform(array) %}


{% set newArray = [] %}


{% for item in array %}

{% set newItem = process(item) %}

{% set newArray = newArray|merge([newItem]) %}

{% endfor %}


{{ newArray }}


{% endmacro %}


This leaves the original array unchanged.


Cache expensive macro output or queries. For example:

{% cache %}


{% set results = search(params) %}

{{ results }}


{% endcache %}


Avoiding side effects is critical for optimized, scalable macros.

Code Structure Practices

Well-structured macro code is readable and maintainable. Limit complexity - small, focused macros are ideal.

Use descriptive names like entry_summary rather than es. Detailed docblock comments explain usage.

Group related macros into files or namespaces like seo_macros.twig.

Adhering to these practices will keep macro code clean as it evolves.

Reusability Considerations

Craft's flexibility allows macros tailored for specific use cases. But aim for reusability when possible.

Avoid hardcoded values - use parameters instead:

{% macro greeting(name) %}


<p>Hello {{ name }}!</p>


{% endmacro %}


This reusable greeting macro accepts the name as a parameter.

Loose coupling avoids dependencies on global state or external systems.

General purpose macros can serve a wide range of use cases. But targeted macros have benefits too - strike a pragmatic balance.

Craft CMS Macro Examples

Navigation Menus

Macros are perfect for outputting navigation menus in Craft CMS templates. For example:

{% macro renderMenu(menuHandle) %}


{% set menuItems = craft.menu.getMenuItems(menuHandle) %}


<ul>

{% for item in menuItems %}

<li>

<a href="{{ item.url }}">{{ item.title }}</a>


{% if item.children %}

{{ _self.renderMenu(item.children) }}

{% endif %}

</li>

{% endfor %}

</ul>


{% endmacro %}


This recursively loops through the menu items to render nested lists. It accepts the menu handle and handles the output.

To add caching:

{% macro renderMenu(menuHandle) %}


{% cache %}


{% set menuItems = craft.menu.getMenuItems(menuHandle) %}


{# Output menu #}


{% endcache %}


{% endmacro %}


Image Galleries

Displaying image galleries from Craft assets is easy with macros. For example:

{% macro imageGallery(assetSource, limit=10) %}


{% set assets = assetSource.limit(limit) %}


<div class="gallery">


{% for asset in assets %}

<img src="{{ asset.url }}" alt="{{ asset.title }}">

{% endfor %}


</div>


{% endmacro %}


This loops through the given assets outputting image tags. Add pagination:

{% macro paginatedGallery(assetSource, page, limit) %}


{% set total = assetSource|length %}


{% set start = (page - 1) * limit %}

{% set end = start + limit %}


{% set assets = assetSource|slice(start, end) %}


{# Output gallery #}


{% endmacro %}


Web Forms

Form rendering and processing can be encapsulated in a macro:

{% macro contactForm() %}


<form method="post" action="">


{# Form fields #}


</form>


{% if formSubmitted %}


{# Form processing logic #}


{% endif %}


{% endmacro %}


This handles the form output and submission/processing flow. It can then be reused across the site by calling {{ contactForm() }}.


The macro handles the repetitive code leaving templates clean.

Shape April 2022 HR 202
Andy Golpys
- Author

Andy has scaled multiple businesses and is a big believer in Craft CMS as a tool that benefits both Designer, Developer and Client. 

Share
Feedback
Show us some love
Email Us
We usually reply within 72 hours
Agency Directory
Submit your agency
Affiliate Partners
Let's chat