Skip to main content

Email Template Development

This guide explains how to create and customize email templates in TestPlanIt using the Handlebars templating system.

Overview​

TestPlanIt uses Handlebars for email templating, providing:

  • Separation of content and presentation
  • Reusable layouts and partials
  • Template helpers for common operations
  • Responsive, professional email designs

Template Structure​

Directory Layout​

lib/email/
├── template-service.ts # Template rendering engine
├── notificationTemplates.ts # Email sending functions
├── templates/
│ ├── layouts/ # Base layouts
│ │ └── main.hbs # Default layout
│ ├── partials/ # Reusable components
│ │ └── header.hbs # Logo header
│ ├── notification.hbs # Single notification
│ └── daily-digest.hbs # Daily summary

Template Hierarchy​

  1. Layout - Base HTML structure (header, footer, styles)
  2. Template - Specific email content
  3. Partials - Reusable components

Creating Email Templates​

Basic Template​

Create a new file in lib/email/templates/:

<!-- welcome.hbs -->
<h2>Welcome to TestPlanIt!</h2>

{{#if userName}}
<p>Hi {{userName}},</p>
{{else}}
<p>Hello,</p>
{{/if}}

<p>Welcome to TestPlanIt. We're excited to have you!</p>

<div class="text-center">
<a href="{{appUrl}}/getting-started" class="button">
Get Started
</a>
</div>

Using the Template​

import { renderEmailTemplate } from './lib/email/template-service';

// Render the template
const { html, subject } = await renderEmailTemplate('welcome', {
userName: 'John Doe',
appUrl: process.env.NEXTAUTH_URL,
subject: 'Welcome to TestPlanIt!'
});

// Send the email
await transporter.sendMail({
from: process.env.EMAIL_FROM,
to: user.email,
subject,
html
});

Available Helpers​

Date Formatting​

<!-- Format as "January 1, 2024" -->
{{formatDate createdAt}}

<!-- Format as "January 1, 2024, 2:30 PM" -->
{{formatDateTime createdAt}}

Comparisons​

{{#if (eq status "completed")}}
<span class="success">✓ Completed</span>
{{/if}}

{{#if (gt count 0)}}
<p>You have {{count}} notifications</p>
{{/if}}

Available Operators​

  • eq - Equal to
  • ne - Not equal to
  • gt - Greater than
  • gte - Greater than or equal
  • lt - Less than
  • lte - Less than or equal

Template Variables​

Common Variables​

All templates receive these variables:

{
appUrl: string; // Application URL
userId: string; // Current user ID
currentYear: number; // For copyright
userName?: string; // User's display name
subject: string; // Email subject
}

Custom Variables​

Pass additional data as needed:

await renderEmailTemplate('custom-template', {
// Common variables
...commonData,

// Custom variables
projectName: 'My Project',
testResults: {
passed: 95,
failed: 5,
total: 100
}
});

Layouts​

Default Layout​

The main.hbs layout provides:

  • Responsive design
  • Consistent header/footer
  • Base styles
  • Unsubscribe links

Custom Layouts​

Create new layouts in templates/layouts/:

<!-- minimal.hbs -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{subject}}</title>
</head>
<body>
{{{content}}}
</body>
</html>

Use custom layout:

await renderEmailTemplate('alert', data, {
layout: 'minimal'
});

Partials​

Creating Partials​

Add files to templates/partials/:

<!-- button.hbs -->
<div class="text-center">
<a href="{{url}}" class="button {{class}}">
{{text}}
</a>
</div>

Using Partials​

{{> button url="/dashboard" text="View Dashboard" class="primary"}}

Styling Emails​

Inline Styles​

Email clients require inline styles:

<div style="padding: 20px; background-color: #f5f5f5;">
<h2 style="color: #333; margin-bottom: 10px;">
{{title}}
</h2>
</div>

CSS Classes​

Define classes in the layout:

<style>
.notification-item {
padding: 20px;
border: 1px solid #e5e7eb;
border-radius: 8px;
margin-bottom: 16px;
}
</style>

Responsive Design​

Use media queries for mobile:

<style>
@media only screen and (max-width: 600px) {
.email-body {
padding: 20px !important;
}
}
</style>

Testing Templates​

Local Development​

  1. Set up MailHog for local email testing:

    docker run -p 1025:1025 -p 8025:8025 mailhog/mailhog
  2. Configure .env:

    EMAIL_SERVER_HOST=localhost
    EMAIL_SERVER_PORT=1025
    EMAIL_SERVER_USER=
    EMAIL_SERVER_PASSWORD=
  3. View emails at http://localhost:8025

Preview Templates​

Create a preview script:

// scripts/preview-email.ts
import { renderEmailTemplate } from '../lib/email/template-service';

async function preview() {
const { html } = await renderEmailTemplate('notification', {
userName: 'Test User',
notification: {
title: 'Test Notification',
message: 'This is a test',
createdAt: new Date()
},
appUrl: 'http://localhost:3000'
});

console.log(html);
}

preview();

Best Practices​

Content​

  • Keep subject lines under 50 characters
  • Use preheader text effectively
  • Include alt text for images
  • Provide text-only alternatives

Design​

  • Use tables for layout (better email client support)
  • Stick to web-safe fonts
  • Test with images disabled
  • Keep width under 600px

Development​

  • Test across email clients (Gmail, Outlook, Apple Mail)
  • Validate HTML markup
  • Minimize external dependencies
  • Use absolute URLs for links and images

Security​

  • Sanitize user input
  • Don't include sensitive data
  • Use HTTPS URLs
  • Include unsubscribe links

Common Patterns​

Notification List​

{{#each notifications}}
<div class="notification-item">
<h3>{{this.title}}</h3>
<p>{{this.message}}</p>
<small>{{formatDateTime this.createdAt}}</small>
</div>
{{/each}}

Conditional Content​

{{#if urgent}}
<div class="alert alert-urgent">
⚠️ This requires immediate attention
</div>
{{/if}}

Call-to-Action Buttons​

<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="center">
<a href="{{actionUrl}}" class="button">
{{actionText}}
</a>
</td>
</tr>
</table>

Troubleshooting​

Template Not Found​

  • Ensure file exists in templates/ directory
  • Check file extension is .hbs
  • Verify template name in code matches filename

Variables Not Rendering​

  • Check variable names match exactly
  • Ensure data is passed to template
  • Use {{{variable}}} for HTML content

Styles Not Working​

  • Use inline styles for better support
  • Test in target email clients
  • Avoid advanced CSS features

Email Not Sending​

  • Verify SMTP configuration
  • Check email worker is running
  • Review logs for errors
  • Test with simple template first