Skip to content

SendGrid Dynamic Templates — Reusable Email Designs with Handlebars

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about SendGrid Dynamic Templates. We cover key concepts, practical examples, and best practices to help you master this topic.

SendGrid dynamic templates use Handlebars syntax to create reusable email designs with dynamic content placeholders, allowing marketing and design teams to update email layouts without developer involvement.

What You'll Learn

  • How to create a dynamic template in SendGrid
  • How to use Handlebars syntax for personalization
  • How to send emails using dynamic templates

Why It Matters

Without dynamic templates, email HTML is embedded in application code. Any design change requires a code deployment. Dynamic templates let designers create and update email designs in the SendGrid UI while developers simply pass data. This decouples email design from application logic.

Real-World Use

DodaTech's marketing team designs all transactional emails (welcome, password reset, invoice) using SendGrid's drag-and-drop editor with Handlebars placeholders. Developers only send the dynamic data. When the marketing team updates the welcome email design, all new users instantly see the new design without any code change.

flowchart LR
    A["Marketing Team"] --> B["SendGrid Template\nEditor"]
    B --> C["Dynamic Template\nwith {{handlebars}}"]
    D["Your App"] -->|"dynamicTemplateData"| E["SendGrid API"]
    E --> C
    C --> F["Personalized\nEmail"]
    style A fill:#fef3c7,stroke:#d97706
    style D fill:#dbeafe,stroke:#2563eb
    style C fill:#bbf7d0,stroke:#16a34a

Creating a Dynamic Template

Via SendGrid UI or API:

import requests

def create_dynamic_template(name, html_content, subject):
    response = requests.post(
        "https://api.sendgrid.com/v3/templates",
        headers={
            "Authorization": f"Bearer {SENDGRID_API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "name": name,
            "generation": "dynamic"
        }
    )

    template_id = response.json()["id"]
    print(f"Template created: {template_id}")
    return template_id

# Add a version to the template
def add_template_version(template_id, subject, html_content):
    response = requests.post(
        f"https://api.sendgrid.com/v3/templates/{template_id}/versions",
        headers={
            "Authorization": f"Bearer {SENDGRID_API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "template_id": template_id,
            "subject": subject,
            "html_content": html_content,
            "active": 1
        }
    )
    return response.json()

Handlebars Template Example

<!-- welcome-template.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Welcome to {{companyName}}</title>
</head>
<body>
  <h1>Welcome, {{userName}}!</h1>
  <p>Thank you for creating an account.</p>
  <p>Your email: {{userEmail}}</p>

  {{#if trialDays}}
  <p>You have {{trialDays}} days of free trial remaining.</p>
  {{/if}}

  <h3>Quick Links</h3>
  <ul>
    {{#each quickLinks}}
    <li><a href="{{url}}">{{label}}</a></li>
    {{/each}}
  </ul>

  <p>Best regards,<br>The {{companyName}} Team</p>
</body>
</html>

Sending with a Dynamic Template

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

def send_template_email(to_email, template_id, template_data):
    message = Mail(
        from_email='noreply@dodatech.com',
        to_emails=to_email
    )

    # Set the template ID
    message.template_id = template_id

    # Set dynamic template data
    message.dynamic_template_data = template_data

    try:
        response = sg.send(message)
        print(f"Template email sent: {response.status_code}")
        return True
    except Exception as e:
        print(f"Error: {str(e)}")
        return False

# Usage
send_template_email(
    to_email='user@example.com',
    template_id='d-abc123def456',
    template_data={
        'userName': 'Jane Doe',
        'userEmail': 'jane@example.com',
        'companyName': 'DodaTech',
        'trialDays': 14,
        'quickLinks': [
            {'label': 'Dashboard', 'url': 'https://dodatech.com/dashboard'},
            {'label': 'Profile', 'url': 'https://dodatech.com/profile'},
            {'label': 'Help Center', 'url': 'https://help.dodatech.com'}
        ]
    }
)

Common Mistakes

1. Using Legacy Templates Instead of Dynamic

Legacy templates use substitution tags with - delimiters. Dynamic templates use Handlebars {{ }}. Always choose dynamic generation for new templates.

2. Not Escaping Special Characters

Handlebars escapes HTML by default. Use triple braces {{{ }}} for raw HTML, but be careful of XSS risks.

3. Forgetting to Activate a Template Version

Template versions must be activated. An inactive version returns no content. Always set active: 1.

4. Using Handlebars Helpers Incorrectly

Helpers like {{#if}}, {{#each}}, and {{#unless}} must be closed with {{/if}}, {{/each}}. Unclosed helpers break the template.

5. Missing Template Data Fields

If a template references {{userName}} but the data doesn't include it, Handlebars renders an empty string instead of throwing an error. Test with all expected fields.

Practice Questions

  1. What syntax does SendGrid dynamic templates use?
  2. How do you specify the template when sending?
  3. What Handlebars helper iterates over arrays?
  4. How do you render raw HTML in Handlebars?
  5. What happens when a template variable is missing?

Answers

  1. Handlebars {{ }} syntax. 2. Set message.template_id and message.dynamic_template_data. 3. {{#each array}}{{/each}}. 4. Use triple braces {{{ }}}. 5. Handlebars renders an empty string.

Challenge

Build a template management system that creates dynamic templates via the SendGrid API, uploads versions with Handlebars content, activates the latest version, and provides a preview endpoint that renders a template with sample data.

FAQ

What are SendGrid dynamic templates?

Templates that use Handlebars syntax for dynamic content placeholders, managed in the SendGrid UI.

How is Handlebars different from substitution tags?

Handlebars uses {{ }} syntax with conditionals and loops; substitution tags used -name- with no logic.

Can I test a dynamic template before sending?

Yes. Use SendGrid's template preview or send to a test address in sandbox mode.

What happens if a template variable is missing?

Handlebars renders an empty string for missing variables.

Can I use conditional logic in dynamic templates?

Yes. Handlebars supports {{#if}}, {{#unless}}, {{#each}}, and custom helpers.

Mini Project

Build a Python application that manages the full lifecycle of dynamic templates: creates templates and versions via the SendGrid API, uploads Handlebars HTML with conditional blocks and loops, sends test emails with sample data for preview, and promotes versions from draft to active.

What's Next

  • Learn about template versioning for managing template changes
  • Explore email attachments and inline images
  • Continue to categories and unique arguments for email tracking

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro