Twilio SendGrid NodeJS mail service for sending transactional and marketing emails with templates, attachments, and advanced delivery features.
—
Templates and personalization enable dynamic, customized email content for individual recipients. SendGrid supports both modern dynamic templates and legacy templates, with comprehensive personalization capabilities for per-recipient customization.
Modern template system with handlebars-style syntax and rich data binding capabilities.
// Dynamic template message structure
const msg = {
to: recipients,
from: 'sender@example.com',
templateId: 'd-1234567890abcdef', // Dynamic templates start with 'd-'
dynamicTemplateData: templateData // Object with template variables
};
// Template data can include any JSON-serializable data
interface DynamicTemplateData {
[key: string]: any; // Strings, numbers, objects, arrays, booleans
}Usage Examples:
// Basic dynamic template
const msg = {
to: 'user@example.com',
from: 'noreply@example.com',
templateId: 'd-abc123def456',
dynamicTemplateData: {
firstName: 'John',
lastName: 'Doe',
accountBalance: 1250.75,
isVip: true,
orders: [
{ id: '12345', total: 99.99, date: '2024-03-15' },
{ id: '67890', total: 149.50, date: '2024-03-10' }
]
}
};
sgMail.send(msg);
// Complex template data with nested objects
const welcomeEmail = {
to: 'newuser@example.com',
from: 'welcome@example.com',
templateId: 'd-welcome-series-1',
dynamicTemplateData: {
user: {
firstName: 'Alice',
lastName: 'Johnson',
email: 'newuser@example.com',
signupDate: '2024-03-15',
preferences: {
newsletter: true,
notifications: false
}
},
company: {
name: 'Example Corp',
supportUrl: 'https://example.com/support',
unsubscribeUrl: 'https://example.com/unsubscribe'
},
features: [
{ name: 'Dashboard', available: true },
{ name: 'Analytics', available: false },
{ name: 'API Access', available: true }
]
}
};Traditional template system using substitution tags for simple variable replacement.
// Legacy template message structure
const msg = {
to: recipients,
from: 'sender@example.com',
templateId: 'legacy-template-id', // Legacy templates don't start with 'd-'
substitutions: substitutionData, // Object mapping tags to values
substitutionWrappers: ['::', '::'] // Optional custom wrappers
};
// Substitution format
interface Substitutions {
[key: string]: string; // All values must be strings
}Usage Examples:
// Basic legacy template
const msg = {
to: 'user@example.com',
from: 'noreply@example.com',
templateId: 'password-reset-template',
subject: 'Password Reset for ::firstName::',
substitutions: {
'::firstName::': 'John',
'::resetUrl::': 'https://app.example.com/reset?token=abc123',
'::expiryTime::': '24 hours'
}
};
// Custom substitution wrappers
const customMsg = {
to: 'user@example.com',
from: 'noreply@example.com',
templateId: 'custom-template',
substitutions: {
'[[username]]': 'johndoe',
'[[activationCode]]': '123456'
},
substitutionWrappers: ['[[', ']]']
};
// Template sections for reusable content blocks
const sectionMsg = {
to: 'user@example.com',
from: 'noreply@example.com',
templateId: 'newsletter-template',
sections: {
'::header::': '<h1>Monthly Newsletter</h1>',
'::footer::': '<p>© 2024 Example Corp</p>',
'::promotions::': '<div>Special offers this month!</div>'
},
substitutions: {
'::firstName::': 'John',
'::month::': 'March'
}
};Per-recipient customization allowing different template data, subjects, and recipients for each personalization.
// Personalization data structure
interface PersonalizationData {
to: EmailData | EmailData[]; // Required recipients
from?: EmailData; // Override sender
cc?: EmailData | EmailData[]; // CC recipients
bcc?: EmailData | EmailData[]; // BCC recipients
subject?: string; // Override subject
headers?: { [key: string]: string }; // Custom headers
substitutions?: { [key: string]: string }; // Legacy template data
dynamicTemplateData?: { [key: string]: any }; // Dynamic template data
customArgs?: { [key: string]: string }; // Custom tracking args
sendAt?: number; // Scheduled send time
}
// Use personalizations in mail data
const msg = {
from: 'sender@example.com',
templateId: 'd-personalized-template',
personalizations: [personalizationData]
};Usage Examples:
// Multiple personalizations for different recipients
const msg = {
from: 'newsletter@example.com',
templateId: 'd-monthly-newsletter',
personalizations: [
{
to: [{ email: 'premium@example.com', name: 'Premium User' }],
subject: 'Premium Newsletter - Exclusive Content',
dynamicTemplateData: {
firstName: 'John',
subscriptionType: 'Premium',
exclusiveContent: true,
featuredArticles: [
{ title: 'Advanced Features Guide', url: 'https://example.com/premium1' },
{ title: 'Investment Strategies', url: 'https://example.com/premium2' }
]
}
},
{
to: [{ email: 'basic@example.com', name: 'Basic User' }],
subject: 'Monthly Newsletter - Latest Updates',
dynamicTemplateData: {
firstName: 'Jane',
subscriptionType: 'Basic',
exclusiveContent: false,
featuredArticles: [
{ title: 'Getting Started Guide', url: 'https://example.com/basic1' },
{ title: 'Tips and Tricks', url: 'https://example.com/basic2' }
]
}
}
]
};
// Personalization with different send times
const scheduledMsg = {
from: 'reminders@example.com',
templateId: 'd-appointment-reminder',
personalizations: [
{
to: ['patient1@example.com'],
subject: 'Appointment Reminder - Tomorrow at 10 AM',
sendAt: Math.floor(Date.now() / 1000) + 86400, // 24 hours from now
dynamicTemplateData: {
patientName: 'John Smith',
appointmentDate: '2024-03-16',
appointmentTime: '10:00 AM',
doctorName: 'Dr. Johnson'
}
},
{
to: ['patient2@example.com'],
subject: 'Appointment Reminder - Tomorrow at 2 PM',
sendAt: Math.floor(Date.now() / 1000) + 86400, // 24 hours from now
dynamicTemplateData: {
patientName: 'Jane Doe',
appointmentDate: '2024-03-16',
appointmentTime: '2:00 PM',
doctorName: 'Dr. Williams'
}
}
]
};Use the Personalization class for programmatic construction of personalization objects.
const { Personalization } = require('@sendgrid/helpers').classes;
/**
* Create a new personalization instance
* @param data - Optional initial personalization data
*/
const personalization = new Personalization(data);
// Recipient management
personalization.setTo(to);
personalization.addTo(to);
personalization.setCc(cc);
personalization.addCc(cc);
personalization.setBcc(bcc);
personalization.addBcc(bcc);
// Content customization
personalization.setSubject(subject);
personalization.setSendAt(sendAt);
personalization.setDynamicTemplateData(data);
personalization.setSubstitutions(substitutions);
// Headers and tracking
personalization.setHeaders(headers);
personalization.addHeader(key, value);
personalization.setCustomArgs(customArgs);
personalization.addCustomArg(key, value);
// Substitution management
personalization.addSubstitution(key, value);
personalization.reverseMergeSubstitutions(substitutions);
personalization.setSubstitutionWrappers(wrappers);
// Convert to JSON
const jsonData = personalization.toJSON();Usage Examples:
const { Personalization } = require('@sendgrid/helpers').classes;
// Build personalization programmatically
const personalization = new Personalization();
// Set recipients
personalization.setTo([
{ email: 'user1@example.com', name: 'User One' },
{ email: 'user2@example.com', name: 'User Two' }
]);
personalization.addCc('manager@example.com');
// Set custom subject
personalization.setSubject('Personal Message for Your Team');
// Add dynamic template data
personalization.setDynamicTemplateData({
teamName: 'Development Team',
projectStatus: 'On Track',
deadline: '2024-04-01',
completionPercentage: 75
});
// Add custom tracking
personalization.setCustomArgs({
teamId: 'dev-team-1',
projectId: 'proj-2024-q1'
});
// Add custom headers
personalization.addHeader('X-Team-Priority', 'High');
// Use in email
const msg = {
from: 'pm@example.com',
templateId: 'd-team-update-template',
personalizations: [personalization]
};Utility functions for working with template data and substitutions.
// Global template data application (Mail class methods)
const mail = new Mail();
/**
* Apply global substitutions to a personalization
* @param personalization - Personalization instance to modify
*/
mail.applySubstitutions(personalization);
/**
* Apply global dynamic template data to a personalization
* @param personalization - Personalization instance to modify
*/
mail.applyDynamicTemplateData(personalization);
// Substitution merging (Personalization class method)
/**
* Merge substitutions while preserving existing ones
* @param substitutions - New substitutions to merge
*/
personalization.reverseMergeSubstitutions(substitutions);Usage Examples:
const { Mail, Personalization } = require('@sendgrid/helpers').classes;
// Global template data
const mail = new Mail();
mail.setDynamicTemplateData({
companyName: 'Example Corp',
supportEmail: 'support@example.com',
unsubscribeUrl: 'https://example.com/unsubscribe'
});
// Individual personalization
const personalization = new Personalization();
personalization.setTo('user@example.com');
personalization.setDynamicTemplateData({
firstName: 'John',
accountBalance: 1500.00
});
// Apply global data to personalization
mail.applyDynamicTemplateData(personalization);
// Now personalization has both global and individual data
// Merge substitutions safely
personalization.setSubstitutions({
'::firstName::': 'John',
'::accountType::': 'Premium'
});
personalization.reverseMergeSubstitutions({
'::firstName::': 'Johnny', // Won't override existing
'::lastName::': 'Doe' // Will be added
});
// Result: firstName stays 'John', lastName becomes 'Doe'{{user.firstName}} instead of {{n}}// Handle template errors
sgMail.send(templateMsg)
.catch(error => {
if (error.response && error.response.body) {
const { errors } = error.response.body;
errors.forEach(err => {
if (err.field === 'template_id') {
console.error('Template not found or invalid:', err.message);
} else if (err.field === 'personalizations') {
console.error('Personalization error:', err.message);
}
});
}
});Install with Tessl CLI
npx tessl i tessl/npm-sendgrid--mail