Skip to main content

Dynamic Email Content with Liquid

Read to learn how to personalize your emails with dynamic content using Liquid.

Written by Clare Atreo

Personalizing Emails with Merge Tags

Qomon email templates use Liquid, a template language that lets you insert dynamic content into your emails. Merge tags are placeholders that Qomon replaces with real data when an email is sent - contact names, event details, petition titles, unsubscribe links, and more.

The data available to your template depends on where the email is used:

  • Email campaigns: Contact data only (contact.firstname, contact.surname, contact.mail).

  • Action Flow emails: Full contact data, plus action, petition, and online form data.


How Liquid works

Liquid uses two main syntaxes:

Syntax

Purpose

Example

{{ variable }}

Output a value

{{ contact.firstname }}

{% tag %}

Logic (conditions, loops)

{% if contact.mail %}…{% endif %}

You can also apply filters to transform values:

{{ contact.firstname | default: "Friend" }}

Note - Qomon uses open-source Liquid, not Shopify themes. The Shopify Liquid reference covers the same core syntax, but includes Shopify-only objects (product, cart, etc.) that don't exist in Qomon. Use the open-source Liquid docs as your reference.


Finding merge tags in Qomon

To find available merge tags head to the Merge tags panel in the email preview:

  1. Open an email campaign or Action Flow email and click "Open Merge Tags Panel".

  2. In the preview sidebar, add a data source (e.g. a contact or event).

  3. Expand Merge tags to see every available key with a sample value.

  4. Click a key to copy it as a ready-to-use tag, e.g. {{ contact.firstname }}.

Always copy keys from this panel - names and prefixes match exactly what the renderer expects.


Merge tag namespaces

Tags are grouped by namespace (a prefix before the field name).

contact.* - Contact data

Merge tag

Description

{{ contact.firstname }}

First name

{{ contact.surname }}

Last name

{{ contact.mail }}

Email address

{{ contact.mobile }}

Mobile phone

{{ contact.membership_code }}

Membership code

{{ contact.last_membership_end_date }}

Membership end date

action.* - Events and actions

Available in Action Flow emails where an action is linked to the message.

Merge tag

Description

{{ action.name }}

Event name

{{ action.start }}

Start date/time (raw)

{{ action.end }}

End date/time (raw)

{{ action.link }}

Public link

{{ action.registration_link }}

Registration link

{{ action.broadcast_link }}

Live stream link

{{ action.hide_broadcast_link }}

Whether broadcast link is hidden (true/false)


Root-level action keys (no prefix)

These pre-formatted keys are ready for email display. They do not use the action. prefix:

Merge tag

Description

{{ display_date_range }}

Human-readable date range (locale-aware)

{{ display_time_range }}

Human-readable time range (locale-aware)

{{ display_pitch }}

Formatted event description

{{ lineAddress1 }}

Street and number

{{ lineAddress2 }}

City and postal code

{{ lineAddress3 }}

Country

{{ lineAddressInfo }}

Extra info (building, room, etc.)

Prefer these over raw action.start / action.end / action.pitch - they handle locale and time zone automatically.

site.* - Petitions and online forms

Merge tag

Description

{{ site.title }}

Page title

{{ site.author }}

Author name

{{ site.redirect_url }}

Redirect URL after submission

{{ site.end_date }}

End date

Use {{ site.title }}, not {{ petition.title }} - the petition.* namespace doesn't exist.


Special tags

Merge tag

Description

{{ unsubscribe }}

Unsubscribe link - required in all bulk emails

{{ view }}

"View in browser" link

<a href="{{ unsubscribe }}">Unsubscribe</a>
<a href="{{ view }}">View this email in your browser</a>


Handling missing data

Contacts often have incomplete profiles. Always plan for empty fields.

Use the default filter for optional text fields:

Hello {{ contact.firstname | default: "there" }},

Use {% if %} to hide an entire block when a field is absent:

{% if contact.mobile %}
Call us: {{ contact.mobile }}
{% endif %}

Use {% unless %} for the reverse:

{% unless action.hide_broadcast_link %}
<a href="{{ action.broadcast_link }}">Watch the live stream</a>
{% endunless %}

Liquid falsy values: Only nil and false are falsy. Empty strings ("") and 0 are truthy - use the default filter rather than {% if %} alone for text fields.

Address keys (lineAddress1, etc.) are omitted entirely when no location is set, so always wrap them in conditionals:

{% if lineAddress1 %}
{{ lineAddress1 }}<br>
{% endif %}


Example: event invitation email

<h2>You're invited: {{ action.name }}</h2>

<p>{{ display_pitch }}</p>

<p>
<strong>When:</strong><br>
{{ display_date_range }}<br>
{{ display_time_range }}
</p>

{% if lineAddress1 %}
<p>
<strong>Where:</strong><br>
{{ lineAddress1 }}<br>
{% if lineAddress2 %}{{ lineAddress2 }}<br>{% endif %}
{% if lineAddress3 %}{{ lineAddress3 }}<br>{% endif %}
</p>
{% endif %}

<p><a href="{{ action.registration_link }}">Register now</a></p>

<a href="{{ unsubscribe }}">Unsubscribe</a>


Common mistakes

❌ Incorrect

✅ Correct

{{ petition.title }}

{{ site.title }}

{{ action.display_date_range }}

{{ display_date_range }}

{{ contact.first_name }}

{{ contact.firstname }}


Further reading

Questions? Contact our team via the live chat!

Did this answer your question?