SendinBlue’s New Template Language to create email templates and campaigns [NEW]

Our template language includes predefined placeholders and logic that may be used to insert dynamic content into your email, and modify how this content is displayed to the recipient.

The template language provides a standardized, common structure for creating efficient templates and powerful design options.

Good to know: The New Template Language replaces an older version of the SendinBlue Template Language which used different syntax and was less robust. In this guide, we’ll simply refer to the New Template Language as “the template language”.

Before getting started

When creating a single email to send to many recipients at once, create your email as a Campaign.

When creating a template to send as a Transactional email or from an Automation workflow, build and save it as a Template.

Emails using the template language can be triggered to send via their respective SendinBlue API v3 calls (for campaigns) or (for transactional emails) while providing the campaign ID or template ID. They may also be sent via the SMTP relay while providing the header X-SIB-API with the correct parameters.

Good to know: If you have not used our Transactional (SMTP) platform before, you may need to request its activation. To check, log in to your account and visit this page. If you do not see a prompt asking you to activate your SMTP account and your quota is higher than 0, you’re ready to start sending.

Overview

The template language makes use of variables and filters which get replaced with specific values when the email is triggered to be sent, as well as tags which add logic to control how and if the content is displayed.

There are two kinds of delimiters available:

{{ ... }} - “prints” or outputs the result of a variable or filter into the email
{% ... %} - executes the logic of a tag, such as a for-loops.

Good to know: The template language may be applied to modify several aspects of your email including the subject line, preheader, within HTML <body> tags, and the recipient “to” field. HTML is automatically escaped by default inside templates, but this behavior may be changed using the autoescape tag described in detail below.

 

Variables

There are three types of variables available and each is described below.

Good to know: When using the "Send a test" feature to send a test version of your email template, variables will not be replaced with recipient-specific content in the test email. Instead, you will see the variables exactly as they are coded in your template. 

1. Predefined variables  

These are available for all SendinBlue users:

{{ mirror }}

inserts a link to view your email as a web page

This variable is not mandatory.

Typically placed at the top of an email and formatted as a link like this:

<a href={{ mirror }}>Click here to view this message in your browser.</a>

{{ unsubscribe }}

inserts a link to unsubscribe from your emails

Any non-transactional emails sent via SendinBlue should include an unsubscribe link.

Typically placed in the footer and formatted as a link like this:

<a href={{ unsubscribe }}>Click here to unsubscribe.</a>

 

2. Contact attributes   

This variable type contains two elements and is structured as:

{{ contact.attributeNAME }}

Attribute = your SendinBlue contact attribute name as it appears on your Contacts page in SendinBlue. Attributes are always in CAPS and never contain spaces.

e.g., to insert a contact’s phone number where the attribute is named PHONE, use: {{ contact.PHONE }}

If the contact attribute name contains a dash -, such as CELL-PHONE, format your variable like this instead: {{ contact|key:"ATTRIBUTE" }}.  

e.g. {{ contact|key:"CELL-PHONE" }}

3. Transactional parameters

Transactional parameter variables also contain two elements and are structured as:

{{ params.parameterNAME }}

parameterNAME = your transactional parameter name, formatted and cased exactly as you pass it via the API.

e.g., to insert a list of products where the parameter is named PRODUCTS, use: {{ params.PRODUCTS }}.

Now, let’s consider a common use case: generating a list of products purchased by a customer. Since the number of products purchased varies per customer, we can combine a for-loop with your PRODUCTS transactional parameter, like this example:  

<ul> 
{% for product in params.products %}

   <li><strong>{{ product.NAME }}</strong> - {{ product.PRICE }}</li>
{% endfor %}
</ul>

This would output an unordered, bulleted list of products including the product name and price, like this and each recipient’s list may include a unique number of products:  

  • Product 1 - $10
  • Product 2 - $10
  • Product 3 - $10

Good to know: We recommend avoiding the use of hyphens or any special characters in your variable/parameter names. If you have no other choice, you can use {{ params|key:”parameter-NAME” }}. You may nest the filter multiple times, such as in {{ params|key:”my-product”|key:”my-product-name” }}.

Arrays

When using an array, you may access all individual elements. Specify their ID like this: params.parameterARRAY.ID.parameterNAME. For example: {{ params.PRODUCTS.1.NAME }}.

 

Tags  

The most common tags available to use in templates are iffor and autoescape. Guidelines and examples for each are included below. 

if

The powerful logic offered by if enables you to add or remove entire content blocks within your template (or modify content within a block). This effectively extends the usefulness of a single template for multiple scenarios.

The tag {% if %} enables you to test if an expression is true (or if an array is empty), and modify the email content based on the result. You can also check for false values, multiple conditions, and multiple branches.

Here are several common ways to use if:

if

Checks if a value is true or if an array is not empty

{% if contact.ACTIVE %}
Congrats! You achieved your goal this month.
{% endif %}
{% endif %}

---

{% if params.tutors %}

{% for tutor in params.tutors %}
The following tutors are available to help you:
<ol>
<li>{{ tutor.name }}</li>
{% endfor %}
</ol>
{% endif %}

==

check if an expression is true

{% if coupon == “WELCOME” %}
<p>Welcome to our list! Here’s your first coupon: WELCOME25</p>
{% endif %} 

---

{% if contact.DONOR == true %}
<b>Thank you for your support!</b>
{% endif %}

if, in

check if a value (substring) exists within a string or if a variable exists within array

{% if “@example.com" in "bob@example.com" %}
This appears since "@example.com" is a substring of "bob@example.com"
{% endif %}

 

{% if "Piano" in params.types %}
Be careful! There are heavy items in your order. You must be present to receive this delivery.
{% endif %}

not

check for values that are false

{% if not user.subscribed %}
   <p>You are not subscribed to our secret sale alerts. Sign up here.</p>
{% endif %}

and / or

evaluate multiple conditions

{% if temperature > 10 and temperature < 55 %}
   <p>Brr. It’s cold! Here’s a coupon for 20% off of any hot beverage, today only.</p>
{% endif %}

 

{% if contact.LANG == “FR” and contact.COUNTRY == “Canada” %}
{% endif %}

 

{% if contact.COUNTRY == “United States” or contact.COUNTRY == “Canada” %}
{% endif %}

elif, else

evaluate multiple branches

{% if event.paid %}
   Your spot is reserved. Thanks for submitting your payment.
{% elif event.registered %}
   Your spot is reserved but will be released without payment by January 2.
{% else %}
   There’s still time to sign up! Click here.
{% endif %}

 

Good to know: We do not recommend applying if tag comparisons to variables that contain “float” values as they may not produce accurate results. (Float values are those that contain a fractional number instead of an integer/whole number.) However, these may be applied if the value is passed as a string (contained in quotation marks “like this”).

 

for

As we’ll explore later in this guide, for is quite powerful when combined with your transactional parameters. It enables you to easily do complex work, such as inserting dynamic lists of products.

The tag {% for %} for enables you to loop, or iterate, over each item in a sequence. This is particularly helpful when the number of items in the sequence is unknown when creating a template.

Here are several common ways to use for:

for, in

display a list

<ul>
   {% for user in params.USERS %}
       <li>{{ user.username }}</li>
   {% endfor %}
</ul>

else

render a replacement block if your sequence is empty

<ul>
   {% for user in params.USERS %}
       <li>{{ user.username }}</li>
   {% else %}
       <li><em>no user found</em></li>
   {% endfor %}
</ul>

Would display “no user found”.

reversed

reverses the print order of items in your sequence

{% for country in params.countries reversed %} {% end for %}

Would display a list of countries, but in reverse order compared to the original list.

 

Loop variables

Inside of a for -loop block you can also access these special variables:

forloop.Counter

Always set to an integer representing the number of times the loop has been entered. This is one-indexed, so the first time through the loop, forloop.Counter will be set to 1.

This may be easily used to number items in a loop:

{% for product in params.products %}
{{ forloop.Counter }}. {{ product.name }} {% endfor %}

will output a list like this:

1. Product a
2. Product b
3. Product c, etc.

forloop.Counter0

The current iteration of the loop. (0 indexed)

forloop.Revcounter

Always set to an integer representing the number of remaining items in the loop. The first time through the loop, forloop.Revcounter will be set to the total number of items in the sequence you’re traversing. The last time through the loop, forloop.revcounter will be set to 1.

forloop.Revcounter0

The number of iterations from the end of the loop (0 indexed)

forloop.First

Boolean value set to True if this is the first time through the loop. This is convenient for special-casing the first item in a list.

forloop.Last

Boolean value set to True if this is the last time through the loop. A common use for this is to put separating characters (like , or |) in a list of items.

forloop.Parentloop

References the forloop object for the parent loop, in case of nested loops.

Here’s an example:

{% for country in countries %}
<table>
{% for city in country.city_list %}
<tr>
<td>Country #{{ forloop.Parentloop.counter }}</td>
<td>City #{{ forloop.Counter }}</td>
<td>{{ city }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}

 

Good to know: Loop variable names are case sensitive.

e.g. to assign a numeric value (counting from 1) to each item printed from your iteration, use {{ forloop.Counter }}:

{% for user in users %}
   {{ forloop.Counter }} - {{ user.username }}
{% endfor %}

 

autoescape

Good to know: By default SendinBlue escapes the HTML (and JS) content of all variables. This means any HTML passed within a variable will be escaped and printed as text. For example, if your variable content is <h1>My title</h1>, then your template would output exactly this text within your email: <h1>My title</h1> rather than outputting the phrase “My title” formatted as an H1 title.

The autoescape tag controls the current auto-escaping behavior. Since SendinBlue templates autoescape HTML by default, you will typically need to apply this tag to disable auto-escaping within a particular block. The block should be closed with an endautoescape ending tag.

We’ll review a common example when disabling autoescape may be useful. Let’s say that you pass products in a variable, but each product requires unique formatting.

If autoescape is used like this:

{% autoescape off %}{{ params.my_html }}{% endautoescape %}

Then, auto-escaping will be disabled for the variable {{ params.my_html }}. Now, if your variable content is <h1>My title</h1>, it will display as an H1 title. 

The only exceptions are variables that are already marked as safe from escaping, either by the code that populated the variable, or because it has had the safe filter applied.

verbatim

The verbatim tag allows you to use double braces {{ like this }} in your email without them being recognized as a template language element. To escape {{ }} and print these symbols directly in your email, wrap them in this tag:

{% verbatim %}
{{ Print variable }}
{% endverbatim %}

This text will appear fully in your email as:

{{ Print variable }}

Filters

Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (|) and may have optional arguments in parentheses. Multiple filters can be chained. The output of one filter is applied to the next.

Most commonly used filters

Modifying and formatting word case

capfirst

capitalizes a value

The first character will be uppercase, all others lowercase.

{{ contact.NAME|capfirst }}

john doe will output John doe.

title

returns a titlecased version of the value

Words will start with uppercase letters, all remaining characters are lowercase.

{{ contact.NAME|title }}

john doe will output John Doe.

upper

converts a value to uppercase

{{ contact.NAME|upper }}

john doe will output JOHN DOE.

lower

converts a value to lowercase

{{ contact.NAME|lower }}

JOHN DOE will output john doe.

trunchars

Truncates a string if it is longer than the specified number of characters. Truncated strings will end with a translatable ellipsis sequence (…).

{{ value|truncatechars:9 }}

If value = congratulations, it would output as congratul…

Formatting numbers

floatformat

Rounds the output of your contact attribute or parameter’s float values to the specified decimal place.
To function correctly, the passed values should be passed as a number, rather than as a string.
(Values should not be passed in quotes as “value”.)

If your template contains
{{ contact.BALANCE }} and the output value would be 40.320000, the float may be rounded to “n” decimal place by using:
{{ contact.BALANCE | floatformat: n}}

{{ contact.BALANCE | floatformat: 2}}
Will output as 40.32 instead of 40.320000

{{ contact.BALANCE | floatformat: 0}}
Will output as 40 instead of 40.320000

Formatting dates

time_parse

Converts your date format (passed as a string) to a standard date format that may be used with other filters

Pass your current date format as an argument: format the exact date/time of how Monday January 2 15:04:05 -0700 MST 2006 would be displayed if it were the value.

If your string is formatted using RFC3339 you may use the dedicated parser time_parse_rfc3339 without an argument.

{{ params.my_date|time_parse:"15:04 02/01/2006” }}

{ params.my_date|time_parse:"Monday 02 January 2006” }}

Note: Only English words for day and month are recognized.

date

Prints the date in the specified format
To define your own format, write down what the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would look like formatted your way.

Please note that you must pass a date to this filter.

You’ll get a date from a string using the time_parse filter.

{{ "14:01 01/06/2018"|time_parse:"15:04 02/01/2006"|date:"Mon Jan 2 15:04:05 2006"}}

Will output as:

Fri Jun 1 14:01:00 2018

Note: Only English words for day and month are recognized.

 

Please note:

  • Day and month abbreviations and full names are supported in English only
  • If a value is not passed, it’s considered to be 0
  • The default timezone (if not passed) is UTC
  • You may use the am/pm keywords
  • The most common ways to define the timezone are: the dedicated keywords (MST, CET, UTC, Asian/Kolkata etc.), or as numeric, +0100 or -0100, to UTC.

Other filters

first

returns the first "element" of a sequence, a mapping, or a string

{{ [1, 2, 3, 4]|first }}

Will output as 1

join

returns a string which is the concatenation of the items of a sequence

The separator between elements is an empty string by default. You may define it with the optional first parameter.

{{ [1, 2, 3]|join }}

Will output as 123  


{{ [1, 2, 3]|join('|') }}

Will output as 1|2|3

last

returns the last "element" of a sequence, a mapping, or a string

{{ [1, 2, 3, 4]|last }}

Will output as 4

length

returns the number of items of a sequence or mapping, or the length of a string

{% if users|length > 10 %}
   ...
{% endif %}

safe

marks a string as not requiring further HTML escaping prior to output

{{ params.htmltest | safe }}

"params" :{HtmlTest: "<p>This is my test sentence.</p>"}

Will output a paragraph formatted in the recipient’s mail client.

{{ params.html }}
"params" :{HtmlTest: "<p>html para</p>"}

Without the safe filter, this will display the HTML tag as normal string in the recipient’s mail client:

<p>This is my test sentence</p>

slice

extracts a slice of a sequence, a mapping, or a string

This filter may be configured to:

A. Loop through only the first ‘n’ number of elements in an array

B. Loop through elements between two specified positions (‘n’ and ‘m’) in the array

In both of these cases, remember that elements in an array are assigned a unique position number starting with ‘0’ as the first position (they are “zero-indexed”).

C. The filter may also be configured to loop through elements between two specified elements in an array.

In each example, consider that your array contains the following:

["a","b","c","d","e","f","g","h",...]

A.
{% for product in params.products|slice:’:5’ %}
{{ product.name - {{ product.price }}
{% endfor %}
Will loop over the first 5 positions (0-4) which are: ["a","b","c","d","e"}

B.
{% for product in params.products|slice:’2:3’ %}
{{ product.name - {{ product.price }}
{% endfor %}
Will loop over positions (2-3) which are: ["c","d"]

C.
{% for product in params.products|slice:’b:d’ %}
{{ product.name - {{ product.price }}
{% endfor %}
Will loop over the specified elements, ["b","c","d"]

 

Testing & Troubleshooting your template

When using the "Send a test" feature to send a test version of your email template, contact attribute variables will be replaced with recipient-specific content in the test email while transactional parameter variables will appear blank.

If you have any questions or concerns, please contact our customer care team.

Additional documentation resources

The template language used by SendinBlue is based on the Django Template Language.

This guide covers the most relevant and frequently used template language structure and elements. Below is an exhaustive list of all supported tags and filters. Reference this Django documentation to view more details.

All supported tags

autoescape, comment, cycle, filter, firstof, for, if, ifchanged, now, set, spaceless, templatetag, with

All supported filters

escape, safe, escapejs, add, addslashes, capfirst, center, cut, date, default, default_if_none, divisibleby, first, floatformat, get_digit, iriencode, join, last, length, length_is, linebreaks, linebreaksbr, linenumbers, ljust, lower, make_list, phone2numeric, pluralize, random, removetags, rjust, slice, stringformat, striptags, time, title, truncatechars, truncatechars_html, truncatewords, truncatewords_html, upper, urlencode, urlize, urlizetrunc, wordcount, wordwrap, yesno (Reference: https://djangobook.com/builtin-template-tags-filters/)

 

Please note: If you identify a desirable element that is not yet supported, please reach out to our customer care team at contact@sendinblue.com to request it.