Fun Virtual Team Building Activities We Love

Alyssa Welch

Looking for some fun virtual activities and games? Our team has tried a lot and is here to share the ones we actually love and continue to use. If you’re a remote team, these are the most fun virtual team building activities we recommend. 

Not all of these virtual team building activities cost money or require a lot of work to plan. We tried to offer a range of virtual activities for your team that we’ve had positive experiences with and enjoy coming back to again and again!

21 Virtual Activities and Games

Plan Your Escape

Putting problem-solving skills to the test in escape rooms is always a fun challenge, but how can we do that over Zoom in a way that doesn’t feel cheesy? The Escape Game Virtual Escape Rooms are hosted LIVE from their actual locations! Mixing reality with a virtual dashboard, you direct your guide to where you’d like to find clues and solve puzzles, which allows our team to work together to escape without being locked in a small room! Bonus: You also get a fun group photo at the end and bragging rights for whatever group was quicker to escape!

Escape Room winners with The Escape Game

Get Your Game On

Long days on video calls can get draining pretty quickly. We’ve tried virtual game platforms like Zoom Apps, but our team has found connecting in virtual reality with Oculus headsets to be the most fun and feels closer to being together in person! We have monthly game time to play VR together as well as a #vr_meetups slack channel for whenever folks want to jump into a game. We love these Oculus virtual team games: PokerVR, BeatSaber, Elven Assassin, Walkabout Mini Golf, Cook-Out, Catan, and Demeo, to name a few. 

Note: This virtual activity does involve some upfront costs, but the investment is worth it to bring the team together in a fun, unique way off computers. We send each new team member an Oculus headset and reimburse them up to $100 in VR games.

Pro tip: you can cast to your computer and stream on Zoom. We do that for competitions – this was our PokerVr final.

Share a Meal or Happy Hour

You don’t need an office or be together in person to share a meal anymore. Pizzatime is the easiest way to organize a pizza party for your remote team across different time zones and even countries! You select your pizza preferences, and everyone’s order arrives at the same time. While you’re enjoying that slice of pie, you can add an experience like Stand up Comedy or Radio Bingo! Coffee more your jam? They do Coffeetime, too. Same concept, just caffeinated.

Update: they now offer Lunchtime and Drinktime for teams looking for more options!

Visit a Farm

Goat-2-Meeting is for all the animal lovers on your team! A tour guide from Sweet Farm will join your Zoom to give you a brief intro to the farm and show you some of the animals, depending on who wants to be on camera right then! Paco the goat and Sturgis the horse might be our favorite duo!

Sweet Farm's Goat-2-Meeting

Host a Sip and See

No, this isn’t to meet a new baby, but our take on Lunch and Learn’s. Whether you’re team coffee or team tea, you can learn about your favorite beverage and bond! For tea enthusiasts, Mona at Teawala offers a virtual tea tasting where she shares the properties, history, and benefits of tea as you brew and taste them together at home. For your Coffeeholics, join Ricardo for the Worlds Top Coffee Masterclass AirBnB experience! Both of these are fun virtual team building activities where you get to enjoy a drink and learn something new.

Teawala Virtual Tea Tasting with Mona

Watch a Movie

What’s better than watching a movie together? Playing Bingo at the same time! We recommend using Teleparty for your next movie night. It works with Netflix, Hulu, Disney, and HBO and allows up to 1,000 viewers! Even if you’ve seen the movie before, you need to pay attention if you want to dab all the bingo spots to win a prize! We did this for Halloween with Hocus Pocus, and it was a blast! Pro tip: Etsy is a great place to look for themed Bingo cards (we used these for Hocus Pocus).

Get Crafty

Our team has many parents, and we love including their children in our virtual activities whenever we can! Arts and craft sessions are a perfect way to do that. We’ve worked with Lori Richmond, a children’s author-illustrator and artist from Brooklyn, New York, who offers in-person and virtual drawing workshops! With a simple shopping list, parents and their kiddos show up while Lori tells a fun and engaging story, walking you through the steps to create your very own masterpiece!

Drawing with Lori Richmond

Have someone on your team who’s crafty? Have them host a session! We’ve had embroidery lessons from our Product Manager and macrame with our Technical Support Engineer! Your group can purchase supplies on their own, or you can send kits to each team member.

Customer.io embroider with Kate

Paint and Sip

There’s no need to rush out to the craft store for this virtual activity. Each participant will receive a care package with everything they’ll need, including a canvas, easel, paint, palette, and brushes before Paint Night. How easy is that? Grab a beverage and follow along with the instructor to create a masterpiece together! We channeled our inner van Gogh’s and painted mountain landscapes with Ryptic Team Building’s Virtual Paint Night.

Ryptic Team Building Virtual Paint Night

Get Moving

Working from a desk can be sedentary, so we love finding hosts to get us up and moving! Some of our favorites include meditation and yoga with Om Office Yoga Vibes and Tai Chi and Qi-gong with Shifu Dao from STQI Toronto School! We’re also lucky enough to have a certified yoga instructor on our team who teaches Yoga Nidra for everyone. Whatever your team is into, doing fitness and mindfulness together is a great virtual team building activity. 

Customer.io does Tai Chi with Shifu Dao

Test Your Knowledge

Whether you’re looking for an icebreaker or a fun virtual game, Kahoot is a great way to play trivia with the entire team. You can play already made trivia games, but we love creating our own with Customer.io trivia questions about the team. 

Movies more your thing? Another fun trivia game to play over Zoom is Connect the Stars. You choose two actors and try to link them together with as few connections as possible. This virtual game is fun to play as a group or in smaller teams that compete with one another.

Connect the Stars virtual game

Find Your Next Star Baker

The Great Ami Bake Off has become a team favorite! We’ve had contestants make a Customer.io logo cookie, an Ami (our mascot) cake, Kawaii-themed desserts, and spooky Halloween treats. All you need are volunteers, a shopping list, and their kitchens. We have each baker spotlighted on Zoom, with hosts narrating during the competition. Hosting a Bake Off is a super fun virtual team building activity your team will want to make an annual event!

Great Ami Bake Off

Show Off a Talent

Who says you have to be together to sing karaoke? Our team hosts karaoke parties over Zoom. You choose your song and share your audio and sing along. The audio lag adds to the fun and laughs! Not to mention singing in your room by yourself at noon with or without any liquid courage.

CEO of Customer.io Colin singing karaoke

In addition to karaoke parties, we’ve also hosted a Customer.io Talent Show over Zoom. Contestants sign up and either perform their talent live or show a recording. This is a fun way to discover hidden talents on your team—we have a classically trained opera singer and some incredible musicians that play all sorts of instruments.

Bill as a judge for Customer.io's Talent Show

Share a Passion

What better way to share something you’re passionate about with your team and teach them something new with a Powerpoint Party? This has become a staple in our company and something we look forward to time and again. We’ve learned about bog bodies (this still comes up years later), RV adventures, budgeting, and so much more. 

Customer.io Powerpoint Party virtual activity

Read A Book… Or Maybe Two or Three

Start a Bookclub. Our team meets every couple of weeks to discuss the latest book they’ve read. We’ve read: Klara and the Sun, Empowered, 100 Years of Solitude, How to be an Antiracist, Pachinko, and even watched a movie, The Lunchbox, together. The team loves coming together to share insights and have meaningful discussions about each book. 

Customer.io bookclub

Full Service Companies

Need help hosting your own virtual team building activity? We recommend these services to do the planning for you.

TeamBuilding.com

TeamBuilding is the number one team building company that provides these activities and events as a service. They do all the heavy lifting for you! Show up with your team and be guided through a series of puzzles, scavenger hunts, and riddles—the team to complete them first wins! From Ghost Hunt Haunted Mansion to Tiny Campfire (s’mores included!), the live hosts stay in character throughout the entire activity. If there are supplies to send to your team in advance, they’ll handle it all! TeamBuilding has been a go-to resource for our virtual retreats and team meetups.

Tiny Campfire with TeamBuilding.com

Boombox Events

Another full service for team building and bonding, Boombox has activities ranging from calligraphy, pasta making, and mixology classes. Their team will work with you to get all the supplies you’ll need to create an awesome virtual experience. We especially loved Brush Script with Melissa.

Brush Script with Melissa via Boombox Events

Alyssa Welch

Alyssa is our People & Culture Specialist. She’s responsible for creating memorable team member experiences through culture-building and team bonding activities such as retreats, team meetups, and more.

Follow Customer.io on Instagram to see the latest things the team is up to, including virtual activities and in-person meetups. 

Email Integration Tips for Startup Founders

Gabriela Kustner

Choosing a customer engagement platform can be tricky. Getting it to work right for you can be even trickier. Since we know that startup founders have limited brain space to spend on planning their email integration, we’ve included some of our own CEO’s do’s and don’ts for email integration that he spoke about recently on the Userlist podcast.

Do: measure twice, cut once.

“As you’re setting up your campaigns and deciding how to import your data and what to do with the data model, you will save a lot of time if you plan right,” says Colin Nederkoorn, Customer.io’s CEO. “Make sure you understand your email tool. Do a lot of small tests. Then, once you’re ready to import all of your data, make sure you’ve got the right data model that will support all of the automations you’ll want to do.”

Take it from us: spending 1 hour on planning your integration right may save you 10 hours in the future. Don’t neglect it.

Do: build with future integrations in mind.

Facts are facts: the customer engagement platform (CEP) you chose today won’t be the one you’re using in five years. When you plan your integration, aim to make it as much as possible platform-agnostic. That way when you change platforms (or the platform changes something), you’ll be ready to adapt. Avoid tools that require you to use proprietary languages that aren’t reusable in other systems: while not a CEP, Looker’s LookML, we’re looking at you.

Also, don’t constrain your future self by limiting the kind of data you send in to our platform. It can be hard to know what event data you’ll want to use in the future, so we recommend that you err on the side of sending in more data than less (while respecting privacy constraints, of course).

Don’t: disrespect your customers’ privacy.

If you’re like us, you may get a lot of emails from people offering to sell you lists of potential companies to reach out to. Don’t give in. “The most valuable thing that you have is your relationship with your customers and your audience,” says Colin. “Don’t betray that trust. First party data is the best kind of data, not purchased data.”

“Remember, the expectation that consumers have with what companies do with their data has been frayed. Even if you’re a good company, you might get tarred with the same brush that people are using to talk about the information resellers. Nowadays, consumers are naturally reticent to provide data to companies, but that trust is key to having a good relationship with your audience and customers. Be a good actor. Collect your own first party data. Set expectations for what you’re doing with it. And then treat your customers and their data well.”

In summary, though email marketing can feel like an after-thought with all the other important tasks you’ve got as a start-up founder, taking the time to think it through will save you effort and make for better customer relationships.

Using Liquid in Customer.io

Alex Patton

As we’ve discussed, there are many “flavors” of Liquid—and while you may encounter some variation in the terminology that’s used, the principles are all the same. Of course, Customer.io has its own Liquid flavor, tailored to personalize your emails! Here are some of the highlights you might want to explore next:

We also have some handy “recipes” for certain use cases; some fun ones you can try include:

Now that you’re armed with a core understanding of the three components of Liquid and how they work together, you can go out into the wilds and explore whatever flavor of Liquid you find there. Have fun getting super personal with your content—and driving higher engagement and conversation!

Ready to use your newfound knowledge of Liquid to craft more personalized messages?

For Loops

Alex Patton

You already know that you can use the for tag (also called a for loop) to iterate (aka, loop) over arrays and objects. A common use case for a for loop is listing each item a customer purchased in a transactional email. In this section, we’ll take a deeper dive into for loops so you can use them with greater precision to get better results with your emails.

Looping over objects and arrays

Arrays contain an ordered list of string or number values or an ordered list of objects (a collection of key/value pairs). Great news—you’ve already learned how to loop over an array of objects! Here’s the example you practiced on in the Intermediate Tags lesson:

{% for senior_pets in products.new_products.senior_pets %}
  - {{ senior_pets.title }} x {{ senior_pets.price }}
{% endfor %}
- bowl elevator x 15.00
- stair steps x 25.00
- carry sling x 30.00
- senior vitamins x 10.00

Products.new_products is an array, and each product—senior_pets—is an object that contains key/value pairs. In this case, senior_pets is just the name we’ve assigned the object; the first item in the for loop syntax just defines the context, so something like “for <x> in <y>” just defines the objects in the array as “<x>”.

For loops can also iterate over an array of string or number values. Let’s look at a loop over an array of strings:

{% assign items = "leash, bowl, collar" | split: ", " %}
{% for item in items %}{{ item }}{% endfor %}
leashbowlcollar

The for loop iterates through the array and outputs a list of its values. The second, third, and fourth lines of code above are the for loop tag.

A little detour to look at that first line of code, just so you know what you’re looking at:

  • We’ve used the assign tag to store a value in our code. In this case, the data type of the value type is a string.
  • But we know that the values need to be in a specific order so the for loop can iterate over them. So we’ve used the split filter to convert that string into an array—an ordered list of info. 
    • What about that comma and space between the quotation marks? That’s the delimiter the split filter will use to recognize and the individual bits of info in the string—when it sees the comma and space between the items in “leash, bowl, collar” it knows to split the string at those points

Looping over hashes

We talked about the concept of hashing as part of encryption schemes earlier in this module. The term “hash” can also refer to a data structure in Ruby (Liquid’s parent language) that is essentially—although not exactly—the same as a JavaScript object. You can use Liquid to iterate over hashes, and the syntax is slightly different. Here’s a primer on how to loop over hashes.

For loop helper tags

The for tag has several “helper” tags: else, break and continue. They give you more control over loops and more options for using them.

The else tag defines an alternative output if you loop over an empty array—instead of a blank line. Here’s how it looks:

{% assign items = empty %}
{% for item in items %}  {{ item }}{% else %}This array is empty.{% endfor %}
This array is empty.

Since the loop cannot run over the empty array, it displays the alternative output. You might use else to avoid extra lines in your emails when keys are unexpectedly empty.    

While available in certain liquid versions, the break and continue tags are not currently available in Customer.io.

Break and continue allow you to stop looping or skip a value. To use them, nest an if/else or elsif/else tag within your for tag. Let’s start by looking at break:

{% assign items = "leash, bowl, collar" | split: ", " %}
{% for item in items %}
    {% if item == "bowl" %}
       {% break %}    
    {% else %}{{ item }}   
    {% endif %}
{% endfor %}
leash

Let’s walk through what the Liquid code is doing:

Line 1: Assign creates a new key and stores its value. This value is an array.

Line 2: Loop over these keys, starting at the first one in the array.

Line 3: This opens the condition tag, which checks the item value against the condition.

Line 4: If the condition in Line 3 is met, stop (break). The loop is over.

Line 5: If the condition in Line 3 is not met, display the key’s value.

Line 6: Close the elsif/if tag.

Line 7: Close the for tag.

When the code runs, it checks the first value in the array to see if it equals “bowl”. If it doesn’t, the code loops over that value and displays it. Then the code runs again, starting at the second value in the array. It will keep looping until it finds “bowl” or reaches the end of the array. In this example, the code can only loop once, because “bowl” is the second value in the array.

Now let’s try the continue tag. It skips over a value in an array that meets a condition. We’ll use the same example as before: 

{% assign items = "leash, bowl, collar" | split: ", " %}
{% for item in items %}
    {% if item == "bowl" %}    
       {% continue%}   
    {% else %}  
       {{ item }}  
    {% endif %}
{% endfor %}
leashcollar

Here, when the next value in the array equals “bowl”, the code skips that value and starts again with the next value in the array’s sequence. It loops until it reaches the last value in the array.

The break and continue tags have a lot of handy uses when personalizing emails. Say a customer has bought a bowl, and you want to recommend related products without repeating the product they already bought—the continue tag makes it happen!  

For loop parameters: limit, offset, and reversed

There are three parameters that you can use with for loops: reversed, limit, and offset. Just as we discussed when describing localization tools, parameter in this context means an extra instruction that customizes the for loop to your needs.  

Reversed begins the loop at the last item in the array’s sequence, rather than the first.

{% assign items = "leash, bowl, collar" | split: ", " %}
{% for item in items reversed %}{{ item }}{% endfor %}
collarbowlleash

Notice that reversed is not separated with a pipe character–these parameters are not filters

Limit allows you to specify a number of iterations, rather than iterating until a break condition is met or the last item in the array

{% assign items = "leash, bowl, collar" | split: ", " %}
{% for item in items limit:2 %}  {{ item }}{% endfor %}
leashbowl

Limit must be followed by a colon and an integer (no spaces). The integer tells the for loop how many iterations to complete. Because we limited this loop to two iterations, only the first two values in the array are output.

Offset allows you to begin a loop at a specific index, or point in the sequence, rather than at 0, the first item in the sequence.

{% assign items = "leash, bowl, collar" | split: ", " %}
{% for item in items offset:2 %}  {{ item }}{% endfor %}
collar

You must follow offset with a colon and an integer (no spaces). The integer represents the position in the array at which the loop should start. Here, we’ve entered 2, so the loop will skip the first two positions in the array, start at the third position, and run until the end of the array. 

You can combine reversed, offset, and limit in the same line of code, like this: 

{% for item in items reversed limit:3 offset:2 %}

However, if you include reversed, it must come before limit and offset. The order of limit and offset doesn’t matter.


Up Next: Using Liquid in Customer.io

 

Encoding, Hashing, and Encryption Schemes

Alex Patton

Sometimes you may need to encode or hash values—or use hashes in an encryption scheme. Liquid filters can help you do that! We’ll give you a tour of the whys and whats here; then you can dive deeper in this Auth0.com article. You’ll know when you need to use the filters below because whatever you’re trying to do will explicitly state that you need to encode or hash certain values. 

Encoding

Encoding transforms a value for data-handling purposes—it’s not related to information security. For example, we encode strings containing special characters to make them URL-safe (aka, containing only characters that can be transmitted over the internet; this is often called percent encoding) and then decode them to make them easier for humans to read. URL encoding is important to make sure links in your emails work—unencoded URLs can break. Another encoding format is base64, used for embedding image files in HTML and sending email attachments. 

Take a look at the escape and url_encode filters. Escape replaces special characters—in this case, quotation marks—with percent-encoded, URL-safe characters:

{{ "Have you read 'How to Train Your Senior Dog'?" | escape }}
Have you read &#39;How to Train Your Senior Dog&#39;?

The URL_encode filter converts URL-unsafe characters with percent-encoded characters and converts spaces to a plus sign:

{{ "reach me at lee@email.com" | url_encode }}
reach+me+at+lee%40email.com

You can reverse URL encoding with the url_decode filter

{{ "reach+me+at+lee%40email.com" | url_decode }}
reach me at lee@email.com

The base64 filter (available in some Liquid flavors) can also be used to encode strings:

{{ "I love dogs." | base64 }}
SSBsb3ZlIGRvZ3Mu

Hashing  

Hashing transforms a value in a way that guarantees its integrity. Stated another way, it allows us to check and see if two items are the same. Its most well-known application is passwords. 

Since storing passwords would present a huge security risk, typically passwords themselves are not stored in a database. Instead, they are hashed, and the resulting hash is stored. For example, when you create an account on a website, the password you set isn’t sent to the website—an algorithm transforms it into a hash, and that’s what’s actually stored. The next time you log in, the password you enter is hashed and compared to the stored hash of the password you set up initially—if they match, you entered the right password! 

Liquid offers several filters that can hash a value: sha1, sha256, and md5. Each represents a different hash algorithm. Here they are in action:

{{ "Customer.io" | md5 }}
d52b6a207bf5255c05b1d0056230617e
{{ "Customer.io" | sha1}}
c197ff0ae0a41983362f35ca972c544061c54d4c
{{ "Customer.io" | sha256 }}
6dddb773238216bce273133bc3f6a12a824f41dd184d09452f05c7659dae7d57

Tricky terminology note: In Ruby (Liquid’s parent language), the word “hash” is also used to mean a collection of keys—the rough equivalent of a JavaScript object. See the For Loops lesson for a brief discussion of hashes in that context.

Hashing in encryption schemes   

Encrypting transforms a value in a way that guarantees confidentiality. That is, access is restricted to authorized users. A cryptographic key is required to encrypt the value, and a different cryptographic key is required to decrypt it. (“Key” here means a cryptographic key, not a Liquid key.)

While we don’t actually use Liquid to encrypt data, some encryption schemes will use hashing to verify integrity as one of the many steps in the encryption process. Hash-based message authentication codes, or HMACs, include both a hash function and a secret cryptographic key. You’ll know when to use this type of hashing, because the instructions you’re following will explicitly state it is required—here’s an example about creating the signature needed to pass data to the Twitter API.  

Here’s a quick look at HMAC hash filters:

{{ "Customer.io" | hmac_sha1: "some_key" }}
2bdf556c9a75766f258d1e2824f6d0e31d1beedc
{{ "Customer.io" | hmac_sha256: "some_key" }}
6dddb773238216bce273133bc3f6a12a824f41dd184d09452f05c7659dae7d57

Those are just the basics of how encoding and hashing are done using filters in Liquid. There’s a lot more to know, so start with this Auth0.com article for a deeper dive if you’ll be working with these filters frequently. 


Up Next: Advanced Liquid – For Loops

Whitespace Control

Alex Patton

Nothing gives away badly coded emails like extra spaces where they don’t belong. That’s where whitespace control comes in! You learned in Liquid Fundamentals that you can use filters to manage unwanted whitespace in your output. Most Liquid flavors will offer the lstrip, rstrip, and strip filters, which take out whitespace to the right or left (or both the right and left at once) of your output.

Another way to manage whitespace is simply by adding hyphens to your notation (aka, the characters with which you enclose your code). You can do this with either a key or a tag. Here’s what the notation looks like for a key if you want to remove any whitespace at the beginning and end of your output: 

{{- customer.first_name -}}

You can also just use one hyphen on either the left or right side of your notation if you just want to strip out whitespace at the beginning or end of your output, like this:

{{- customer.first_name }}
{{ customer.first_name -}}

Voila! A handy way to make sure you don’t have unwanted whitespace without needing to use a strip filter

You can do this exact same trick with the notation for tags. This is especially handy for ensuring you don’t have extra blank lines in your output. Some Liquid flavors (including Shopify’s) will print a blank line for each line of Liquid code, even those that don’t output text (like most tags). That can add many extra lines in your email. You can avoid the problem by adding a hyphen to the right-hand notation of your tags, like this:

{% assign my_variable = "Good dog!" -%}{{ my_variable }}
Good dog!

Experiment with your flavor of Liquid before adding hyphens to your code. Not all flavors add extra line breaks, so you might be making extra work for yourself if you don’t check first!


Up Next: Advanced Liquid – Encoding, Hashing, and Encryption Schemes

Date, Time, and Localization

Alex Patton

Marketing emails frequently work with dates and tailor content to recipients’ regions. For example, when a new customer makes a purchase, you might send a discount code that expires in 10 days. You might also convert times to a customer’s time zone and convert currencies and number formats (like 12,000.00 vs 12 000,00) based on a customer’s region.

Depending on the flavor of Liquid you’re using, you’ll likely have a variety of filters that address most or all of these needs. We’ll give you an overview of common scenarios, and then you can explore your Liquid flavor’s documentation to learn what your options are.

The date filter

The date filter converts a timestamp value to a readable format. Timestamps are characters or encoded data that identify a date and time (like when your customer created an account or made a purchase), sometimes down to fractions of a second. 

Quick sidebar: timestamps are commonly stored in one of two formats. The first is a Unix timestamp, in which time is expressed as the milliseconds that have passed since January 1, 1970. This type of value is a number (specifically, an integer). The second format commonly used for timestamps is ISO 8601—also known as Universal Time Code (UTC) format. This type of value is a string. The UTC format is handy if you need to represent a date before 1970 (like if you want to represent a birth date for someone over 51!).

In either case, the output of a timestamp value is not at all human-friendly to read. That’s where the date filter comes in! Here’s a simple example using a orders.order_placed_at key, in which the value of that key is 1625079104—a Unix timestamp.

{{ orders.order_placed_at | date: "%D"}}
30/06/2021

The value of the orders.order_placed_at key is 1625079104, and we’ve applied the date filter to output a date that’s legible to a reader. 

The date filter is extremely flexible, offering a wealth of options for how your date will be displayed. You just need to include formatting instructions in the filter. In the example above, the instructions are “%D”, which displays the date in DD/MM/YYYY format—which happens to be a common format for dates in European countries.

But maybe you’re emailing someone in the US! Or maybe you want to include the day of the week, or even very specific details about the time—it’s all possible! You can see a full list of available date format instructions here (scroll down to the section titled “rfc822(date)” to see the list)—or make your life a lot easier by using this STRFTIME resource to generate the formatting instructions you want.

Let’s apply a date filter with more complex formatting instructions:

{{ orders.order_placed_at | date: "%A, %b. %d, %Y %I:%M %p %Z" }}
Monday, Jun. 30, 2021 11:51 AM Pacific Daylight Time

Woo, that’s a super detailed output—all we have to do is include the formatting instructions in the order we want things displayed, with the punctuation and spaces we want. That’s important: put the punctuation and spaces in the code; they will appear exactly as entered in your output.

See for yourself how the magic is done. Head over to the STRFTIME resource and see if you can generate the code and recreate the output above. 

Fun fact: if your timestamp is in ISO 8601 instead of Unix, you can convert it by using “date: %s” in the code of your filter; check out the “seconds since 1970” button at the STRFTIME resource to see it happen.

Practice problem 

Now it’s your turn to practice with the date filter

Format your timestamp like this: Monday, 21 June 2021. Feel free to use the STRFTIME resource to compose your instructions for the date filter.

Choose the right Liquid code from the options below:

That’s not right, try again!

The correct answer is A! If you didn’t use the STRFTIME resource to compose your instructions for the date filter, go there now to experiment with the options.

Let’s run the code: 

{{ orders.order_placed_at | date: "%A, %e %B %Y" }}
Monday, 30 June 2021

Why don’t the other options work?

B. The comma after %A is missing. The output would be “Monday 21 June 2021”.

C. The letter case is wrong. Capitalization changes the meaning of this code: for example, %A is the full month name, and %a is the abbreviated month name. The output would be “Mon, 21 Jun 21”.

D. The “date:” portion of the filter is missing.

Using the now key

Liquid has another handy tool for outputting dates: the now key (depending on the flavor of Liquid you’re using, it might also be called the today key). The value of the now key is always the current date and time. Used with the date filter, now will output the current time, like this: 

{{ 'now' | date: "%A, %b. %d, %Y %I:%M %p %Z" }}
Monday, June 30, 2021 11:51 AM Pacific Daylight Time

Keep in mind that, as with all Liquid code, the value of the now key will be the time at which the code is actually rendered—for an email, that means the time your platform creates the email, not the time your reader opens the email. You might want to check out the Countdown Timers section of this tutorial on special content in HTML emails for more options.  

Other date and time filters

Some Liquid flavors have filters that add or subtract time increments to a given timestamp, which can be helpful for dynamically calculating new dates. Let’s say someone signed up for an annual subscription, and you want to let them know the date it will expire. Here’s how you can do that using Customer.io’s add_year filter:

{{ orders.order_placed_at | add_year: 1 }}
Monday, June 30, 2022 11:51 AM Pacific Daylight Time

Note that this particular filter requires that your timestamp is a Unix timestamp; check out the documentation for your flavor of Liquid for any necessary details like that for the filters you’re using. 

Localization tools

Many Liquid flavors have tools that convert currency, number format, or other elements to match the customer’s region. Using a localization filter or adding a localization parameter (essentially, extra instructions) to a filter is one way to accomplish this. 

Let’s use Customer.io’s currency filter as an example. Heads up: for this example, we’re using the assign tag to create a new key and value (that’s the first line of code below) to make it easier to see how this currency filter works. You learned in Intermediate Keys that you can use the assign tag to create keys and store their values right in your Liquid code.

{% assign my_money = "123456.78 " %}
{{ my_money | currency }}
$123,456.78

The second line of code shows the currency filter. This filter defaults to US dollars, with a comma as the thousands separator and a period as the decimal separator. 

But what if you have a global audience? That’s where localization parameter comes in. Let’s use that same example, but this time add a parameter to the currency filter indicating that our recipient is in France. Take a look at the second line of code: 

{% assign my_money = "123456.78 " %}
{{  my_money | currency: "fr" }}
123 456,78€

Now the currency displayed is euros, with a space as the thousands separator and a comma as the decimal separator—the correct currency and number format for that region. Et voila! 

Take some time to look through your Liquid flavor’s documentation to learn how to manage dates, times, currency, and other localization options! It’s a great opportunity to make your emails more personalized and more engaging—and that means more clicks.


Up Next: Advanced Liquid – Whitespace Control

Advanced Liquid

Alex Patton

Just got here? This module is part of a full-length Liquid tutorial. Start from the beginning.  

In this module, we’ll cover:

  • Date, time, and localization
  • Whitespace control
  • Encoding, hashing, and encryption schemes
  • For loops
  • Using Liquid in Customer.io

Now that you’re familiar with Liquid’s three parts—keys, filters, and tags—and you can put them all together to create ultra-personalized content, we’ll dive into a few special topics. This section is an opportunity to get your feet wet in an environment more like what you’ll encounter in the wild world of Liquid!


Up Next: Advanced Liquid – Date, Time, and Localization

Truthy and Falsy

Alex Patton

You may have been expecting Stephen Colbert in this section—nope, it’s still us, here with the truth about truthiness in Liquid. 

Liquid sometimes deals with non-Boolean values (remember, Boolean means true or false) in Boolean contexts like conditional tags. That means a tag might have to evaluate a string, a number, an array, a nil value, or an EmptyDrop value to answer a true or false question. We can’t accurately say those values are true or false (because they aren’t Boolean), so we call them truthy and falsy. 

Liquid considers all value types truthy except nil and false (for Booleans). This can lead to some unexpected results. Let’s say we’re writing a personalized email salutation, and we want to include an alternate greeting if we don’t know the customer’s name. You could write the code like this:

{% if customer.name %}
Hi {{ customer.name }}
{% else %}
Hi friend!
{% endif %}

It’s possible that this code will work just fine—if you don’t have a name for the customer, Liquid will output the second greeting. But if the customer.name key’s value is a special string type called an “empty string,” you’re going to get some funky results. Why? Because Liquid considers all strings—including empty ones—to be truthy. Here’s what the output would look like:

Hi

Not only are you missing the customer’s name, your alternate text hasn’t appeared either, because you actually do know the customer’s name! It’s… empty string.

To avoid the empty string problem, you can use blank. (You’ll remember this example from way back in the How Liquid Works Lesson!) Here’s how it looks:

{% if customer.name != blank %}
Hi {{ customer.name }}
{% else %}
Hi friend!
{% endif %}

You’ve now got a solid basis for using tags along with filters and keys to do all kinds of cool things with Liquid! Now let’s get into some of the more complex issues.


Up Next: Advanced Liquid

Operators and Order of Operations

Alex Patton

We recapped the operators earlier; let’s look closer at some of the trickier ones. Most of the operators used with tags are fairly intuitive: == means equals, and contains means… well, that the string contains something! However, it’s helpful to have a deeper understanding of operators and how they work when using conditional tags.

Operators in conditional tags

Operators tell Liquid to do comparisons to determine whether conditions are met in conditional tags. Here’s a simple example using the if tag and the customer.purchased.most_recent key, the value of which is the last product the customer purchased:

{% if customer.purchased.most_recent == "leash" %}
Thank you for your recent purchase of a leash!
{% endif %}

To evaluate the first line of code, Liquid looks at the value of the customer.purchased.most_recent key and then compares it to “leash.” Since you’ve used the == operator, Liquid checks to see if they are the same. If they are, Liquid outputs your copy! If they aren’t, Liquid outputs nothing.

Using and and or operators to do multiple comparisons

To do multiple comparisons, you can use the and and or operators, like this: 

{% if customer.purchased.most_recent == "leash" or "collar" %}
Now that your good boy has a {{ customer.purchased.most_recent }}, consider buying an ID tag!
{% endif %}

Here, Liquid is comparing the or statement with the key’s value; if they match, it will output the copy. If not, nothing happens. 

Liquid evaluates all and and or statements in pairs. With or, if either the left or the right side matches the key’s value, the condition is met. For and, the condition is not met unless both the left and the right side match the key’s value

This is simple and intuitive when you’re dealing with just one and or or operator. However, Liquid allows you to string together multiple pairs to create complex conditionals. 

Multiple and and or operators

In tags with more than one and or or, Liquid reads the pairs from right to left. This can be some tricky business to remember—it’s the opposite of how Liquid reads filters, and it’s not what we learned in math class about order of operations! 

Let’s look at an example. The customer.purchased key’s value is an array of a customer’s purchases: “leash”, “collar”. Here’s the code:

{% if customer.purchased == "bowl" or "leash" and "collar" %}
Click to see our selection of ID tags and bowl elevators!
{% endif %}

Liquid will read it like this: 

  1. Read the first pairs, starting on the right (leash, collar): Does the key’s value contain leash and collar? Yes.
  1. Go to the next pair to the left (bowl, leash): Does the key’s value contain bowl or leash? Yes.
  1. Are there any more and or or operators? No.
  1. The condition is met because all the and and or pairs matched with the key’s value.

Practice problem: multiple and and or pairs

Let’s practice using multiple and or statements. 

We’ll use the customer.purchased key. For this problem, its value is: “bowl elevator”, “vitamins”, “brush”, “shampoo”, “leash”. 

Look at the code below and choose which output will result. 

{% if customer.purchased contains "shampoo" and "brush" or "collar" and "vitamins" %}
Keep your pet healthy with gentle exercise!
{% endif %}


That’s not right; try again!

The correct answer is B! 

Let’s run the code (in slow motion):

  1. Read the first pair, starting from the right (collar, vitamins): Does the key’s value contain collar and vitamins? No.
  1. Read the next pair (brush, collar): Does the key’s value contain brush or collar? Yes.
  1. Read the last pair (shampoo, brush): Does the key’s value contain shampoo or brush? Yes.
  1. The condition is not met because the first pair did not match the key’s value.

Up Next: Intermediate Tags – Truthy and Falsy

Our mailing list is chock full of tactical lifecycle marketing how-tos, interviews with trending internet businesses, and product updates.