Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Using Handlebars


Twilio SendGrid Dynamic Transactional Templates and Marketing Campaigns designs support the Handlebars(link takes you to an external page) templating language to render the Personalizations you send via the API and the Substitution Tags stored on your Marketing Campaigns contacts.

Handlebars syntax allows you to personalize the messages in your templates by inserting customers' names and other data to make an email relevant to each individual recipient. For example, if you have a customer's name stored in a JSON property called "name", you can insert the property's value into a template by adding {{ name }} wherever you want the customer's name to appear.

Handlebars syntax allows all of this dynamic templating to occur outside of your code base, meaning changes are done quickly in the template with no update to a code base required.

(information)

Info

If you prefer to use your own templating system, you can still insert dynamic data using Substitution Tags.


Personalizing email with Handlebars

personalizing-email-with-handlebars page anchor
(information)

Info

You can manage your templates programmatically with our Mail Send with Dynamic Transactional Templates API.

The Handlebars language provides many features in addition to basic variable replacement, including iterations (loops) and conditional statements. Our templates and designs support most but not all of this Handlebars functionality. Currently, dynamic templates support the following helpers:

For a full helper reference, see the Handlebars reference on this page.


The following use case examples come from the dynamic-template section of our email templates GitHub repo(link takes you to an external page). Each example links to files you can explore on GitHub. You can also work with these templates by uploading them using the Code Editor available in Dynamic Transactional Templates(link takes you to an external page) and the Marketing Campaigns Design Library(link takes you to an external page).

The following use cases are listed with the Handlebars helpers used to build them.

Receipt

receipt page anchor

This example receipt template(link takes you to an external page) uses the following helpers:

This example transactional template(link takes you to an external page) uses the following helpers:

This is an example template that lets you have content in multiple languages(link takes you to an external page), and it uses the following helpers:

This example newsletter template(link takes you to an external page) uses the following helpers:


The following reference provides sample code blocks for each helper, including HTML email snippets and JSON test data. The code examples are shown in three tabs. The first tab, Handlebars, shows the Handlebars tag. The second tab, JSON, shows example data that would be used to populate the Handlebars tag. The third tab, HTML, shows the final output that the Handlebars tag will be rendered to in your email. You can click each tab to switch between the code samples.

Twilio SendGrid templates support the following substitutions:

Basic replacement

basic-replacement page anchor

_10
<!-- Template -->
_10
<p>Hello {{ firstName }}</p>


_10
// Test data
_10
{ "firstName": "Ben" }


_10
<!-- Resulting HTML !-->
_10
<p>Hello Ben</p>


_10
<!-- Template -->
_10
<p>Hello {{user.profile.firstName}}</p>


_10
// Test data
_10
{
_10
"user": {
_10
"profile": {
_10
"firstName": "Ben"
_10
}
_10
}
_10
}


_10
<!-- Resulting HTML -->
_10
<p>Hello Ben</p>


_10
<!-- Template -->
_10
<p>Hello {{user.profile.firstName}}</p>


_15
// Test data
_15
{
_15
"user": {
_15
"orderHistory": [
_15
{
_15
"date": "2/1/2018",
_15
"item": "shoes"
_15
},
_15
{
_15
"date": "1/4/2017",
_15
"item": "hat"
_15
}
_15
]
_15
}
_15
}


_10
<!-- Resulting HTML -->
_10
<p>Hello</p>

(information)

Info

If you include the characters ', " or & in a subject line replacement be sure to use three brackets like below.


_10
<!-- Template -->
_10
<!-- Per Handlebars' documentation: If you don't want Handlebars to escape a value, use the "triple-stash", {{{ -->
_10
<p>Hello {{{firstName}}}</p>


_10
// Test data
_10
{ "firstName": "<strong>Ben</strong>" }


_10
<!-- Resulting HTML -->
_10
<p>Hello <strong>Ben</strong></p>

The formatDate helper takes a time in either epoch or ISO8601 format and converts it to a format you specify using the tokens in the following table. If you send a date field without converting it, it will be displayed in ISO8601 format with the full timestamp (e.g., 2020-01-01T23:00:00.000Z). The following example display results are for Tuesday, January 1st, 2020 3:00:00PM Pacific Standard Time.

TokenDisplayed Result
YYYY2020
YY20
MMMMJanuary
MMMJan
MM01
M1
DD01
D1
ddddTuesday
dddTue
hh03
h3
HH00
H00
mm00
m0
ss00
s0
APM
ZZ-0800
Z-08:00

_10
<!-- Template without timezone offset -->
_10
<p>Join us {{formatDate timeStamp dateFormat}}</p>
_10
_10
<!-- Template with timezone offset -->
_10
<p>Join us {{formatDate timeStamp dateFormat timezoneOffset}}</p>


_10
// Test data
_10
{
_10
"timeStamp": "2020-01-01T23:00:00.000Z",
_10
"dateFormat": "MMMM DD, YYYY h:mm:ss A",
_10
"timezoneOffset": "-0800"
_10
}


_10
<!-- Resulting HTML without timezone-->
_10
<p>Join us January 01, 2020 11:00:00 PM</p>
_10
_10
<!-- Resulting HTML with timezone-->
_10
<p>Join us January 01, 2020 3:00:00 PM</p>


_10
<!-- Insert with a default value -->
_10
<p>Hello {{insert name "default=Customer"}}! Thank you for contacting us about {{insert businessName "your business"}}.</p>


_10
// Test data with all values
_10
{
_10
"name": "Ben",
_10
"businessName": "Twilio SendGrid"
_10
}
_10
_10
// Test data with missing value
_10
{
_10
"name": "Ben"
_10
}


_10
<!-- Resulting HTML with all values -->
_10
<p>Hello Ben! Thank you for contacting us about Twilio SendGrid.</p>
_10
_10
<!-- Resulting HTML with missing value and a default-->
_10
<p>Hello Ben! Thank you for contacting us about your business.</p>

Twilio SendGrid templates support the following conditionals:

Basic If, Else, Else If

basic-if-else-else-if page anchor

_10
<!-- Template -->
_10
\{{#if user.profile.male}}
_10
<p>Dear Sir</p>
_10
{{else if user.profile.female}}
_10
<p>Dear Madame</p>
_10
{{else}}
_10
<p>Dear Customer</p>
_10
{{/if}}


_26
// Test data one
_26
{
_26
"user":{
_26
"profile":{
_26
"male":true
_26
}
_26
}
_26
}
_26
_26
// Test data two
_26
{
_26
"user":{
_26
"profile":{
_26
"female":true
_26
}
_26
}
_26
}
_26
_26
// Test data three
_26
{
_26
"user":{
_26
"profile":{
_26
_26
}
_26
}
_26
}


_10
<!-- Resulting HTML from test data one -->
_10
<p>Dear Sir</p>
_10
_10
<!-- Resulting HTML from test data two -->
_10
<p>Dear Madame</p>
_10
_10
<!-- Resulting HTML from test data three -->
_10
<p>Dear Customer</p>


_10
<!-- Template -->
_10
\{{#if user.suspended}}
_10
<p>Warning! Your account is suspended, please call: {{@root.supportPhone}}</p>
_10
{{/if}}


_10
// Test data
_10
{
_10
"user": {
_10
"suspended": true
_10
},
_10
"supportPhone": "1-800-555-5555"
_10
}


_10
<!-- Resulting HTML -->
_10
<p>Warning! Your account is suspended, please call: 1-800-555-5555</p>


_10
<!-- Template -->
_10
\{{#unless user.active}}
_10
<p>Warning! Your account is suspended, please call: {{@root.supportPhone}}</p>
_10
{{/unless}}


_10
// Test data
_10
{
_10
"user": {
_10
"active": false
_10
},
_10
"supportPhone": "1-800-555-5555"
_10
}


_10
<!-- Resulting HTML -->
_10
<p>Warning! Your account is suspended, please call: 1800-555-5555</p>

Basic greaterThan
basic-greaterthan page anchor

_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#greaterThan scoreOne scoreTwo}}
_10
Congratulations, you have the high score today!
_10
{{/greaterThan}}
_10
Thanks for playing.
_10
</p>


_11
// Test data one
_11
{
_11
"scoreOne": 100,
_11
"scoreTwo": 78
_11
}
_11
_11
// Test data two
_11
{
_11
"scoreOne": 55,
_11
"scoreTwo": 78
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>
_10
Hello Ben! Congratulations, you have the high score today! Thanks for playing.
_10
</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>Hello Ben! Thanks for playing.</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#greaterThan scoreOne scoreTwo}}
_10
Congratulations, you have the high score today!
_10
{{else}}
_10
You were close, but you didn't get the high score today.
_10
{{/greaterThan}}
_10
Thanks for playing.
_10
</p>


_11
// Test data one
_11
{
_11
"scoreOne": 100,
_11
"scoreTwo": 78
_11
}
_11
_11
// Test data two
_11
{
_11
"scoreOne": 55,
_11
"scoreTwo": 78
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>
_10
Hello Ben! Congratulations, you have the high score today! Thanks for playing.
_10
</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>
_10
Hello Ben! You were close, but you didn't get the high score today. Thanks for
_10
playing.
_10
</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#lessThan scoreOne scoreTwo}}
_10
You were close, but you didn't get the high score today.
_10
{{/lessThan}}
_10
Thanks for playing.
_10
</p>


_11
// Test data one
_11
{
_11
"scoreOne": 55,
_11
"scoreTwo": 78
_11
}
_11
_11
// Test data two
_11
{
_11
"scoreOne": 100,
_11
"scoreTwo": 78
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>
_10
Hello Ben! You were close, but you didn't get the high score today. Thanks for
_10
playing.
_10
</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>Hello Ben! Thanks for playing.</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#lessThan scoreOne scoreTwo}}
_10
You were close, but you didn't get the high score today.
_10
{{else}}
_10
Congratulations, you have the high score today!
_10
{{/lessThan}}
_10
Thanks for playing.
_10
</p>


_11
// Test data one
_11
{
_11
"scoreOne": 55,
_11
"scoreTwo": 78
_11
}
_11
_11
// Test data two
_11
{
_11
"scoreOne": 100,
_11
"scoreTwo": 78
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>
_10
Hello Ben! You were close, but you didn't get the high score today. Thanks for
_10
playing.
_10
</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>
_10
Hello Ben! Congratulations, you have the high score today! Thanks for playing.
_10
</p>

The equals comparison can check for equality between two values of the same data type. The equals helper will also attempt to coerce data types to make a comparison of values independent of their data type. For example, {{#equals 3 "3"}} will evaluate to true.

(error)

Danger

Please be aware that the editor's Preview page will not properly render the results of a comparison between coerced values. You will see proper comparisons between coerced values only in a delivered message.

When checking for truthiness, be aware that empty strings, zero integers, and zero floating point numbers evaluate to false. Non-empty strings, non-zero integers, and non-zero floating point numbers, including negative numbers, evaluate to true.


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#equals customerCode winningCode}}
_10
You have a winning code.
_10
{{/equals}}
_10
Thanks for playing.
_10
</p>


_11
// Test data one
_11
{
_11
"customerCode": 289199,
_11
"winningCode": 289199
_11
}
_11
_11
// Test data two
_11
{
_11
"customerCode": 167320,
_11
"winningCode": 289199
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>Hello Ben! You have a winning code. Thanks for playing.</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>Hello Ben! Thanks for playing.</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#equals customerCode winningCode}}
_10
You have a winning code.
_10
{{else}}
_10
You do not have a winning code.
_10
{{/equals}}
_10
Thanks for playing.
_10
</p>


_11
// Test data one
_11
{
_11
"customerCode": 289199,
_11
"winningCode": 289199
_11
}
_11
_11
// Test data two
_11
{
_11
"customerCode": 167320,
_11
"winningCode": 289199
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>Hello Ben! You have a winning code. Thanks for playing.</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>Hello Ben! You do not have a winning code. Thanks for playing.</p>

The notEquals comparison can check for inequality between two values of the same data type. The notEquals helper will also attempt to coerce data types to make a comparison of values independent of their data type. For example, {{#notequals 3 "3"}} will return true.

When checking for truthiness, be aware that empty strings, zero integers, and zero floating point numbers evaluate to false. Non-empty strings, non-zero integers, and non-zero floating point numbers, including negative numbers, evaluate to true.


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#notEquals currentDate appointmentDate}}
_10
Your appointment is not today.
_10
{{/notEquals}}
_10
We look forward to your visit.
_10
</p>


_11
// Test data one
_11
{
_11
"currentDate": 20230715,
_11
"appointmentDate": 20230715
_11
}
_11
_11
// Test data two
_11
{
_11
"currentDate": 20230710,
_11
"appointmentDate": 20230715
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>Hello Ben! We look forward to your visit.</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>Hello Ben! Your appointment is not today. We look forward to your visit.</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#notEquals currentDate appointmentDate}}
_10
Your appointment is not today.
_10
{{else}}
_10
Your appointment is today.
_10
{{/notEquals}}
_10
We look forward to your visit.
_10
</p>


_11
// Test data one
_11
{
_11
"currentDate": 20230715,
_11
"appointmentDate": 20230715
_11
}
_11
_11
// Test data two
_11
{
_11
"currentDate": 20230710,
_11
"appointmentDate": 20230715
_11
}


_10
<!-- Resulting HTML from test data one-->
_10
<p>Hello Ben! Your appointment is today. We look forward to your visit.</p>
_10
_10
<!-- Resulting HTML from test data two-->
_10
<p>Hello Ben! Your appointment is not today. We look forward to your visit.</p>

When checking for truthiness, be aware that empty strings, zero integers, and zero floating point numbers evaluate to false. Non-empty strings, non-zero integers, and non-zero floating point numbers, including negative numbers, evaluate to true.


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#and favoriteFood favoriteDrink}}
_10
Thank you for letting us know your dining preferences.
_10
{{/and}}.
_10
We look forward to sending you more delicious recipes.</p>


_11
// Test data one
_11
{
_11
"favoriteFood": "Pasta",
_11
"favoriteDrink": ""
_11
}
_11
_11
// Test data two
_11
{
_11
"favoriteFood": "Pasta",
_11
"favoriteDrink": "Coffee"
_11
}


_10
<!-- Resulting HTML from test data one -->
_10
<p>Hi Ben! We look forward to sending you more delicious recipes.</p>
_10
_10
<!-- Resulting HTML from test data two -->
_10
<p>
_10
Hi Ben! Thank you for letting us know your dining preferences. We look forward
_10
to sending you more delicious recipes.
_10
</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#and favoriteFood favoriteDrink}}
_10
Thank you for letting us know your dining preferences.
_10
{{else}}
_10
If you finish filling out your dining preferences survey, we can deliver you recipes we think you'll be most interested in.
_10
{{/and}}.
_10
We look forward to sending you more delicious recipes.</p>


_11
// Test data one
_11
{
_11
"favoriteFood": "Pasta",
_11
"favoriteDrink": ""
_11
}
_11
_11
// Test data two
_11
{
_11
"favoriteFood": "Pasta",
_11
"favoriteDrink": "Coffee"
_11
}


_12
<!-- Resulting HTML from test data one -->
_12
<p>
_12
Hi Ben! If you finish filling out your dining preferences survey, we can
_12
deliver you recipes we think you'll be most interested in. We look forward to
_12
sending you more delicious recipes.
_12
</p>
_12
_12
<!-- Resulting HTML from test data two -->
_12
<p>
_12
Hi Ben! Thank you for letting us know your dining preferences. We look forward
_12
to sending you more delicious recipes.
_12
</p>

When checking for truthiness, be aware that empty strings, zero integers, and zero floating point numbers evaluate to false. Non-empty strings, non-zero integers, and non-zero floating point numbers, including negative numbers, evaluate to true.


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#or isRunner isCyclist}}
_10
We think you might enjoy a map of trails in your area.
_10
{{/or}}.
_10
Have a great day.
_10
</p>


_16
// Test data one
_16
{
_16
"isRunner": true,
_16
"isCyclist": false
_16
}
_16
_16
// Test data two
_16
{
_16
"isRunner": false,
_16
"isCyclist": false
_16
}
_16
// Test data three
_16
{
_16
"isRunner": false,
_16
"isCyclist": true
_16
}


_14
<!-- Resulting HTML from test data one -->
_14
<p>
_14
Hi Ben! We think you might enjoy a map of trails in your area. You can find
_14
the map attached to this email. Have a great day.
_14
</p>
_14
_14
<!-- Resulting HTML from test data two -->
_14
<p>Hi Ben! Have a great day.</p>
_14
_14
<!-- Resulting HTML from test data three -->
_14
<p>
_14
Hi Ben! We think you might enjoy a map of trails in your area. You can find
_14
the map attached to this email. Have a great day.
_14
</p>


_10
<!-- Template -->
_10
<p>
_10
Hello Ben!
_10
{{#or isRunner isCyclist}}
_10
We think you might enjoy a map of trails in your area. You can find the map attached to this email.
_10
{{else}}
_10
We'd love to know more about the outdoor activities you enjoy. The survey linked below will take only a minute to fill out.
_10
{{/or}}.
_10
Have a great day.
_10
</p>


_16
// Test data one
_16
{
_16
"isRunner": true,
_16
"isCyclist": false
_16
}
_16
_16
// Test data two
_16
{
_16
"isRunner": false,
_16
"isCyclist": false
_16
}
_16
// Test data three
_16
{
_16
"isRunner": false,
_16
"isCyclist": true
_16
}


_17
<!-- Resulting HTML from test data one -->
_17
<p>
_17
Hi Ben! We think you might enjoy a map of trails in your area. You can find
_17
the map attached to this email. Have a great day.
_17
</p>
_17
_17
<!-- Resulting HTML from test data two -->
_17
<p>
_17
Hi Ben! We'd love to know more about the outdoor activities you enjoy. The
_17
survey linked below will take only a minute to fill out. Have a great day.
_17
</p>
_17
_17
<!-- Resulting HTML from test data three -->
_17
<p>
_17
Hi Ben! We think you might enjoy a map of trails in your area. You can find
_17
the map attached to this email. Have a great day.
_17
</p>

The length helper will return the number of characters in a given string or array. For non-string and non-array values, length will return 0. Length can be useful in combination with other helpers as shown with greaterThan in the following example.


_10
<!-- Templates -->
_10
<p>
_10
Hello Ben!
_10
{{#greaterThan (length cartItems) 0}}
_10
It looks like you still have some items in your shopping cart. Sign back in to continue checking out at any time.
_10
{{else}}
_10
Thanks for browsing our site. We hope you'll come back soon.
_10
{{/greaterThan}}
_10
</p>


_10
// Test data one
_10
{
_10
"cartItems": ["raft", "water bottle", "sleeping bag"]
_10
}
_10
_10
// Test data two
_10
{
_10
"cartItems": []
_10
}


_10
<!-- Resulting HTML with test data one-->
_10
<p>
_10
Hello Ben! It looks like you still have some items in your shopping cart. Sign
_10
back in to continue checking out at any time.
_10
</p>
_10
_10
<!-- Resulting HTML with test data two-->
_10
<p>Hello Ben! Thanks for browsing our site. We hope you'll come back soon.</p>

You can loop or iterate over data using the {{#each }} helper function to build lists and perform other useful templating actions.

Basic Iterator with each

basic-iterator-with-each page anchor

_10
<!-- Template -->
_10
<ol>
_10
{{#each user.orderHistory}}
_10
<li>You ordered: {{this.item}} on: {{this.date}}</li>
_10
{{/each}}
_10
</ol>


_15
// Test data
_15
{
_15
"user": {
_15
"orderHistory": [
_15
{
_15
"date": "2/1/2018",
_15
"item": "shoes"
_15
},
_15
{
_15
"date": "1/4/2017",
_15
"item": "hat"
_15
}
_15
]
_15
}
_15
}


_10
<!-- Resulting HTML -->
_10
<ol>
_10
<li>You ordered: shoes on: 2/1/2018</li>
_10
<li>You ordered: hat on: 1/42017</li>
_10
</ol>

The following examples show you how to combine multiple Handlebars functions to create a truly dynamic template.

Dynamic content creation

dynamic-content-creation page anchor

_10
<!-- Template -->
_10
\{{#each user.story}}
_10
{{#if this.male}}
_10
<p>{{this.date}}</p>
_10
{{else if this.female}}
_10
<p>{{this.item}}</p>
_10
{{/if}}
_10
{{/each}}


_22
// Test data
_22
{
_22
"user": {
_22
"story": [
_22
{
_22
"male": true,
_22
"date": "2/1/2018",
_22
"item": "shoes"
_22
},
_22
{
_22
"male": true,
_22
"date": "1/4/2017",
_22
"item": "hat"
_22
},
_22
{
_22
"female": true,
_22
"date": "1/1/2016",
_22
"item": "shirt"
_22
}
_22
]
_22
}
_22
}


_10
<!-- Resulting HTML -->
_10
<p>2/1/2018</p>
_10
<p>1/4/2017</p>
_10
<p>shirt</p>

Dynamic content creation with dynamic parts 1

dynamic-content-creation-with-dynamic-parts-1 page anchor

_18
<!-- Template -->
_18
\{{#each user.story}}
_18
{{#if this.male}}
_18
{{#if this.date}}
_18
<p>{{this.date}}</p>
_18
{{/if}}
_18
{{#if this.item}}
_18
<p>{{this.item}}</p>
_18
{{/if}}
_18
{{else if this.female}}
_18
{{#if this.date}}
_18
<p>{{this.date}}</p>
_18
{{/if}}
_18
{{#if this.item}}
_18
<p>{{this.item}}</p>
_18
{{/if}}
_18
{{/if}}
_18
{{/each}}


_20
// Test data
_20
{
_20
"user": {
_20
"story": [
_20
{
_20
"male": true,
_20
"date": "2/1/2018",
_20
"item": "shoes"
_20
},
_20
{
_20
"male": true,
_20
"date": "1/4/2017"
_20
},
_20
{
_20
"female": true,
_20
"item": "shirt"
_20
}
_20
]
_20
}
_20
}


_10
<!-- Resulting HTML -->
_10
<p>2/1/2018</p>
_10
<p>shoes</p>
_10
<p>1/4/2017</p>
_10
<p>shirt</p>

Dynamic content creation with dynamic parts 2

dynamic-content-creation-with-dynamic-parts-2 page anchor

_10
<!-- Template -->
_10
\{{#if people}}
_10
<p>People:</p>
_10
{{#each people}}
_10
<p>{{this.name}}</p>
_10
{{/each}}
_10
{{/if}}


_10
// Test data
_10
{
_10
"people": [{ "name": "Bob" }, { "name": "Sally" }]
_10
}


_10
<!-- Resulting HTML -->
_10
<p>People:</p>
_10
<p>Bob</p>
_10
<p>Sally</p>



Rate this page: