Create Dynamic HTML Email Templates with Apps Script

Hardcoding HTML strings in your script is messy and hard to maintain. Apps Script's HtmlService supports proper HTML template files — keeping your markup clean and your code readable.

Method 1: Simple string interpolation

For quick, one-off emails, build HTML strings directly:

function sendSimpleTemplate(name, product, amount) { const html = ` <div style="font-family:sans-serif;max-width:600px;margin:0 auto;"> <h2 style="color:#1a73e8;">Order Confirmation</h2> <p>Hi ${name},</p> <p>Thank you for your order of <strong>${product}</strong>.</p> <p>Total: <strong>$${amount}</strong></p> <a href="#" style="display:inline-block;padding:12px 24px;background:#1a73e8;color:#fff;text-decoration:none;border-radius:4px;"> View Order </a> </div> `; GmailApp.sendEmail('[email protected]', 'Your Order Confirmation', '', { htmlBody: html }); }

Method 2: HtmlService template files

For complex, reusable templates, use .html files in your Apps Script project.

Create an HTML template file

In the script editor, click + > HTML file and name it EmailTemplate.

<!DOCTYPE html> <html> <body style="font-family:sans-serif;margin:0;padding:0;background:#f5f5f5;"> <div style="max-width:600px;margin:24px auto;background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.08);"> <!-- Header --> <div style="background:#1a73e8;padding:24px 32px;"> <h1 style="color:#fff;margin:0;font-size:22px;"><?= title ?></h1> </div> <!-- Body --> <div style="padding:32px;"> <p>Hi <?= name ?>,</p> <p><?= message ?></p> <? if (items && items.length > 0) { ?> <table width="100%" cellpadding="8" cellspacing="0" style="border-collapse:collapse;margin-top:16px;"> <tr style="background:#f1f3f4;"> <th align="left">Item</th> <th align="right">Amount</th> </tr> <? for (var i = 0; i < items.length; i++) { ?> <tr style="border-bottom:1px solid #eee;"> <td><?= items[i].name ?></td> <td align="right">$<?= items[i].amount ?></td> </tr> <? } ?> </table> <? } ?> </div> <!-- Footer --> <div style="padding:16px 32px;background:#f8f9fa;text-align:center;"> <p style="font-size:12px;color:#9aa0a6;margin:0;"> © <?= new Date().getFullYear() ?> Your Company. All rights reserved. </p> </div> </div> </body> </html>

Render and send the template

function sendTemplateEmail() { const template = HtmlService.createTemplateFromFile('EmailTemplate'); // Inject data template.title = 'Your Invoice is Ready'; template.name = 'Alice Smith'; template.message = 'Here is a summary of your recent purchase:'; template.items = [ { name: 'Apps Script Course', amount: '49.00' }, { name: 'Template Pack', amount: '19.00' }, ]; const htmlBody = template.evaluate().getContent(); GmailApp.sendEmail( '[email protected]', 'Your Invoice — March 2025', 'Please view this email in an HTML-compatible client.', { htmlBody } ); Logger.log('Template email sent.'); }

Send personalised emails to everyone in a Sheet

function sendBulkTemplateEmails() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); const data = sheet.getDataRange().getValues().slice(1); data.forEach(([name, email, product, amount, sent]) => { if (sent === 'Yes' || !email) return; const template = HtmlService.createTemplateFromFile('EmailTemplate'); template.title = 'Your Order Confirmation'; template.name = name; template.message = `Your order has been received and is being processed.`; template.items = [{ name: product, amount: amount }]; const htmlBody = template.evaluate().getContent(); GmailApp.sendEmail(email, 'Order Confirmation', '', { htmlBody }); // Mark as sent const rowIndex = data.indexOf([name, email, product, amount, sent]) + 2; sheet.getRange(rowIndex, 5).setValue('Yes'); }); }

Include a shared header/footer file

function getTemplate(filename) { return HtmlService.createTemplateFromFile(filename); } function include(filename) { return HtmlService.createHtmlOutputFromFile(filename).getContent(); }

In your template HTML:

<?!= include('EmailHeader') ?> <p>Your main content here.</p> <?!= include('EmailFooter') ?>

Tips

  • <?= ... ?> outputs escaped HTML (safe). <?!= ... ?> outputs raw HTML (use for including other HTML files).
  • <? ... ?> is for scriptlets — conditions, loops, variable declarations.
  • Always test email HTML across clients (Gmail, Outlook, Apple Mail) — they all render CSS differently.
  • Keep styles inline — most email clients ignore <style> blocks entirely.
  • The HtmlService template syntax is based on JSP/EJS-style scriptlets, not modern template literals.