Skip to main content

9 Power Tips for Using Liquid to Personalize Emails

Personalizing your emails requires personalized content — and one incredibly powerful way to do that is with Liquid, an open-source template language created by Shopify.

Liquid enables you to load and display dynamic content based on the data you have, whether it’s for an e-commerce experience built with Shopify or messages sent through You can even incorporate dynamic logic to personalize your emails without needing an advanced computer science degree. This provides much more flexibility than the basic merge tags many email service providers use.

My name is Carl and my company MailCharts, which allows e-commerce companies to plan and optimize their campaigns, uses Over the last few years, I’ve gained loads of experience in working with Liquid. In this post, I’ll show you high-value tricks and “good to knows” — from the basics to the more advanced — that you’ll find useful when creating your next email in

First, some Liquid basics

There are 3 types of Liquid code:

Objects, or variables output dynamic content. Objects use double curly braces {{ }} to output, or render, the content.

Liquid objects example

Filters modify the output of a Liquid object. You add filters inside the output braces and denote it using a pipe character |.

Liquid filter example

Tags create the programming logic that directs templates on what to do with data. You can do things like apply if/then logic, assign variables, and create conditions.

Liquid tags example

Next-level Liquid Tips

Day of the week

Ever receive an email with a “Happy Thursday” towards the end of the email? I find this detail to be super friendly. Here’s how to add this to your mailers:

Happy {{ 'now' | timezone: "US/Central" | date: "%A"}}

Notice the timezone: “US/Central” part. Make sure you change this to your own timezone (and test the timezone to make sure it works).

Grabbing the first name from a full name

Let’s say you want to include your customer’s first name in the subject line, but all you have is their full name (first name and last name together in one entry, like “Kimmy Schmidt”). Yes, you could create a script to add a new attribute to every subscriber — or you can use this code:

{{ | truncatewords: 1, "" }}

(This works 99% of the time. You’ll run into a small issue if someone has a first name with two names. So if someone entered Ford Allen Mc Miller — with Ford Allen as their first name and Mc Miller as their last, you’d be sending the email displaying Ford in the subject.)

Capitalizing names

It’s always a good idea to capitalize words correctly, like someone’s first name. The problem is that data entered manually by your users probably won’t be consistent over time. Here’s how to address that:

{{ customer.first_name | capitalize }}

Voila! Their name will always be capitalized.

You can also mix and match the tricks shared in this post. For example, here’s the Liquid to display the first name from a full name and make sure it’s always capitalized:

{{ | truncatewords: 1, "" | capitalize }}

If / else logic

With Liquid, you can include if / else logic in your emails. This allows you to personalize email content based on different conditions, controlling what you render and when. Here’s how you can create a very simple if / else statement:

Hi {% if customer.first_name %}{{ customer.first_name }}{% else %}there{% endif %}, 
welcome to MailCharts!

If a user has the customer.first_name attribute, the message will merge in the name — and if the attribute doesn’t exist, you’ll see “Hi there, welcome to MailCharts!”

Dynamic logic if an attribute exists

When using an if / else statement, it’s a best practice to double-check that a field actually exists. This prevents you from running into unexpected errors, which can happen if the property technically exists on the user record but is empty.

You can do this by appending .size > 0 to your if / else logic. Putting that together with the | capitalize filter, here it is in action:

Hi {% if customer.first_name.size > 0 %}{{ customer.first_name | capitalize }}
{% else %}there{% endif %}, welcome to MailCharts!


Let’s say you want to add a nice, random quote to the end of your emails. Or maybe you want to randomize which product benefits you show over time. You can achieve this by using a capture group:

{% capture timeSeed %}{{ 'now' | date: "%s" }}{% endcapture %}
{% assign random = timeSeed | modulo: 5 %}

{% if random == 0%}
Message 1
{% elsif random == 1 %}
Message 2
{% elsif random == 2 %}
Message 3
{% elsif random == 3 %}
Message 4
{% elsif random == 4 %}
Message 5
{% endif %}

Here, we created a timeSeed variable which gets assigned the seconds of the current date. Then we use modulo: 5 on it. Why 5? Because in our example, we wanted to rotate through 5 different messages. If you had 6 messages, use modulo 6. If you had only 2 messages, use modulo 2 — and so forth.

We then use the typical if, elsif, else logic to display the different messages.

Here’s another example in action, with randomized burger specials:

Liquid code to randomize content

Advanced Liquid Tips

Loops with limits

Imagine you want to send an email featuring 4 products that a user checked out on your site. If you’re saving each product viewed inside an array in your data, you can simply use a for loop with a limit, as such:

{% for product in customer.products_viewed limit:4 %}{{ }}, {% endfor %}

Here’s how we recently used this on a MailCharts email:

You can now get detailed insights on {% if customer.suggested_companies.size > 3 %}
{% for company in customer.suggested_companies limit:4 %}{{ }}, {% endfor %}
or any other company{% else %}any company{% endif %} by running a Company Overview Report.

The result, based on my attributes: “You can now get detailed insights on J Crew, MailChimp, Warby Parker, Litmus, or any other company by running a Company Overview Report.”

And here’s a preview:

MailCharts email

Notice how we combined a for loop with a limit parameter inside an if statement that used a .size check. (Try saying that fast four times. Go.)

Clean(er) if logic

If you’ve ever had to manage emails with lengthy if/else logic then you know how difficult code maintenance can be. No worries though, the capture tag is your friend.

Here’s an example:

{% if customer.items_in_cart.size > 1 %}
{% capture destinationUrl %}{{ }}{% endcapture %}
{% capture buttonText %}Buy {{ }}{% endcapture %}
{% else %}
{% capture destinationUrl %}{% endcapture %}
{% capture buttonText %}View all products{% endcapture %}
{% endif %}

<a href="/%7B%7BdestinationUrl%7D%7D">{{buttonText}}</a>

Neat, right? You define all your variables at the top of the email and then simply use them throughout.

(Note: You can also use {% assign buttonText = "View all products" %} if the value you’re assigning doesn’t rely on Liquid code being executed. In the example above, we need to use a capture group because of the {{ }} part of the destination url.)

URL encoding

Last, but certainly not least, if you want to include a user’s email address in a link. A common use-case is to pre-populate a form or other tracked paramters with their email address). But how do you handle users that have a plus sign (“+”) in their email address? (e.g.

Sometimes, if you simply use {{ }}, what you’ll see is something like inside the form. That’s not what you want!

If that happens, all you need to do is use the filter | escape. Using escape allows you to fully escape all special characters.

Here’s the code:

{{ “” | escape }}


{{ "" | escape }}

Instead of, you’ll see “” in the form.

Want to learn more? Shopify has a great basics overview. The documentation on Liquid and merging structured data get very detailed, and here are the Liquid tags that supports.

Do you have any other tips or tricks the community would find helpful? If so, leave them in the comments below!

Carl Sednaoui headshotCarl Sednaoui is the co-founder and director of marketing at MailCharts and a veritable Liquid expert. MailCharts helps email marketers plan and optimize their email programs, using competitive and industry intel, plus persona-based analysis and transactional insights.