Skip to content

Macros

Macros in Experience Builder allow developers to define reusable blocks of code that can be invoked multiple times throughout a Liquid template, streamlining the management of complex logic and content. This documentation provides an in-depth exploration of macro creation, usage, and special considerations within Experience Builder. You'll find detailed examples in Liquid syntax, covering input and output handling, key implementation points, as well as edge cases and nuances that may arise during integration. This guide is tailored for experienced developers, offering advanced insights into maximizing the utility of macros for efficient, scalable template management.

Creating and Using Macros

Definition

A macro is defined using the {% macro %} tag, which serves as the foundation for creating reusable blocks of code. The definition includes an identifier, which uniquely names the macro, followed by a body of statements that make up the logic or content of the macro. Additionally, you can define parameters within the macro, allowing you to pass values dynamically into the macro during invocation. These parameters enable the macro to adapt to different use cases, making it more versatile and efficient for a variety of template scenarios.

Syntax:

    {% macro identifier [parameter] %}
      <!-- body -->
    {% endmacro %}

Example of Macro Definition and Usage

Defining a Macro

Here, we define a macro called greet that takes one parameter, name.

Liquid Code:

    {% macro greet name %}
      Hello, {{ name }}!
    {% endmacro %}

Using the Macro

Once defined, the macro greet can be invoked using the {{ macro.identifier }} syntax.

Liquid Code:

    {{ macro.greet("John") }}
Output:

Hello, John!

Chaining and Special Parameters

Macros can accept multiple parameters, providing flexibility in how they are used across different parts of a template. By allowing multiple parameters, macros can be tailored to handle a range of values, making them more dynamic and adaptable. Additionally, macros support the chaining of multiple calls within a template, enabling more complex logic and streamlined code. This chaining capability enhances the modularity and reusability of your code, allowing developers to construct sophisticated templates with minimal redundancy.

Detailed Example

Here, we define a macro full_name which takes two parameters: first_name and last_name.

Liquid Code:

    {% macro full_name first_name last_name %}
      Full Name: {{ first_name }} {{ last_name }}
    {% endmacro %}
Use the full_name macro:
    {{ macro.full_name("John", "Doe") }}
Output:

Full Name: John Doe

Conditional Macros

You can incorporate conditionals within macros to handle a variety of scenarios, allowing the macro to behave differently depending on the input or context. This flexibility ensures that your macros are not only reusable but also adaptable to different situations, enabling you to tailor the output based on specific conditions. By using conditionals, you can control the flow of logic within the macro, ensuring that the right code is executed in the appropriate context, which enhances the versatility and efficiency of your templates.

Liquid Code:

    {% macro conditionalGreet name %}
      {% if name %}
        Hello, {{ name }}!
      {% else %}
        Hello, Guest!
      {% endif %}
    {% endmacro %}
Use the conditionalGreet macro:

{{ macro.conditionalGreet("Alice") }}

Output:

    Hello, Alice!
Calling it without a parameter:
    {{ macro.conditionalGreet(nil) }}
Output:
    Hello, Guest!

Key Points

  1. Scope: Variables defined within a macro have local scope, meaning they are confined to the macro itself and do not impact the outer template or other parts of the code. This ensures that the macro operates independently, allowing you to use the same variable names in different macros or sections of the template without risk of conflicts. This encapsulation enhances modularity and promotes cleaner, more maintainable code by preventing unintended side effects outside the macro.
  2. Reusability: Macros enhance the reusability of code blocks by allowing developers to define a set of instructions that can be invoked multiple times within a template. This reduces code repetition, promoting efficiency and maintainability. By centralizing logic in macros, you can easily update or modify functionality in one place, ensuring consistency across the template and reducing the risk of errors. Additionally, macros help organize complex templates, making the codebase cleaner and more modular, which ultimately improves readability and long-term scalability.

Special Cases

  1. Nested Macros: If one macro calls another, it’s essential to ensure that parameter names are unique across the macros to prevent potential collisions. Naming conflicts can arise when multiple macros share the same parameter name, which could lead to unexpected behavior or incorrect data being passed. To maintain clarity and avoid errors, it’s recommended to adopt a clear and distinct naming convention for each macro’s parameters. This practice helps ensure that each macro operates independently and as intended, even when they are nested or called in sequence, improving the overall stability and reliability of your code.
  2. Errors: If a macro is invoked with incorrect parameters, it can result in rendering errors or unpredictable behavior within the template. Passing the wrong type, number, or format of parameters may cause the macro to malfunction, leading to incomplete or incorrect output. It's crucial to validate that the parameters match the expected structure and data types defined in the macro’s signature. Ensuring correct parameter usage helps maintain the integrity of the macro’s functionality and prevents errors from propagating throughout the template.

Lazy Evaluation

Macros in Experience Builder are evaluated lazily, meaning they are defined once but only executed when invoked. This behavior provides flexibility, as the macro's logic is not processed until it is explicitly called within the template. This approach allows for dynamic content rendering based on runtime conditions, making it possible to adjust the output depending on factors such as user input, system state, or other environmental variables at the time of invocation. Lazy evaluation optimizes performance by ensuring that macros are only executed when needed, minimizing unnecessary processing.

Example of Lazy Evaluation:

    {% macro lazyGreet time_of_day %}
      {% if time_of_day == "morning" %}
        Good Morning!
      {% elsif time_of_day == "evening" %}
        Good Evening!
      {% else %}
        Hello!
      {% endif %}
    {% endmacro %}

    {% assign time_of_day_var = "morning" %}
    {{ macro.lazyGreet(time_of_day_var) }}
Output:
    Good Morning!