Get started
Working with merge field helpers
Merge field helpers extend the capabilities of standard merge fields, enabling more dynamic and complex logic using advanced helper syntax. With helpers, you can apply conditional logic to your templates and iterate over and display lists within channel-based nodes (for example, email body or SMS body fields).
Merge field helpers are only supported in message content fields within channel-based nodes. Other fields will display an error if helper syntax is used.
This guide outlines supported syntax, use cases, examples, and important considerations.
What are merge field helpers?
Merge field helpers are tools that let you perform advanced data manipulations within merge fields. These helpers allow for conditional rendering, looping through arrays, and using context-aware variables like @index or @first.
Supported helpers:
| Helper | Use Case | Example |
|---|---|---|
{{#if}} |
Conditional rendering if a value exists |
Handlebars template: Result: If |
{{#unless}} |
Render if the condition is false, empty, or null |
Handlebars template: Result: If |
{{#each}} |
Iterate over arrays. Works with arrays of primitives or arrays of objects. Use arrays of objects to output tables or multi-field lists. Use arrays of primitives for single-value lists. |
Primitive example: Payload:
{"names":["Alice","Bob","Charlie"]}
Handlebars:
{{#each names}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
Result:
Alice, Bob, Charlie
Object array example: See Building tables with array data below. |
{{else}} |
Define alternative output inside {{#if}} when the condition is false. |
{{#if subscribed}}
Thank you for being a subscriber!
{{else}}
Stay informed and subscribe now!
{{/if}}
|
{{this}} |
Represents the current item inside {{#each}}. For objects, use {{this.field}} to access properties. |
Primitive array:
{{#each names}}Name: {{this}} {{/each}}
Object array:
{{#each people}}Person: {{this.name}} {{/each}}
|
Building tables with array data
To generate tables or multi-column lists, you must use an array of objects. This is required for rendering itemised receipts, order summaries, or any structure where each row shows multiple fields.
["A", "B", "C"]) can only be rendered as single-column lists. For tables, you must provide objects ([{"product":...}, ...]).Required payload structure
{
"customer": {
"firstName": "Jess",
"orders": [
{
"product": "Pendula T-shirt",
"quantity": 2,
"price": "$25.00"
},
{
"product": "Sticker Pack",
"quantity": 1,
"price": "$5.00"
},
{
"product": "Notebook",
"quantity": 3,
"price": "$12.00"
}
]
}
}
Example HTML in template
<table cellpadding="5" cellspacing="0" border="1"
style="border-collapse: collapse; width: 100%; border: 1px solid #0a2342; font-family: Arial, sans-serif; font-size: 14px;">
<thead>
<tr style="background-color: #f2f2f2;">
<th>#</th><th>Product</th><th>Quantity</th><th>Price</th>
</tr>
</thead>
<tbody>
{{#each inboundWebhookTrigger_1.customer.orders}}
<tr>
<td>{{@index}}</td>
<td>{{this.product}}</td>
<td>{{this.quantity}}</td>
<td>{{this.price}}</td>
</tr>
{{/each}}
</tbody>
</table>
Example output
| # | Product | Quantity | Price |
|---|---|---|---|
| 0 | Pendula T-shirt | 2 | $25.00 |
| 1 | Sticker Pack | 1 | $5.00 |
| 2 | Notebook | 3 | $12.00 |
Notes
- Arrays of primitives cannot produce tables.
- Field names (
product,quantity,price) must match your payload exactly (case sensitive). -
@indexgives the position in the loop (starts at 0).
Supported data variables (within #each loops):
| Variable | Description | Example |
|---|---|---|
@index |
The current index in the loop |
{{#each names}}{{@index}}: {{this}}\n{{/each}} → 0: Alice 1: Bob 2: Charlie
|
@key |
The key when looping over objects |
{{#each user}}{{@key}}: {{this}}\n{{/each}} (with { name: "Alice" }) → name: Alice
|
@first |
true if the current item is the first |
{{#each names}}{{#if @first}}First: {{/if}}{{this}}{{/each}} → First: Alice Bob Charlie
|
@last |
true if the current item is the last |
{{#each names}}{{this}}{{#unless @last}}, {{/unless}}{{/each}} → Alice, Bob, Charlie
|
Partial support
-
Nested helpers: You can nest helpers (e.g.,
{{#each}}inside{{#if}}), but complex nesting may reduce readability and increase the risk of errors.{{#each users}} {{#if this.active}} {{this.name}} {{/if}} {{/each}}
Key considerations
- Field-specific support: Merge field helpers are only supported in message content fields of channel nodes (e.g. email body). Other fields will display an error, ‘Helpers are not supported in this input field.’
-
Limited validation:
-
Unvalidated paths in helpers: Using
this.some.pathis not validated once helpers are used. - No deep syntax validation: Complex helper structures (e.g., nested loops with aliases) aren’t fully validated.
-
Closing tags: Helpers like
{{/if}}must be properly closed. Pendula’s validation system won’t enforce this.
-
Unvalidated paths in helpers: Using
-
“Iterable” type error: The
#eachhelper requires the referenced merge field to be an array or object. Using a non-iterable value triggers an error.
Troubleshooting common issues
| Error | Cause | Solution |
|---|---|---|
| Wavy red underline on merge field | Invalid or unrecognised merge field in helper block (misspelled or unsupported path) | Check spelling and ensure the field exists in the payload |
| “Iterable” type error | Using #each on a non-array or non-object |
Verify the merge field references an array/object |
| Blank output in message | Merge field has no data, invalid path, or primitive array used in a table | Use {{else}} or #unless for fallbacks, and check your payload structure |