Skip to main content
Matik Help Center home page Help Center
Matik Blog Case Studies
  1. Matik
  2. Matik Mail
  3. Matik Mail Overview

Adding Loops, Conditionals, and other Logic to Email Templates (Code Editor)

Using logic in code editor templates

Matik Mail's code editor lets you go beyond static layouts. You can show or hide sections of an email based on data, change content depending on a value, or loop over a section to repeat it multiple times. This is powered by a templating language called Jinja, which Matik runs behind the scenes when an email is generated.

This functionality is only available in the code editor, not the visual builder.

Note: Logic only works with Text-type dynamic content that returns a single value, like a number, status, or name. It does not work directly with Table-type dynamic content that returns multiple rows, like a SQL query with multiple results. See Limitations below.

Referencing dynamic content in logic

Normally, to display a dynamic content value directly, you use standard double curly brace syntax, like {{Account_Name}}. That inserts the account name value directly into the email.

However, you can also reference dynamic content to power logic. The rules are slightly different depending on whether you're displaying the value or using it inside a logic block.

To use a value in logic, write the DC name without curly braces, inside a {% %} block

Logic blocks (if-statements, loops, and so on) wrap their contents in {% %} instead of {{ }}. Inside these blocks, reference dynamic content by its name only — no curly braces. For example, to use {{Account_Tier}} to power logic:

{% if Account_Tier == 'Enterprise' %}
<p>Welcome, Enterprise customer!</p>
{% endif %}

Write the DC name exactly as it appears in your dynamic content list, matching capitalization.

For DCs with subcontent, reference the full Parent.subcontent path

If your dynamic content uses subcontent (for example, a DC named Objectives with subcontent fields Objective_1, Objective_2), reference the full dotted path in both display and logic contexts:

<!-- Display -->
{{Objectives.Objective_1}}

<!-- Logic -->
{% if Objectives.Objective_1 == 'Complete' %}
<p>You completed your first objective!</p>
{% endif %}

For numeric comparisons, add |int or |float

DC values are stored as text by default. To compare them as numbers, add |int for whole numbers or |float for decimals:

{% if Revenue|int > 1000000 %}
<p>Strong quarter!</p>
{% endif %}

Without the |int or |float filter, Matik compares the value as text. For example, the text "500000" sorts after the text "1000000" alphabetically, so a text comparison can give the wrong answer for numeric thresholds.

Conditional sections

Conditional sections appear or disappear based on the value of dynamic content. Build them with {% if %}, {% elif %}, and {% else %}.

Show or hide a section based on a numeric threshold

Use {% if %} with |int or |float to show different content based on whether a value crosses a threshold. For example, to show a section if {{Revenue}} exceeds a certain threshold:

{% if Revenue|int > 1000000 %}
<div>Strong quarter — revenue exceeded $1M.</div>
{% else %}
<div>Below target — let's review next steps together.</div>
{% endif %}

Show different content based on a DC value

Use == to compare a string DC to a specific value. Wrap the comparison value in single quotes. For example, to show different content depending on the value of {{Account_Tier}}:

{% if Account_Tier == 'Enterprise' %}
<p>Your dedicated CSM will reach out next week.</p>
{% elif Account_Tier == 'Pro' %}
<p>Check out our office hours for tailored guidance.</p>
{% else %}
<p>Visit our knowledge base for resources.</p>
{% endif %}

Show fallback content when a DC is empty

An {% if %} on a DC name alone checks whether the DC has any value. Use this to show fallback content when data hasn't been populated yet:

{% if Last_Login_Date %}
<p>You last logged in on {{Last_Login_Date}}.</p>
{% else %}
<p>Log in to your account to get started.</p>
{% endif %}

Apply conditional styling

Combine {% if %} with inline CSS to change how a value looks based on its content. For example, color a an {{NPS_Score}} metric green if it's above a target and red if it's below:

<span style="color: {% if NPS_Score|int >= 50 %}#2E7D32{% else %}#C62828{% endif %}; font-weight: bold;">
{{NPS_Score}}
</span>

Looping over content

Loops let you repeat a section of HTML multiple times in a single email. Build them with {% for %} and {% endfor %}.

Loop a fixed number of times based on a count

Use range() with a scalar DC to repeat content a specific number of times. The number of loops is taken from the DC value:

{% for i in range(Number_of_Objectives|int) %}
<div class="objective">Objective placeholder</div>
{% endfor %}

If {{Number_of_Objectives}} equals 3, the email renders three identical objective divs.

Tip: The i in {% for i in range(...) %} is a counter variable. You don't need to use i anywhere — it's a placeholder. The body of the loop runs once per loop iteration regardless.

Loop with different content per iteration

You can also set each loop iteration to show different content — for example, the name of a different child account each time. Use the built-in variable loop.index to control which slot's content shows on each iteration. In this example, each loop returns data for a different child account:

{% for i in range(Number_of_Children|int) %}
  {% if loop.index == 1 %}
    <div>Account: {{Child_Account_1}}</div>
  {% endif %}
  {% if loop.index == 2 %}
    <div>Account: {{Child_Account_2}}</div>
  {% endif %}
  {% if loop.index == 3 %}
    <div>Account: {{Child_Account_3}}</div>
  {% endif %}
{% endfor %}

loop.index is a built-in variable that holds the current iteration number, starting at 1. The {% if loop.index == N %} blocks gate which slot's content renders on each pass.

This pattern requires you to define a fixed maximum number of slots ahead of time — one DC per slot. If you need to show data for up to 10 child accounts, set up 10 DCs and 10 {% if loop.index == N %} blocks. Looping over a variable-length list of unknown size is not supported (see Limitations).

Use a single DC's subcontent fields as slots

You can also use subcontent to use a single DC to populate different data in each iteration of a loop. If you are able to return all the content you need in a single query with multiple columns, this is usually cleaner than adding multiple dynamic content items.

Reference each subcontent field with the Parent.field syntax in both the loop body and any conditional checks:

{% for i in range(Number_of_Children|int) %}
  {% if loop.index == 1 %}
    <div>Account: {{Children.child_1}}</div>
  {% endif %}
  {% if loop.index == 2 %}
    <div>Account: {{Children.child_2}}</div>
  {% endif %}
  {% if loop.index == 3 %}
    <div>Account: {{Children.child_3}}</div>
  {% endif %}
{% endfor %}

Here, Children is a single DC and child_1, child_2, child_3 are subcontent fields on it. You only manage one DC instead of three.

When to use which slot pattern: Use individual DCs per slot when each slot's data comes from a different query, source, or shape. Use subcontent fields as slots when all slot values come from the same row of the same data source.

Skip empty slots when fewer items are returned

If your data sometimes returns fewer items than the maximum number of slots you've defined — for example, you set up 10 child account slots but a particular customer only has 5 children — you don't want empty cards rendering for the missing slots. Combine the loop.index check with a truthiness check on the slot's DC value using and. 

Example with individual DCs per slot:

{% for i in range(10) %}
  {% if loop.index == 1 and Child_Account_1 %}
    <div>Account: {{Child_Account_1}}</div>
  {% endif %}
  {% if loop.index == 2 and Child_Account_2 %}
    <div>Account: {{Child_Account_2}}</div>
  {% endif %}
  <!-- ...continue up to slot 10 -->
{% endfor %}

Example with subcontent fields:

{% for i in range(10) %}
  {% if loop.index == 1 and Children.child_1 %}
    <div>Account: {{Children.child_1}}</div>
  {% endif %}
  {% if loop.index == 2 and Children.child_2 %}
    <div>Account: {{Children.child_2}}</div>
  {% endif %}
  <!-- ...continue up to slot 10 -->
{% endfor %}

In either version, if a given slot's value is empty for a recipient, that iteration's {% if %} evaluates to false and nothing renders. The email shows only the slots that have data.

Best practices

  • Always cast to a number when comparing numerically: Use {% if Revenue|int > 1000000 %}, not {% if Revenue > 1000000 %}. Without the filter, Matik compares the value as text, which can give wrong results.
  • Wrap string comparisons in single quotes: Use {% if Status == 'Active' %}, not {% if Status == Active %}. Without quotes, the unquoted word is treated as another variable name and the comparison silently fails.
  • Test by sending a test email after each change: Logic errors do not surface in the visual preview. Always run a test send to confirm the rendered output matches what you expect.
  • Move complex logic upstream when you can: If you find yourself writing more than three or four nested {% if %} blocks, consider building the logic into your data source (SQL, Salesforce formula, etc.) and exposing the final result as a single DC. The template stays readable and the logic is easier to maintain.
  • Use the slots pattern only when the maximum count is known: Slots work well for "up to 5 child accounts" or "top 3 objectives." If the count is unbounded or large, pre-format the HTML in your data source and inject it as a single DC instead.
  • Use the truthiness check inside slots to handle variable-length data: If your data returns fewer items than your max slot count, add and SlotDC to each {% if loop.index == N %} block so empty slots are skipped instead of rendering blank cards.

Limitations

  • Tabular dynamic content cannot be looped over directly: If your DC returns rows from a SQL query, Salesforce report, or similar source, you cannot use {% for row in MyTable %} to iterate over the rows. Tabular DCs render as a single HTML table in the email, not as an iterable list. To show one section per row, either use the slots pattern with multiple scalar DCs, or pre-format the HTML in your data source.
  • Loop variables cannot be displayed with {{ }}: Inside a loop, references like {{i}}, {{loop.index}}, or {{child}} render as empty. Only registered dynamic content names work inside {{ }}. To show iteration-specific content, use {% if loop.index == N %} blocks to gate which DC value renders per iteration.
  • Logic is only available in the code editor: The visual editor does not support {% if %} or {% for %} blocks. Switch your template to code editor mode to use them.

Was this article helpful?

Have more questions? Submit a request

Articles in this section

  • Adding Loops, Conditionals, and other Logic to Email Templates (Code Editor)
  • Managing Unsubscribes in Matik Mail
  • Getting Started with Matik Mail
  • Creating Matik Mail Templates
  • Generating Emails with Matik Mail
  • Managing Content Generation in Flows
  • Creating Sender, CC: & BCC: Dynamic Content
  • Dynamic Charts and Tables in Matik Mail
  • CSV Bulk Generation For Matik Mail

Comments

0 comments

Article is closed for comments.

Personalize data-driven content in minutes

Product

  • How it Works
  • Integrations
  • AI Features
  • Security

Solutions

  • Sales
  • Customer Success
  • Ops & Strategy
  • Data

Resources

  • Blog
  • Templates
  • AI + CS Resource Hub
  • Case Studies
  • Help Center

Company

  • About Us
  • Careers
  • Terms of Service
  • Privacy Policy

© 2024 Matik, Inc.