Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.kit.com/llms.txt

Use this file to discover all available pages before exploring further.

Dynamic content block HTML is a Liquid template. When Kit sends an email, it renders your template for each subscriber by substituting Liquid variables with that recipient’s data (automation context, subscriber fields, and other send-time drops available in Kit’s email pipeline).

Variable paths

Dynamic content blocks support two data sources, each with its own variable path.

Automation event data

When your block is linked to an automation event plugin, event data is accessed through the automation namespace:
automation.{app_identifier}.{event_node_identifier}.{field_name}
  • app_identifier — Your app’s unique identifier, assigned in the Kit App Store.
  • event_node_identifier — The identifier of the linked automation event plugin element (the event node chosen via related_plugin_id), not the dynamic content block plugin’s own identifier.
  • field_name — A key from the event data stored for that subscriber (typically aligned with the fields your polling integration puts in each event’s context).
For example, if your app identifier is yourapp and the linked event node’s identifier is abandoned_checkout, you’d access the checkout URL like this:
{{ automation.yourapp.abandoned_checkout.checkout_url }}

Subscriber custom fields

Subscriber custom fields are accessible directly via the subscriber namespace, regardless of whether the block uses an event node:
subscriber.{field_name}
For example, to reference a custom field named company:
{{ subscriber.company }}
You can use subscriber custom fields alongside automation event data in the same template, or as the sole data source in blocks that don’t require an event node.
In the email editor preview for a dynamic content block, Kit renders Liquid using preview context built from default_values and (when linked) the automation namespace—not the full send-time Liquid environment. In particular, subscriber is not injected on that preview path, so {{ subscriber.* }} may render empty in the editor even though it works at send time. Always verify behavior in a sent test email when mixing subscriber and automation variables.

Writing your template

Guard the entire block

Always wrap your content in a top-level conditional. This ensures the block renders gracefully if the automation data isn’t present—for example, if the email is sent outside the expected workflow:
{% if automation.yourapp.abandoned_checkout %}
  <!-- Your personalized content here -->
{% endif %}

Iterate over arrays

Use Liquid’s for tag to loop over arrays. The limit filter caps the number of items rendered. Always cap loops in email templates to avoid unexpectedly long emails:
{% for product in automation.yourapp.abandoned_checkout.products limit: 4 %}
  <table width="100%" cellpadding="0" cellspacing="0">
    <tr>
      <td>
        {% if product.image_url %}
          <img src="{{ product.image_url }}" width="80" alt="{{ product.name }}" />
        {% endif %}
      </td>
      <td style="padding-left: 12px;">
        <p style="margin: 0; font-weight: bold;">{{ product.name }}</p>
        <p style="margin: 4px 0 0;">{{ product.price }}</p>
      </td>
    </tr>
  </table>
{% endfor %}

Conditionally show fields

Use {% if %} to guard optional fields. Subscribers may have different data states, so don’t assume every field is always populated:
{% if product.image_url %}
  <img src="{{ product.image_url }}" alt="{{ product.name }}" />
{% endif %}

{% assign discount_count = automation.yourapp.abandoned_checkout.discount_codes | size %}
{% if discount_count > 0 %}
  <p>Discount applied: {{ automation.yourapp.abandoned_checkout.discount_codes | join: ", " }}</p>
{% endif %}

Show overflow counts

When you limit the number of items shown, you can tell subscribers how many more they have:
{% assign total = automation.yourapp.abandoned_checkout.products | size %}
{% assign shown = 4 %}
{% assign remaining = total | minus: shown %}
{% if remaining > 0 %}
  <p style="text-align: center; color: #6e6e6e;">
    + {{ remaining }} more item{% if remaining != 1 %}s{% endif %} in your cart
  </p>
{% endif %}

Available Liquid filters

Kit’s email rendering uses Liquid with a supported subset of filters (similar to common Liquid, but not identical to every filter documented for other Liquid hosts). Prefer filters you have verified in a sent test email or editor preview. Commonly useful ones in dynamic content blocks:
FilterExampleOutput
size{{ products | size }}Number of items in an array
minus{{ total | minus: 4 }}Subtraction
join{{ codes | join: ", " }}Join array to string
truncate{{ name | truncate: 60 }}Truncate string to N characters
upcase{{ label | upcase }}Uppercase string
downcase{{ label | downcase }}Lowercase string

Preview vs. send time

Your template is rendered twice:
  1. At design time (editor preview) — Kit renders your template using default_values merged into the automation namespace when an event node is linked (see plugin configuration). Preview does not mirror the full send-time Liquid drop (for example, subscriber is not available on this preview path).
  2. At send time — Kit renders the same stored template with real per-recipient data, including automation context from Visual Automations (when applicable) and the normal subscriber Liquid drop for subscriber fields.
Because of this, your template must handle both cases gracefully. Ensure your default_values produce HTML that looks close to what real data would render—creators use this preview to judge how the block will look—and validate anything that depends on subscriber in a sent email, not only in the editor.

Email compatibility

The same security and compatibility rules that apply to standard content blocks also apply to your rendered Liquid template output. Kit sanitizes the final HTML before delivery. Avoid these in your template:
  • Scripts and iframes
  • Audio and video elements
  • Form, input, or command elements
  • External CSS stylesheets and CSS url() values
Best practices:
  • Use inline style attributes instead of CSS classes—email clients strip external stylesheets
  • Use table-based layouts (<table>, <tr>, <td>) for reliable rendering across email clients
  • Keep your maximum width at 600px
  • Include target="_blank" on all links
  • Use absolute URLs for all images and links
  • Guard image fields with {% if %} so missing images don’t produce broken <img> tags
See plugin security and plugin recommendations for the full list of restrictions and best practices.

Full example

An abandoned cart block with a product list, totals, and a checkout button:
{% if automation.yourapp.abandoned_checkout %}
  <table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif;">
    
    {% for product in automation.yourapp.abandoned_checkout.products limit: 4 %}
      <tr>
        <td style="padding: 12px; border-bottom: 1px solid #e5e5e5;">
          <table width="100%" cellpadding="0" cellspacing="0">
            <tr>
              {% if product.image_url %}
                <td width="80">
                  <img src="{{ product.image_url }}" width="80" alt="{{ product.name }}" style="display: block; border-radius: 4px;" />
                </td>
              {% endif %}
              <td style="padding-left: 12px; vertical-align: top;">
                <p style="margin: 0 0 4px; font-weight: bold; color: #000;">{{ product.name }}</p>
                <p style="margin: 0; color: #6e6e6e;">Qty: {{ product.quantity }}</p>
                <p style="margin: 4px 0 0; color: #000;">{{ product.price }}</p>
              </td>
            </tr>
          </table>
        </td>
      </tr>
    {% endfor %}

    {% assign total_count = automation.yourapp.abandoned_checkout.products | size %}
    {% assign remaining = total_count | minus: 4 %}
    {% if remaining > 0 %}
      <tr>
        <td style="padding: 8px 12px; text-align: center; color: #6e6e6e;">
          + {{ remaining }} more item{% if remaining != 1 %}s{% endif %}
        </td>
      </tr>
    {% endif %}

    <tr>
      <td style="padding: 16px 12px; border-top: 1px solid #e5e5e5; text-align: right; color: #000;">
        Total: <strong>{{ automation.yourapp.abandoned_checkout.total }}</strong>
      </td>
    </tr>

    <tr>
      <td style="padding: 16px 12px; text-align: center;">
        <a href="{{ automation.yourapp.abandoned_checkout.checkout_url }}"
           target="_blank"
           style="display: inline-block; padding: 12px 24px; background-color: #000; color: #fff; text-decoration: none; border-radius: 4px; font-weight: bold;">
          Complete your order
        </a>
      </td>
    </tr>

  </table>
{% endif %}