MailViewr - Free HTML Email Preview Tool

Free Transactional Email Template — Order Confirmation

Order confirmations · receipts · account notifications

Includes:HTML fileHandlebars variablessample-data.jsonVariables reference600px · Inline CSS · No dependencies

This template is compatible with SendGrid Dynamic Templates, Klaviyo Flows, Mailchimp automations, Brevo transactional emails, Postmark, HubSpot, and any backend using Handlebars.js, Jinja2, or Liquid templating.

Template Preview

Previewed with sample-data.json. Replace {{variables}} with your ESP tags before sending.

How to use it

1

Download or copy the HTML

Self-contained file. No external dependencies.

2

Swap variables for your ESP tags

See the reference table below for Mailchimp, SendGrid, Klaviyo.

3

Or plug into a template engine

Handlebars.js · Jinja2 · Liquid. Use sample-data.json to test.

4

Preview before sending

Paste rendered HTML back into MailViewr to verify.


Compatible with

MailchimpSendGridKlaviyoBrevoHubSpotHandlebars.jsJinja2Liquid

Test with real data⬇ sample-data.json

Variables reference

11 variables · ESP merge tag equivalents · items[] loop

{{customer_name}} · {{order_number}} · {{order_date}} · {{order_status}} · {{order_status_url}} · {{subtotal}} · {{shipping}} · {{total}} · {{support_url}} · {{company_name}} · {{year}} · items[]
VariableDescriptionMailchimpSendGridKlaviyoBrevo
{{customer_name}}Full name of the customer*|FNAME|*{{customer_name}}{{ customer_name }}{{ contact.FIRSTNAME }}
{{order_number}}Order ID / number*|ORDER_ID|*{{order_number}}{{ order_number }}{{ params.order_number }}
{{order_date}}Formatted order date*|ORDER_DATE|*{{order_date}}{{ order_date }}{{ params.order_date }}
{{order_status}}Current status (Processing / Shipped / etc.)*|ORDER_STATUS|*{{order_status}}{{ order_status }}{{ params.order_status }}
{{order_status_url}}Link to order tracking page*|ORDER_URL|*{{order_status_url}}{{ order_status_url }}{{ params.order_status_url }}
{{subtotal}}Order subtotal e.g. $149.97*|SUBTOTAL|*{{subtotal}}{{ subtotal }}{{ params.subtotal }}
{{shipping}}Shipping cost e.g. $10.00*|SHIPPING|*{{shipping}}{{ shipping }}{{ params.shipping }}
{{total}}Grand total e.g. $159.97*|TOTAL|*{{total}}{{ total }}{{ params.total }}
{{support_url}}Link to support / contact page*|SUPPORT_URL|*{{support_url}}{{ support_url }}{{ params.support_url }}
{{company_name}}Your company / brand name*|LIST:COMPANY|*{{company_name}}{{ company_name }}{{ params.company_name }}
{{year}}Current year for copyright*|CURRENT_YEAR|*{{year}}{{ "now" | date: "%Y" }}{{ params.year }}

items[] — loop variable

Rendered with {{#each items}}{{/each}}. Inside the loop use {{this.name}}, {{this.quantity}}, {{this.price}}. In Klaviyo use {% for item in items %} / {% endfor %} with {{ item.name }} etc.

⚠️ order_status color

The status cell uses a neutral dark color by default. If you want status-driven colors (green for Shipped, red for Cancelled), set the color CSS property conditionally in your backend before sending — do not hard-code a single color in the template.

How to use this template

Step-by-step integration guide for Mailchimp, SendGrid, Klaviyo, Brevo, HubSpot, Handlebars.js, Jinja2, and Liquid.

Variable mapping

Template variableMailchimp tag
{{first_name}}*|FNAME|*
{{unsubscribe_url}}*|UNSUB|*
{{company_name}}*|MC:COMPANY|*
{{year}}Hardcode current year (e.g. 2025)
{{discount_percent}}, {{offer_price}}, {{expiry_hours}}, {{cta_url}}, {{terms_url}}Hardcode per campaign or use Content Blocks
{{#each features}}...{{/each}}Not supported — hardcode items or use Content Blocks

Steps

  1. Download the HTML template.
  2. Replace variables using the mapping table above.
  3. In Mailchimp: Campaigns → Create Email → Code Your Own.
  4. Paste the modified HTML into the code editor.
  5. Click Send Test to verify rendering before launching.

⚠ Gotcha

Mailchimp strips box-shadow and some advanced CSS properties. Always send a test email before launching your campaign.

Preview your Mailchimp template before sending

Variable mapping

Template variableSendGrid tag
{{first_name}}{{first_name}}
{{discount_percent}}{{discount_percent}}
{{offer_price}}{{offer_price}}
{{expiry_hours}}{{expiry_hours}}
{{cta_url}}{{cta_url}}
{{terms_url}}{{terms_url}}
{{unsubscribe_url}}{{unsubscribe_url}}
{{company_name}}{{company_name}}
{{year}}{{year}}
{{#each features}}...{{/each}}{{#each features}}...{{/each}} (native)

Steps

  1. Download the HTML template — variables are already Handlebars syntax, no changes needed.
  2. In SendGrid: Email API → Dynamic Templates → Create a Dynamic Template.
  3. Add a new version and paste the HTML into the code editor.
  4. In the Test Data panel, paste your JSON data object.
  5. Use the template ID in API calls with the dynamic_template_data field.

⚠ Gotcha

Handlebars support is only available in Dynamic Transactional Templates — not Marketing Campaigns. Make sure you're using the correct template type.

Variable mapping

Template variableKlaviyo tag
{{first_name}}{{ first_name }}
{{discount_percent}}{{ discount_percent }}
{{offer_price}}{{ offer_price }}
{{expiry_hours}}{{ expiry_hours }}
{{cta_url}}{{ cta_url }}
{{terms_url}}{{ terms_url }}
{{unsubscribe_url}}{{ unsubscribe_url }}
{{company_name}}{{ company_name }}
{{year}}{{ year }}
{{#each features}}{% for feature in features %}
{{this.name}}, {{this.original_price}}{{ feature.name }}, {{ feature.original_price }}
{{/each}}{% endfor %}

Steps

  1. Download the HTML template.
  2. Replace all variables and loops using the mapping table above.
  3. In Klaviyo: Campaigns → Create Campaign → Email → drag in an HTML block.
  4. Paste the modified HTML.
  5. Use Preview with a profile to verify variable substitution.

⚠ Gotcha

Klaviyo uses Jinja2-style syntax, not Handlebars. The {{#each}} loop must be rewritten as {% for feature in features %} … {% endfor %}.

Variable mapping

Template variableBrevo tag
{{first_name}}{{ contact.FIRSTNAME }}
{{discount_percent}}{{ params.discount_percent }}
{{offer_price}}{{ params.offer_price }}
{{expiry_hours}}{{ params.expiry_hours }}
{{cta_url}}{{ params.cta_url }}
{{terms_url}}{{ params.terms_url }}
{{unsubscribe_url}}{{ params.unsubscribe_url }}
{{company_name}}{{ params.company_name }}
{{year}}{{ params.year }}
{{#each features}}{% for feature in params.features %}
{{this.name}}, {{this.original_price}}{{ feature.name }}, {{ feature.original_price }}
{{/each}}{% endfor %}

Steps

  1. Download the HTML template.
  2. Replace variables using the mapping table above.
  3. In Brevo: Email Campaigns → Template Library → New Template → paste HTML.
  4. Pass transactional params in your API call under the params object.
  5. Preview and test before sending.

⚠ Gotcha

Brevo distinguishes between contact attributes (contact. prefix) and transactional params (params. prefix). Mixing them up is the most common integration error.

Variable mapping

Template variableHubSpot tag
{{first_name}}{{ contact.firstname }}
{{unsubscribe_url}}{{ unsubscribe_link }}
{{company_name}}{{ account.company_name }}
{{discount_percent}}{{ discount_percent }}
{{offer_price}}{{ offer_price }}
{{expiry_hours}}{{ expiry_hours }}
{{cta_url}}{{ cta_url }}
{{terms_url}}{{ terms_url }}
{{year}}{{ year }}
{{#each features}}{% for feature in features %}
{{this.name}}, {{this.original_price}}{{ feature.name }}, {{ feature.original_price }}
{{/each}}{% endfor %}

Steps

  1. Download the HTML template.
  2. Swap variables to HubL syntax using the mapping table above.
  3. In HubSpot: Marketing → Email → Create Email → Custom Code module.
  4. Paste the modified HTML.
  5. Use Preview & Test to verify with a contact record.

⚠ Gotcha

HubSpot uses HubL (a Jinja2 variant). Custom HTML emails require a paid Marketing Hub account.

Variable mapping

Template variableHandlebars.js tag
{{first_name}}{{first_name}} — no change
{{discount_percent}}, {{offer_price}}, etc.All scalar variables — no change
{{#each features}}...{{/each}}{{#each features}}...{{/each}} — no change

Steps

  1. Install: npm install handlebars
  2. Load the template HTML as a string (e.g. via fs.readFileSync).
  3. Compile and render using the snippet below.
  4. Pass your data object to the compiled template function.
const Handlebars = require('handlebars');
const fs = require('fs');

const templateStr = fs.readFileSync('template.html', 'utf-8');
const template = Handlebars.compile(templateStr);

const html = template({
  first_name: 'Alex',
  discount_percent: '20',
  offer_price: '$79',
  expiry_hours: '48',
  cta_url: 'https://example.com/deal',
  terms_url: 'https://example.com/terms',
  unsubscribe_url: 'https://example.com/unsubscribe',
  company_name: 'Acme Inc.',
  year: new Date().getFullYear(),
  features: [
    { name: 'Feature A', original_price: '$99' },
    { name: 'Feature B', original_price: '$49' },
  ],
});

Variable mapping

Template variableJinja2 tag
{{first_name}}{{ first_name }}
{{discount_percent}}{{ discount_percent }}
{{offer_price}}{{ offer_price }}
{{expiry_hours}}{{ expiry_hours }}
{{cta_url}}{{ cta_url }}
{{terms_url}}{{ terms_url }}
{{unsubscribe_url}}{{ unsubscribe_url }}
{{company_name}}{{ company_name }}
{{year}}{{ year }}
{{#each features}}{% for feature in features %}
{{this.name}}, {{this.original_price}}{{ feature.name }}, {{ feature.original_price }}
{{/each}}{% endfor %}

Steps

  1. Install: pip install jinja2
  2. Load and render the template using the snippet below.
from jinja2 import Environment
from datetime import datetime

env = Environment()
with open('template.html') as f:
    template = env.from_string(f.read())

html = template.render(
    first_name='Alex',
    discount_percent='20',
    offer_price='$79',
    expiry_hours='48',
    cta_url='https://example.com/deal',
    terms_url='https://example.com/terms',
    unsubscribe_url='https://example.com/unsubscribe',
    company_name='Acme Inc.',
    year=datetime.now().year,
    features=[
        {'name': 'Feature A', 'original_price': '$99'},
        {'name': 'Feature B', 'original_price': '$49'},
    ],
)

⚠ Gotcha

Jinja2 uses {% %} for logic blocks and {{ }} for variables. The Handlebars {{#each}} loop must be rewritten as {% for feature in features %} … {% endfor %}.

Variable mapping

Template variableLiquid tag
{{first_name}}{{ first_name }}
{{discount_percent}}{{ discount_percent }}
{{offer_price}}{{ offer_price }}
{{expiry_hours}}{{ expiry_hours }}
{{cta_url}}{{ cta_url }}
{{terms_url}}{{ terms_url }}
{{unsubscribe_url}}{{ unsubscribe_url }}
{{company_name}}{{ company_name }}
{{year}}{{ year }}
{{#each features}}{% for feature in features %}
{{this.name}}, {{this.original_price}}{{ feature.name }}, {{ feature.original_price }}
{{/each}}{% endfor %}

Steps

  1. Download the HTML template.
  2. Replace Handlebars syntax with Liquid using the mapping table above.
  3. In Shopify Emails: use {{ customer.first_name }} for customer-scoped data.
  4. In Klaviyo or generic Liquid contexts: use flat {{ first_name }} syntax.
  5. Preview with a test customer or profile to verify rendering.

⚠ Gotcha

Shopify Liquid uses customer-scoped variables ({{ customer.first_name }}), while standard Liquid uses flat variables ({{ first_name }}). Know which context you're targeting.

Frequently asked questions

Yes. This transactional email template is completely free to download and use. No signup required.

Yes. Replace the {{variable}} placeholders with Mailchimp merge tags. For example, replace {{customer_name}} with *|FNAME|* and {{order_number}} with *|ORDER_ID|*.

Yes. Klaviyo uses Liquid syntax. Replace {{#each items}} with {% for item in items %} and {{/each}} with {% endfor %}. Scalar variables use {{ variable }} with spaces.

Yes. The template ships with Handlebars syntax natively. For Jinja2 change {{variable}} to {{ variable }} and {{#each items}} to {% for item in items %}. A sample-data.json is included to test rendering.

Yes. The template is 600px max-width with fully inline CSS. It renders correctly in Gmail, Outlook, Apple Mail, and mobile clients.

Yes. The items table uses a {{#each items}} loop so it renders however many products are in the order. Each item exposes name, quantity, and price fields.