MailViewr - Free HTML Email Preview Tool

Free Product Announcement Email Template — Feature Releases

Product launches · feature releases · company updates

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

This template is compatible with Mailchimp automations, Klaviyo Flows, SendGrid Dynamic Templates, Brevo email campaigns, HubSpot workflows, Postmark, 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

9 variables · ESP merge tag equivalents · features[] loop

{{first_name}} · {{announcement_headline}} · {{cta_url}} · {{upgrade_url}} · {{support_url}} · {{preferences_url}} · {{unsubscribe_url}} · {{company_name}} · {{year}} · features[]
VariableDescriptionMailchimpSendGridKlaviyoBrevo
{{first_name}}Recipient's first name*|FNAME|*{{first_name}}{{ first_name }}{{ contact.FIRSTNAME }}
{{announcement_headline}}Main heading shown below the banner*|MERGE1|*{{announcement_headline}}{{ announcement_headline }}{{ params.announcement_headline }}
{{cta_url}}"Learn More" CTA button link*|LIST:URL|*{{cta_url}}{{ cta_url }}{{ params.cta_url }}
{{upgrade_url}}Upgrade / pricing page link in the callout*|LIST:URL|*{{upgrade_url}}{{ upgrade_url }}{{ params.upgrade_url }}
{{support_url}}Contact or support page link in the footer*|LIST:URL|*{{support_url}}{{ support_url }}{{ params.support_url }}
{{preferences_url}}Email preference centre link*|UPDATE_PROFILE|*{{preferences_url}}{{ preferences_url }}{{ params.preferences_url }}
{{unsubscribe_url}}One-click unsubscribe link*|UNSUB|*{{{unsubscribe}}}{{ unsubscribe_url }}{{ unsubscribeUrl }}
{{company_name}}Your brand or company name*|LIST:COMPANY|*{{company_name}}{{ company_name }}{{ params.company_name }}
{{year}}Current year for the footer copyright line*|CURRENT_YEAR|*{{year}}{{ "now" | date: "%Y" }}{{ params.year }}

features[] loop — What's New list

The “What's New?” bullet list uses a {{#each features}} loop. Each item needs two fields: this.title (feature name) and this.description (one-line description). Add or remove bullets by adjusting the features array in your template data.

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 product announcement email template is completely free to download and use. No signup required.

The What's New section uses a {{#each features}} loop. Pass an array of objects with title and description fields in your template data. Add or remove bullets by adjusting the features array.

Yes. Replace {{first_name}} with *|FNAME|*, {{unsubscribe_url}} with *|UNSUB|*, and other variables like {{announcement_headline}} with custom merge tags in your Mailchimp audience.

Yes. Klaviyo uses Liquid syntax. Scalar variables use {{ variable }} with spaces. Replace {{unsubscribe_url}} with Klaviyo's unsubscribe block and set up the features loop as a Klaviyo dynamic block if needed.

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

The header uses a blue gradient (#1e40af to #1e3a8a) with a gold accent border (#fbbf24). Search and replace those hex values in the inline style attributes to apply your brand colours.