Liquid Template Guide

Build flexible Bento templates with Liquid helpers that personalize content, generate dynamic blocks, and stay resilient when data is missing.

Picture crafting an email template that adapts to every subscriber automatically. Liquid gives you that power with a lightweight syntax: greet visitors by name, surface personalized offers, or render entire carts without duplicating templates. This guide walks through Bento’s most useful helpers, filters, and data references so you can move fast without sacrificing polish.

The basics

Start with the core helpers below. They cover names, transactional fallback logic, and quick marketing wins while staying readable for anyone who inherits the template later.

Personalization

Name personalization

Use these patterns to keep greetings human—even when subscriber data is incomplete.

First name or fallback

ruby

Simple fallback when first name is missing.

{{ visitor.first_name | default: "Friend" }}

First name, else last name, else default

ruby

Cascading fallback logic for better personalization.

{{ visitor.first_name | default: visitor.last_name | default: "friend" }}
Transactional

Confirmation emails

Ensure order confirmations, receipts, and status updates work even when optional fields are missing.

Order confirmation with order ID

ruby

Display confirmation details only when an order ID exists.

{% if visitor.custom_fields.order_id %}
  Your order #{{ visitor.custom_fields.order_id }} is confirmed!
{% endif %}

Download link for digital products

ruby

Provide conditional download links for digital purchases.

{% if visitor.custom_fields.product_type == "digital" %}
  Download here: {{ visitor.custom_fields.download_url }}
{% endif %}

Trial period days left

ruby

Show remaining trial days with sensible fallback messaging.

{% if visitor.custom_fields.trial_days_left %}
  {{ visitor.custom_fields.trial_days_left }} days left in your trial
{% else %}
  Your trial has ended
{% endif %}
Marketing

Marketing helpers

Create subject lines, greetings, and CTAs that gracefully fall back without breaking tone or grammar.

Friendly subject line

ruby

Add a personalized subject line while keeping a fallback.

Subject: Your guide is ready, {{ visitor.first_name | default: "there" }}!

Greeting with company name fallback

ruby

Blend personal names with company data when needed.

{% if visitor.first_name %}
  Hey {{ visitor.first_name }},{% else %}Hello from {{ visitor.company_name | default: "our team" }},
{% endif %}

Plan name with default

ruby

Display the active plan and fall back to a safe default.

You're on the {{ visitor.custom_fields.plan_name | default: "Free Plan" }}.
Recommendations

Product recommendations

Suggest products based on purchase history, categories, or seasons—gracefully falling back when data is missing.

Category-based recommendations

ruby

Show products based on the last purchased category.

{% if visitor.custom_fields.last_purchased_category %}
  Because you like {{ visitor.custom_fields.last_purchased_category }}, check out these new arrivals!
{% else %}
  Check out our featured products!
{% endif %}

Purchase history fallback

ruby

Suggest items using purchase history or promote popular picks.

{% if visitor.custom_fields.last_purchase_date %}
  It's been a while since your last order on {{ visitor.custom_fields.last_purchase_date }}. Ready for something new?
{% else %}
  Discover products perfect for you!
{% endif %}

Seasonal promotion with personalization

ruby

Blend seasonal messaging with the visitor’s data.

{{ visitor.first_name | default: "Friend" }}, our {{ visitor.custom_fields.season | default: "spring" }} collection is here!

Button

Buttons share the same syntax as links but add visual weight so calls to action stand out. Supply the URL, visible text, and an optional HEX color.

Name Type Description
url URL string Destination URL triggered when the button is pressed.
hyperlink Label text Copy displayed on the button.
color HEX code Overrides the default background color. Learn more at htmlcolorcodes.com or generate palettes at uicolors.app.

Liquid Code

ruby
{{ "http://example.com" | button: "Click here!", "#999" }}

HTML Result

html
<a class='button'
   href='http://example.com'
   style='border-color:#999!important;
          background:#999!important;
          background-color:#999!important;'
>
  Click here!
</a>

Preview:

Audio

Embed audio for clients that support playback and provide a graceful fallback link for those that do not.

Name Type Description
url URL string Public URL to the audio asset.
type string Set to `audio` so Bento outputs the correct HTML structure.

Liquid Code

ruby
{{ 'https://app.bentonow.com/sounds/click.mp3' | audio_tag }}

HTML Result

html
<audio controls src='https://app.bentonow.com/sounds/click.mp3'>
  <p style='opacity:0.5;font-size:0.75em;'>
    If your email client does not support playing audio
    <a href='https://app.bentonow.com/sounds/click.mp3'>
      click here to listen.
    </a>
  </p>
  <source src='https://app.bentonow.com/sounds/click.mp3'></source>
</audio>

Preview:

Images

Display hosted images with optional class names, IDs, alt text, and dimensions so templates remain responsive.

Name Type Description
url URL string Public URL to the image asset.
image_tag string Comma-separated options: CSS classes, ID, alt text, width (px), height (px), and an optional boolean for constrain proportions.

Liquid Code

ruby
{{ 'https://app.bentonow.com/brand/bento-logo-3d.png' | img_tag: "class name", "id", "alt", 100, 200, false }}

HTML Result

html
<img src="https://app.bentonow.com/brand/bento-logo-3d.png"
     class="class name"
     id="id"
     alt="alt"
     width="100"
     height="200"
/>

Preview: Bento logo

Identity helpers

Personalize intros and identity-driven content without manually stitching together name parts or avatars.

Greeting

ruby

Use Bento’s default greeting helper to open messages with a friendly tone. It automatically picks the best available name.

Liquid Code

ruby
{{ visitor.greeting }}

HTML Result

html
Hi Jesse,

Preview: Hi Jesse,

Formatted name

ruby

Generate a visitor’s full name from stored fields with punctuation and spacing handled for you.

Liquid Code

ruby
{{ visitor.pretty_name }}

HTML Result

html
John Doe

Preview: John Doe

Gravatar avatar

ruby

Render a visitor’s Gravatar based on their email address with a sensible placeholder if none exists.

Liquid Code

ruby
{{ visitor.email | profile_tag: visitor.pretty_name.first }}

HTML Result

html
<span class=''>
  <img src="https://gravatar.com/avatar/740cc1919c09dfe40a11772accd61ed1.png?d=mp"
       alt="bento_docs_feedback@bentonow.com"
       style='display:inline-block;
         height:1em;
         width:1em;
         border-radius:100%;
         margin:-1px;
         margin-right:5px;
         height: 1em;
         width: 1em;
         margin: 0 .05em 0 .1em;
         vertical-align: -0.1em;
         height: 1.25em;
         width: 1.25em;
         margin: 0rem;
         vertical-align: -0.25em;
       '
  />
  John
</span>

Preview: bento_docs_feedback@bentonow.comJohn

Operators & conditionals

Gate sections of your template by tags, location, order history, or any other Liquid-friendly field.

If conditional

ruby

Display content when a visitor meets a single condition—perfect for toggling copy based on tags or fields.

Liquid Code

ruby
{% if visitor.tags contains "customer" %}
This visitor has a 'customer' tag.
{% endif %}

HTML Result

html
<!-- if the subscriber has the tag they see:-->
This visitor has a 'customer' tag.
<!-- if they do not they see nothing-->

Preview: This visitor has a 'customer' tag.

If/Else conditional

ruby

Branch copy to show different content when the condition passes or fails. Great for geographic or lifecycle personalisation.

Liquid Code

ruby
{% if visitor.city contains "New York" %}
You are in New York!
{% elsif visitor.city contains "Sydney" %}
You are in Sydney!
{% else %}
You are on earth!
{% endif %}

HTML Result

html
<!-- if the subscriber's city is New York they see:-->
You are in New York!
<!-- if the subscriber's city is Sydney they see:-->
You are in Sydney!
<!-- if the subscriber's city is neither they see:-->
You are on earth!

Preview: You are in Sydney!

Operators

ruby

Support numeric comparison and math inside conditionals. Reward frequent purchasers, high spenders, or any numeric threshold.

Liquid Code

ruby
{% if visitor.order_count >= 10 %}
This visitor has ordered over 10 times!
{% endif %}

HTML Result

html
<!-- if the subscriber has ordered over 10 times they see: -->
You are in New York!
<!-- otherwise they see nothing. -->

Preview: This visitor has ordered over 10 times!

Visitors reference

Drop these Liquid tags anywhere to personalize content with visitor-level metadata.

Liquid tag Result
{{ visitor.email }} test@example.com
{{ visitor.confirmation_url }} Bento confirmation URL
{{ browser_url }} Current browser URL
{{ visitor.unsubscribe_url }} Subscriber unsubscribe URL
{{ visitor.latest_broadcast_url }}
{{ visitor.snooze_url }}
{{ visitor.test_group }} control
{{ visitor.city }} Sydney
{{ visitor.country }} Australia
{{ visitor.is_bot }} `true` / `false`
{{ visitor.time_of_day }}
{{ visitor.tags }} Subscriber tags array
{{ visitor.gender }} Male
{{ visitor.path }}
{{ visitor.feedback }}
{{ visitor.first_name }} John
{{ visitor.last_name }} Doe

Liquid filters

Filters transform the values you pass into Liquid helpers—truncate copy, reformat numbers, or manipulate strings on the fly. Review the quick links, then dive deeper into each example.

Append

ruby

Adds the specified string to the end of another string.

// Liquid Filter
{{ "John" | append: visitor.last_name }}

John Doe

Capitalize

ruby

Capitalizes the first character of a string and lowercases the remaining characters.

// Liquid Filter
{{ "quick Brown Fox." | capitalize }}

Quick brown fox.

// Only the first character of a string is capitalized, so later words are not capitalized:
{{ "my GREAT title" | capitalize }}

My great title

Default

ruby

Supplies a fallback value when the input is `nil`, `false`, or empty. Pass `allow_false: true` to respect boolean false.

// Liquid Filter
// In this example, product_price is not defined, so the default value is used.
{{ product_price | default: 2.99 }}

2.99

// To allow variables to return false instead of the default value,
// you can use the `allow_false` parameter.
{{ display_price | default: true, allow_false: true }}

false

Divide By

ruby

Divides a number and returns the same type as the divisor (integer vs float).

// Liquid Filter
{{ 16 | divided_by: 4 }}
{{ 5 | divided_by: 3 }}

4
1

// divided_by produces a result of the same type as the divisor — that is, if you divide by an integer, the result will be an integer. If you divide by a float (a number with a decimal in it), the result will be a float.
{{ 20 | divided_by: 7.0 }}

2.857142857142857

Downcase

ruby

Converts every character in a string to lowercase.

// Liquid Filter
{{ "Bento Now" | downcase }}

bento now

{{ "apple" | downcase }}

apple

Minus

ruby

Subtracts a number from another number.

// Liquid Filter
{{ 4 | minus: 2 }}
{{ 16 | minus: 4 }}
{{ 183.357 | minus: 12 }}

2
12
171.357

New Line Br

ruby

Replaces newline characters with `<br />` elements.

// Liquid Filter
{% capture string_with_newlines %}
Hello
there
{% endcapture %}

{{ string_with_newlines | newline_to_br }}

Hello<br />
there<br />

Pluralize

ruby

Chooses between singular and plural labels based on the provided count.

// Liquid Filter
You purchased {{ count | pluralize: 'ticket', 'tickets' }}

// count = 1
You purchased 1 ticket

// count = 2+
You purchased 2 tickets

Plus

ruby

Adds a number to another number.

// Liquid Filter
{{ 4 | plus: 2 }}
{{ 16 | plus: 4 }}
{{ 183.357 | plus: 12 }}

6
20
195.357

Prepend

ruby

Adds the specified string to the beginning of another string.

// Liquid Filter
{% assign first_name = "John" %}
{{ "Doe" | prepend: first_name }}

John doe

Remove

ruby

Removes every occurrence of the provided substring.

// Liquid Filter
{{ "The apple fox jumped apple over the apple fence." | remove: "apple" }}

The fox jumped over the fence.

Replace

ruby

Replaces each occurrence of the first argument with the second.

// Liquid Filter
{{ "The fox jumped over my fence." | replace: "my", "your" }}

The fox jumped over your fence.

Round

ruby

Rounds a number to the nearest integer or to a specific decimal precision.

// Liquid Filter
{{ 1.2 | round }}
{{ 2.7 | round }}
{{ 183.357 | round: 2 }}

1
3
183.36

Truncate

ruby

Shortens a string to a fixed length and appends an ellipsis (or custom ending).

// Liquid Filter
{{ "The fox jumped over the fence." | truncate: 18 }}

The fox jumped ov…

// truncate takes an optional second argument that specifies the sequence of characters to be appended to the truncated string. By default this is an ellipsis (), but you can specify a different sequence.
{{ "The fox jumped over the fence." | truncate: 20, ", and so on"  }}

The fox jumped over and so on

Upper

ruby

Converts every character in a string to uppercase.

// Liquid Filter
{{ "Bento" | upcase }}

BENTO

Helpers reference

Utility helpers expose environment flags, hashing utilities, time math, currency formatting, and light personalization tricks.

Environment variables

Check feature flags and integration availability directly inside your templates.

Helper Result
{{ ENV.webhooks }} `true` / `false`
{{ ENV.laravel }} `true` / `false`
{{ ENV.block_form_tracking }} `true` / `false`

Encryption & hashing

Hash or sign values without storing them—handy when you need to obfuscate identifiers or avoid shipping PII in plain text.

Helper Result
{{ "secret_string" | md5 }} 312f946cb92aaa541723382cda411c31
{{ "your secret string" | hmac_sha256: "key" }} 63fa337d0a0e6520a12e223657d17184a5e2b131

Time helpers

Batch scheduling, countdowns, and regional delivery windows all rely on time-aware helpers.

Helper Result
{{ "2010-10-31" | advance_date_to_next: "monday" }} 2010-11-01 00:00:00 +0000
{{ "2024-12-31" | days_until }} 116
{{ "2024-12-31" | weeks_until }} 16
{{ "2022-12-31" | weeks_since }} 87
{{ "2022-12-31" | days_since }} 614
{{ "2022-12-31" | at_midnight }} 2022-12-31 00:00:00 +0000
{{ "2010-10-31" | in_time_zone: "America/Denver" | date: "%Y-%m-%d %H-%M-%S %z" }} 2010-10-31 00-00-00 -0600
{{ "2010-10-31" | timestamp }} 1288483200
{{ visitor.created_at | time_ago_in_words }} about 2 months

Money formatting

Respect the currency assigned to the visitor automatically.

Helper Result
{{ money.formatted }} $900.00 USD

Lottery element

Pick a random item from a delimited list—perfect for rotating offers or headlines.

Helper Result
{{ "Offer One||Offer 2||Offer 3" | split: "||" | pluck_at_random }} Offer 3

Broadcast helpers

These helpers only resolve inside broadcasts and surface metadata about the send.

Helper Result
{{ subject }} Broadcast subject
{{ name }} Broadcast name
{{ created_at }} Broadcast created timestamp

Sequence helpers

These helpers only resolve inside Bento sequences. Use them to provide alternate opt-outs or skip links.

Cancel the sequence

ruby

Provide an alternate opt-out that stops the current sequence without unsubscribing the visitor entirely.

Liquid Code

ruby
{{ sequence.cancel_sequence_url | hyperlink: "No more emails like this!" }}

HTML Result

html
<a href='http://app.bentonow.com/'>
  No more emails like this!
</a>

Preview: No more emails like this!

Skip forward to the next email

ruby

Let subscribers jump ahead in the sequence and optionally redirect them to a follow-up page on completion.

Liquid Code

ruby
// Skip only
{{ sequence.fast_forward_url | hyperlink: "Instantly get the next email." }}

// Skip and redirect
{{ sequence.fast_forward_url | append: "&redirect=https://bentonow.com/skip" | hyperlink: "Instantly get the next email with redirect." }}

HTML Result

html
<!-- Skip only -->
<a href='http://app.bentonow.com'>
  Instantly get the next email.
</a>
<!-- Skip and redirect -->
<a href='http://app.bentonow.com?&redirect=https://bentonow.com/skip'>
  Instantly get the next email with redirect.
</a>

Preview: Instantly get the next email.
Instantly get the next email with redirect.

Ecommerce helpers

Render carts, catalog listings, and checkout URLs when you need commerce-aware sequences.

Render last cart

ruby

Surface the most recent cart contents to nudge visitors back toward checkout.

Liquid Code

ruby
{% assign cart = visitor | current_cart %}
{% for item in cart %}
{{ item.product_name }} - {{ item.product_price }} - {{ item.product_quantity }}
{% endfor %}

HTML Result

html
Apple Watch Ultra 2 - 799.00 USD - 1
49mm Indigo Alpine Loop - 99.00 USD - 1
49mm Orange Ocean Band - 99.00 USD - 1

Preview: Apple Watch Ultra 2 - 799.00 USD - 1
49mm Indigo Alpine Loop - 99.00 USD - 1
49mm Orange Ocean Band - 99.00 USD - 1

Render products

ruby

Loop over the Bento `products` collection to build merchandising sections inside flows.

Liquid Code

ruby
{% for product in products %}
{{ product.product_name }} - {{ product.product_price }} - {{ product.product_quantity }}
{% endfor %}

HTML Result

html
Apple Watch Ultra 2 - 799.00 USD - 1
49mm Indigo Alpine Loop - 99.00 USD - 1
49mm Orange Ocean Band - 99.00 USD - 1

Preview: Apple Watch Ultra 2 - 799.00 USD - 1
49mm Indigo Alpine Loop - 99.00 USD - 1
49mm Orange Ocean Band - 99.00 USD - 1

Abandoned cart checkout URL

Generate a direct link back to the visitor’s in-progress checkout.

Helper Result
{{ abandoned_checkout_url | default: "https://example.com/cart" }} Checkout URL string

Stripe & Shopify helpers

Generate coupon codes or billing links on demand. Perfect for nurture flows or transactional follow-ups.

Shopify coupon

ruby

Generate a unique Shopify coupon code on demand.

Name Type Description
coupon name string Identifier for the new coupon.
discount amount string Amount to deduct (e.g. `-10` for $10 off).
discount type string Use `fixed_amount` or `percentage`.

Liquid Code

ruby
{{ visitor | shopify_coupon_generator: "100OFF", "-10", "fixed_amount" }}

HTML Result

html
100OFF

Preview: 100OFF

Stripe coupon

ruby

Create a Stripe coupon code with percent-based discounts.

Name Type Description
coupon name string Identifier for the coupon code.
discount % string Percent discount to apply.
length string Number of months the coupon remains active.

Liquid Code

ruby
{{ visitor | stripe_coupon_generator: "TEST", "10", 5 }}

HTML Result

html
TEST

Preview: TEST

Stripe billing portal link

ruby

Generate a visitor-specific Stripe billing portal URL.

Name Type Description
URL string Destination to redirect visitors after managing billing.

Liquid Code

ruby
{{ visitor | stripe_billing_portal: "https://bentonow.com" }}

HTML Result

html
https://bentonow.com

Preview: https://bentonow.com

External data

Pull in public data sources and loop through the results—RSS feeds, JSON APIs, YouTube channels, or even HTML snippets.

Name Type Description
URL string Source URL for the data feed.
type string Use `rss`, `fetch_json`, `youtube`, or `fetch_html` depending on the source.

RSS

ruby
{% assign posts = 'https://feedforall.com/sample.xml' | rss %}
{% for post in posts %}
{{ post.title }}
{% endfor %}

Youtube

ruby
{% assign videos = 'CHANNEL_ID i.e UC1XsdqFNRDsiB0ZYem_u4RQ' | youtube %}
{% for video in videos %}
{{ video.title }}
by {{ video.channel_name }} {{ video.link | button: "Click here!", "blue" }}
{% endfor %}

JSON

ruby
{% assign posts = 'https://jsonplaceholder.typicode.com/todos/' | fetch_json %}
{% for post in posts %}
{{ post.title }}
{% endfor %}

HTML

ruby
{% assign blocks = 'https://example.com' | fetch_html: 'ol', 'li' %}
{% for block in blocks %}
{{ block }}
{% endfor %}

HTML Result

html
<!-- Example shopping list response -->
Milk

Eggs

Bread

Preview: Milk
Eggs
Bread

Gmail promo annotation

Add Gmail promo annotations that highlight offers directly in the Promotions tab. Gmail ultimately decides whether to render the annotation, so treat it as a best-effort enhancement.

Name Type Description
promo string Primary annotation text, often the discount amount.
annotation string Supporting text, such as the coupon code or short detail.

Liquid Tag

ruby
{{ "20% OFF" | promotion_annotation: "TEST" }}

Need the original Markdown? Open raw file