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.
Name personalization
Use these patterns to keep greetings human—even when subscriber data is incomplete.
First name or fallback
rubySimple fallback when first name is missing.
{{ visitor.first_name | default: "Friend" }}First name, else last name, else default
rubyCascading fallback logic for better personalization.
{{ visitor.first_name | default: visitor.last_name | default: "friend" }}Confirmation emails
Ensure order confirmations, receipts, and status updates work even when optional fields are missing.
Order confirmation with order ID
rubyDisplay 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
rubyProvide 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
rubyShow 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 helpers
Create subject lines, greetings, and CTAs that gracefully fall back without breaking tone or grammar.
Friendly subject line
rubyAdd a personalized subject line while keeping a fallback.
Subject: Your guide is ready, {{ visitor.first_name | default: "there" }}!Greeting with company name fallback
rubyBlend 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
rubyDisplay the active plan and fall back to a safe default.
You're on the {{ visitor.custom_fields.plan_name | default: "Free Plan" }}.Product recommendations
Suggest products based on purchase history, categories, or seasons—gracefully falling back when data is missing.
Category-based recommendations
rubyShow 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
rubySuggest 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
rubyBlend seasonal messaging with the visitor’s data.
{{ visitor.first_name | default: "Friend" }}, our {{ visitor.custom_fields.season | default: "spring" }} collection is here!Links
Links are the workhorse of any email template. The Liquid helper accepts the destination URL and the label you want subscribers to click.
| Name | Type | Description |
|---|---|---|
| url | URL string | Destination URL triggered when the subscriber clicks. |
| hyperlink | Label text | Text displayed to the subscriber. |
Liquid Code
ruby{{ "http://example.com" | hyperlink: "Click here!" }}HTML Result
html<a href='http://example.com'>
Click here!
</a>Preview: Click 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:
Identity helpers
Personalize intros and identity-driven content without manually stitching together name parts or avatars.
Greeting
rubyUse 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
htmlHi Jesse,Preview: Hi Jesse,
Formatted name
rubyGenerate a visitor’s full name from stored fields with punctuation and spacing handled for you.
Liquid Code
ruby{{ visitor.pretty_name }}HTML Result
htmlJohn DoePreview: John Doe
Gravatar avatar
rubyRender 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:
John
Operators & conditionals
Gate sections of your template by tags, location, order history, or any other Liquid-friendly field.
If conditional
rubyDisplay 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
rubyBranch 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
rubySupport 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
rubyAdds the specified string to the end of another string.
// Liquid Filter
{{ "John" | append: visitor.last_name }}
John DoeCapitalize
rubyCapitalizes 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 titleDefault
rubySupplies 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 }}
falseDivide By
rubyDivides 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.857142857142857Downcase
rubyConverts every character in a string to lowercase.
// Liquid Filter
{{ "Bento Now" | downcase }}
bento now
{{ "apple" | downcase }}
appleMinus
rubySubtracts a number from another number.
// Liquid Filter
{{ 4 | minus: 2 }}
{{ 16 | minus: 4 }}
{{ 183.357 | minus: 12 }}
2
12
171.357New Line Br
rubyReplaces 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
rubyChooses 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 ticketsPlus
rubyAdds a number to another number.
// Liquid Filter
{{ 4 | plus: 2 }}
{{ 16 | plus: 4 }}
{{ 183.357 | plus: 12 }}
6
20
195.357Prepend
rubyAdds the specified string to the beginning of another string.
// Liquid Filter
{% assign first_name = "John" %}
{{ "Doe" | prepend: first_name }}
John doeRemove
rubyRemoves 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
rubyReplaces 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
rubyRounds 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.36Truncate
rubyShortens 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 onUpper
rubyConverts every character in a string to uppercase.
// Liquid Filter
{{ "Bento" | upcase }}
BENTOHelpers 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
rubyProvide 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
rubyLet 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
rubySurface 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
htmlApple 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
rubyLoop 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
htmlApple 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
rubyGenerate 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
html100OFFPreview: 100OFF
Stripe coupon
rubyCreate 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
htmlTESTPreview: TEST
Stripe billing portal link
rubyGenerate 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
htmlhttps://bentonow.comPreview: 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