# Dynamic Content

## Bird Email Template Language Documentation

### Introduction

The Bird Email Template Language is a powerful tool based on Shopify's Liquid syntax, with custom extensions designed specifically for email marketing. This language allows you to create dynamic, personalized email templates efficiently, enabling you to deliver targeted content to your subscribers.

Key features of the Bird Email Template Language include:

* Easy personalization using predefined variables
* Conditional content display based on subscriber attributes or segments
* Integration with your product catalog and external data sources
* Support for internationalization through translations
* Advanced formatting options with filters

This guide will walk you through the essential components of the language, providing examples and best practices to help you create effective email templates.

For a comprehensive reference on the underlying Liquid syntax, please refer to the [Shopify Liquid documentation](https://shopify.dev/docs/api/liquid).

### Predefined Variables

Predefined variables allow you to easily personalize your emails with recipient information and organization details.

#### Contact Variables

Use these to insert recipient-specific information into your emails:

```liquid
{{ contact.attributes.firstName }}
{{ contact.attributes.lastName }}
{{ contact.attributes.email }}
```

You can also use any custom attributes that you've defined for your contacts.

#### Organization Variables

Include your company information consistently across all emails:

```liquid
{{ organization.name }}
{{ organization.fullAddress }}
{{ organization.websiteUrl }}
{{ organization.street }}
{{ organization.city }}
{{ organization.state }}
{{ organization.country }}
{{ organization.zipCode }}
```

#### Email Address Variable

Reference the recipient's email address directly:

```liquid
{{ emailAddress }}
```

Best Practice: Always have a fallback for personalization variables in case they're not set for a particular recipient.

### Special Tags

Special tags provide quick access to common email marketing requirements, such as unsubscribe links and web views.

#### Unsubscribe Links

Essential for compliance with email regulations:

```liquid
{% unsubscribe %}  <!-- Outputs: <a href="...">Unsubscribe</a> -->
{% unsubscribe 'Click here to unsubscribe' %}  <!-- Custom text -->
<a href="{% unsubscribeLink %}">Manage your preferences</a>  <!-- Custom HTML -->
```

#### Web View Links

Provide an option to view the email in a web browser:

```liquid
{% webView %}  <!-- Outputs: <a href="...">View in browser</a> -->
{% webView 'View this email online' %}  <!-- Custom text -->
<a href="{% webViewLink %}">Trouble viewing? Click here</a>  <!-- Custom HTML -->
```

#### Date Tags

Useful for displaying current dates in your emails:

```liquid
Today's Date: {% currentYear %}-{% currentMonthName %}-{% currentDay %}
Sent on: {% currentWeekday %}
```

Best Practice: Always include an unsubscribe link in your emails, typically in the footer.

### Control Flow

Conditional logic and loops allow you to create dynamic content that adapts to each recipient's attributes or preferences.

#### If Statements

Use if statements to show different content based on recipient attributes:

```liquid
{% if contact.attributes.membership == "gold" %}
  <h2>Exclusive Gold Member Offer</h2>
  <p>Enjoy 20% off your next purchase!</p>
{% elsif contact.attributes.membership == "silver" %}
  <h2>Special Silver Member Discount</h2>
  <p>Get 15% off select items!</p>
{% else %}
  <h2>Limited Time Offer</h2>
  <p>Save 10% on your next order!</p>
{% endif %}
```

#### Checking Segment Membership

Target content to specific segments of your audience:

```liquid
{% if contact.segments contains "abcd-1234-efgh-5678" }
  This contact is a member of the segment with ID "abcd-1234-efgh-5678".
{% endif %}
```

#### Case Statements

```liquid
{% case shipping_method %}
  {% when 'ground' %}
    Estimated delivery: 5-7 business days
  {% when 'express' %}
    Estimated delivery: 2-3 business days
  {% else %}
    Please contact us for shipping information
{% endcase %}
```

#### Loops

Iterate over arrays of data to create dynamic lists:

```liquid
<h3>Your Favorite Colors:</h3>
<ul>
{% for color in contact.attributes.favoriteColors %}
  <li style="color: {{ color }};">{{ color }}</li>
{% endfor %}
</ul>
```

Best Practice: Use conditional logic to tailor your message to different audience segments, increasing relevance and engagement.

### Working with Products

Integrate your product catalog directly into your email templates for dynamic product displays.

#### Single Product Lookup

Display details of a specific product:

```liquid
{% catalog 'PRODUCT-ID-123' %}
  <div class="product">
    <h2>{{ catalogItem.title }}</h2>
    <img src="{{ catalogItem.imageUrl }}" alt="{{ catalogItem.title }}">
    <p>Price: {{ catalogItem.price | currencyFormat: catalogItem.currency }}</p>
    {% if catalogItem.originalPrice > catalogItem.price %}
      <p>Original Price: <strike>{{ catalogItem.originalPrice | currencyFormat: catalogItem.currency }}</strike></p>
    {% endif %}
    <a href="{{ catalogItem.url }}">Shop Now</a>
  </div>
{% endcatalog %}
```

#### Product Feed

Display multiple products from a feed:

```liquid
<h2>Recommended for You</h2>
<div class="product-grid">
  {% productfeed 'FEED-ID-456' %}
    {% for item in catalogItems limit:3 %}
      <div class="product">
        <h3>{{ item.title }}</h3>
        <img src="{{ item.imageUrl }}" alt="{{ item.title }}">
        <p>{{ item.price | currencyFormat: item.currency }}</p>
        <a href="{{ item.url }}">View Product</a>
      </div>
    {% endfor %}
  {% endproductfeed %}
</div>
```

Best Practice: Use product feeds to create dynamic content like "Recommended Products" or "New Arrivals" sections in your emails.

### Events

Leverage event data to create highly relevant, timely emails based on subscriber actions.

```liquid
{% if event.type == "abandoned_cart" %}
  <h2>Did you forget something?</h2>
  <p>We noticed you left some items in your cart:</p>
  <ul>
  {% for item in event.properties.items %}
    <li>{{ item.product_name }} - {{ item.price | currencyFormat: event.properties.currency }}</li>
  {% endfor %}
  </ul>
  <a href="{{ event.properties.checkout_url }}">Complete your purchase</a>
{% elsif event.type == "purchase_confirmation" %}
  <h2>Thank you for your purchase!</h2>
  <p>Order Total: {{ event.properties.total_price | currencyFormat: event.properties.currency }}</p>
  <p>Order ID: {{ event.properties.order_id }}</p>
{% endif %}
```

Best Practice: Use event data to trigger timely, relevant emails such as abandoned cart reminders or purchase confirmations.

### Discount Codes

Generate and display unique discount codes in your emails to incentivize purchases.

```liquid
<h2>Your Exclusive Offer</h2>
<p>Use this code for 15% off your next purchase:</p>
<div class="discount-code">{% discountCode "SUMMER_SALE_POOL" %}</div>
```

Best Practice: Use unique discount codes to track the performance of different email campaigns or segments.

### Translations

Create multilingual email templates using the translation feature.

First, set up your translation files (in JSON format) for each language you support. For example:

```json
{
  "welcome_message": "Welcome to our store!",
  "sale_announcement": "Don't miss our big sale, {{name}}!",
  "product_of_the_day": "Product of the day: {{product}}"
}
```

Then use the `t` filter in your templates:

```liquid
<h1>{{ "welcome_message" | t }}</h1>
<p>{{ "sale_announcement" | t: "name", contact.attributes.firstName }}</p>
<h2>{{ "product_of_the_day" | t: "product", featuredProduct.title }}</h2>
```

Best Practice: Use translations to create a single template that can be used for multiple languages, reducing maintenance overhead.

### External Data Sources

Integrate real-time data from external sources into your emails. You can learn more about external data sources here.&#x20;

{% content-ref url="../../external-data-sources" %}
[external-data-sources](https://docs.bird.com/applications/content/external-data-sources)
{% endcontent-ref %}

#### Basic Usage

```liquid
{% datafetch "weather-api" %}
  <p>Current temperature in your area: {{ dataFetchResponse.temperature }}°C</p>
  <p>Weather condition: {{ dataFetchResponse.condition }}</p>
{% enddatafetch %}
```

#### Using Dynamic URLs

For APIs that require dynamic parameters:

```liquid
{% datafetch "user-recommendations", "userId", contact.attributes.userId %}
  <h2>Recommended for You</h2>
  <ul>
  {% for product in dataFetchResponse.recommendations %}
    <li>{{ product.name }} - {{ product.price | currencyFormat: 'USD' }}</li>
  {% endfor %}
  </ul>
{% enddatafetch %}
```

Best Practice: Use external data sources to include up-to-date, personalized information in your emails, such as account balances, loyalty points, or personalized recommendations.

### Supported Liquid Filters

#### Text Formatting

**Filter:** `capitalize`

Capitalizes the first letter of a string.

```liquid
{{ "hello world" | capitalize }}
{{ user.name | capitalize }}
```

**Filter:** `upcase`

Converts string to uppercase.

```liquid
{{ "hello world" | upcase }}
{{ product.category | upcase }}
```

**Filter:** `downcase`

Converts string to lowercase.

```liquid
{{ "HELLO WORLD" | downcase }}
{{ user.email | downcase }}
```

**Filter:** `strip`

Removes leading and trailing whitespace from strings.

```liquid
{{ "  hello world  " | strip }}
{{ user_input | strip }}
```

**Filter:** `lstrip`

Removes leading (left) whitespace from strings.

```liquid
{{ "  hello world  " | lstrip }}
{{ user_input | lstrip }}
```

**Filter:** `rstrip`

Removes trailing (right) whitespace from strings.

```liquid
{{ "  hello world  " | rstrip }}
{{ user_input | rstrip }}
```

**Filter:** `append`

Appends text to the end of a string.

```liquid
{{ "Hello" | append: " World!" }}
{{ filename | append: ".txt" }}
```

**Filter:** `prepend`

Prepends text to the beginning of a string.

```liquid
{{ "World" | prepend: "Hello " }}
{{ filename | prepend: "backup_" }}
```

**Filter:** `replace`

Replaces all occurrences of a substring.

```liquid
{{ "Hello World" | replace: "World", "Universe" }}
{{ content | replace: "\n", "<br>" }}
```

**Filter:** `replace_first`

Replaces only the first occurrence of a substring.

```liquid
{{ "Hello World World" | replace_first: "World", "Universe" }}
{{ text | replace_first: "  ", " " }}
```

**Filter:** `remove`

Removes all occurrences of a substring.

```liquid
{{ "Hello World" | remove: "World" }}
{{ phone | remove: "-" }}
```

**Filter:** `remove_first`

Removes only the first occurrence of a substring.

```liquid
{{ "Hello World World" | remove_first: "World" }}
{{ text | remove_first: "  " }}
```

**Filter:** `slice`

Extracts a substring starting at a position with optional length.

```liquid
{{ "Hello World" | slice: 0, 5 }}
{{ product.sku | slice: 0, 3 }}
```

**Filter:** `truncate`

Truncates a string to a specified length, adding "..." if truncated.

```liquid
{{ product.description | truncate: 100 }}
{{ title | truncate: 50, "..." }}
```

**Filter:** `truncatewords`

Truncates a string to a specified number of words.

```liquid
{{ article.content | truncatewords: 20 }}
{{ description | truncatewords: 10, "..." }}
```

**Filter:** `escape`

Escapes HTML characters in strings.

```liquid
{{ user_content | escape }}
{{ "<script>" | escape }}
```

**Filter:** `escape_once`

Escapes HTML characters, but doesn't double-escape already escaped characters.

```liquid
{{ mixed_content | escape_once }}
{{ "&lt;div&gt;" | escape_once }}
```

**Filter:** `strip_html`

Removes HTML tags from strings.

```liquid
{{ "<p>Hello <strong>World</strong></p>" | strip_html }}
{{ product.description | strip_html }}
```

**Filter:** `strip_newlines`

Removes newline characters from strings.

```liquid
{{ multiline_text | strip_newlines }}
{{ content | strip_newlines }}
```

**Filter:** `newline_to_br`

Converts newline characters to HTML `<br>` tags.

```liquid
{{ multiline_text | newline_to_br }}
{{ comment.body | newline_to_br }}
```

**Filter:** `url_encode`

URL-encodes strings for safe use in URLs.

```liquid
{{ search_query | url_encode }}
{{ "hello world" | url_encode }}
```

**Filter:** `url_decode`

URL-decodes strings.

```liquid
{{ encoded_param | url_decode }}
{{ "hello%20world" | url_decode }}
```

**Filter:** `titleCase`

Converts text to title case, lowercasing small words.

```liquid
{{ "wAr aNd pEaCe HTML" | titleCase }}
{{ contact.firstName | titleCase }}
```

**Filter:** `titleCaseKeepAllCaps`

Converts text to title case while preserving fully uppercase words.

```liquid
{{ "wAr aNd pEaCe HTML" | titleCaseKeepAllCaps }}
{{ "API documentation guide" | titleCaseKeepAllCaps }}
```

#### Arrays and Collections

**Filter:** `join`

Joins array elements with a separator.

```liquid
{{ tags | join: ", " }}
{{ product.categories | join: " | " }}
```

**Filter:** `split`

Splits strings into arrays using a delimiter.

```liquid
{{ "apple,banana,cherry" | split: "," }}
{{ user.full_name | split: " " }}
```

**Filter:** `size`

Returns the size of strings or arrays.

```liquid
{{ "hello" | size }}
{{ products | size }}
```

**Filter:** `first`

Gets the first element of an array.

```liquid
{{ products | first }}
{{ tags | first }}
```

**Filter:** `last`

Gets the last element of an array.

```liquid
{{ products | last }}
{{ user.addresses | last }}
```

**Filter:** `where`

Filters arrays by property values.

```liquid
{% assign clothingProducts = products | where: 'type', 'clothing' %}
{% assign availableProducts = products | where: 'available' %}
{% assign discountedProducts = products | where: 'discount' %}
```

**Filter:** `sort`

Sorts arrays in ascending order.

```liquid
{{ tags | sort }}
{{ products | sort: 'price' }}
```

**Filter:** `sort_natural`

Performs natural sorting (handles numbers in strings properly).

```liquid
{{ filenames | sort_natural }}
{{ versions | sort_natural }}
```

**Filter:** `reverse`

Reverses the order of array elements.

```liquid
{{ products | reverse }}
{{ tags | sort | reverse }}
```

**Filter:** `uniq`

Removes duplicate elements from arrays.

```liquid
{{ categories | uniq }}
{{ tags | uniq | sort }}
```

**Filter:** `compact`

Removes nil/null items from arrays.

```liquid
{{ addresses | compact }}
{{ product.variants | compact }}
```

**Filter:** `concat`

Combines two arrays into one.

```liquid
{{ array1 | concat: array2 }}
{{ featured_products | concat: regular_products }}
```

**Filter:** `map`

Extracts a specific property from each array element.

```liquid
{{ products | map: 'title' }}
{{ users | map: 'email' }}
```

#### Default Values

**Filter:** `default`

Provides default values for nil/empty variables.

```liquid
{{ product.description | default: "No description available" }}
{{ user.avatar | default: "/images/default-avatar.png" }}
```

#### Currency Formatting

**Filter:** `currencyFormat`

Formats currency values with locale-specific formatting.

```liquid
{{ "100.00" | currencyFormat: "EUR", "nl-NL" }}
{{ product.price | currencyFormat: "USD" }}
{{ amount | currencyFormat: "EUR", "es-ES" }}
```

Best Practice: Use the `currencyFormat` filter for all price displays to ensure consistency and proper formatting across different regions.

**Filter:** `moneyWithoutCurrency`

Formats numbers as currency without currency symbols, with custom separators.

```liquid
{{ 1234567890.98 | moneyWithoutCurrency: 2, ',', '.' }}
{{ product.price | moneyWithoutCurrency: 0, ',', '.' }}
{{ "1234.56" | moneyWithoutCurrency: 2, '', '.' }}
```

#### Math & Numbers

**Filter:** `abs`

Returns the absolute value of a number.

```liquid
{{ -5 | abs }}
{{ product.discount | abs }}
```

**Filter:** `ceil`

Rounds a number up to the nearest integer.

```liquid
{{ 4.2 | ceil }}
{{ price | divided_by: 3 | ceil }}
```

**Filter:** `floor`

Rounds a number down to the nearest integer.

```liquid
{{ 4.8 | floor }}
{{ total | divided_by: count | floor }}
```

**Filter:** `round`

Rounds a number to the nearest integer or specified decimal places.

```liquid
{{ 4.6 | round }}
{{ 3.14159 | round: 2 }}
```

**Filter:** `plus`

Adds numbers together.

```liquid
{{ 5 | plus: 3 }}
{{ product.price | plus: tax }}
```

**Filter:** `minus`

Subtracts numbers.

```liquid
{{ 10 | minus: 3 }}
{{ total | minus: discount }}
```

**Filter:** `times`

Multiplies numbers.

```liquid
{{ 3 | times: 4 }}
{{ price | times: quantity }}
```

**Filter:** `divided_by`

Divides numbers.

```liquid
{{ 12 | divided_by: 3 }}
{{ total | divided_by: count }}
```

**Filter:** `modulo`

Returns the remainder of division.

```liquid
{{ 7 | modulo: 3 }}
{{ index | modulo: 2 }}
```

#### Date & Time

**Filter:** `date`

Formats dates using strftime patterns.

```liquid
{{ order.created_at | date: "%B %d, %Y" }}
{{ "2024-09-26" | date: "%A, %b %d" }}
```

**Filter:** `timezone`

Converts timestamps to different timezones.

```liquid
{{ meetingDate | timezone: "Europe/London" | date: "%d %B %Y %H:%M" }}
{{ "2024-09-25T10:30:00Z" | timezone: "America/New_York" }}
```

#### Data Lookup

**Filter:** `lookup`

Retrieves data from lookup tables with nested key support.

```liquid
{{ 'global_links' | lookup: 'compliance', 'terms_and_conditions' }}
{% assign compliance = 'global_links' | lookup: 'compliance' %}
{{ table_name | lookup: 'key', 'nested_key', 'deep_key' }}
```

#### Encoding

**Filter:** `base64_encode`

Encodes values using standard base64 encoding.

```liquid
{{ "hello world" | base64_encode }}
{{ contact.data | base64_encode }}
{{ 123 | base64_encode }}
```

**Filter:** `base64_decode`

Decodes base64 encoded strings.

```liquid
{{ "aGVsbG8gd29ybGQ=" | base64_decode }}
{{ encoded_data | base64_decode }}
```

**Filter:** `base64url_encode`

Encodes values using URL-safe base64 encoding (RFC 4648).

```liquid
{{ "hello world" | base64url_encode }}
{{ url_parameter | base64url_encode }}
```

**Filter:** `base64url_decode`

Decodes URL-safe base64 encoded strings.

```liquid
{{ url_safe_data | base64url_decode }}
{{ token | base64url_decode }}
```

#### Utility & Debug

**Filter:** `json`

Converts values to JSON format.

```liquid
{{ product | json }}
{{ user.preferences | json }}
```

**Filter:** `inspect`

Returns a debug representation of a value.

```liquid
{{ complex_object | inspect }}
{{ array | inspect }}
```

**Filter:** `type`

Returns the type of a value.

```liquid
{{ variable | type }}
{{ product.price | type }}
```

#### Notes

* Base64 filters have a 1MB input size limit for security
* Currency formatting defaults to `en-US` locale when not specified
* Where filter supports both property existence checks and value matching
* Lookup tables are cached during template rendering for performance

For a comprehensive guide on these advanced Liquid features, please refer to the [Shopify Liquid documentation](https://shopify.dev/docs/api/liquid).

Best Practice: While these advanced features are powerful, use them judiciously. Overly complex templates can be difficult to maintain and may impact email rendering performance.

Remember to thoroughly test your email templates across different email clients to ensure consistent rendering and optimal performance. The Bird platform provides tools for previewing and testing your templates before sending.

By mastering the Bird Email Template Language, you'll be able to create highly dynamic, personalized, and engaging email campaigns that resonate with your audience and drive results.
