Digest emails beat screenshots of a sheet. htmlTableFromObjects(rows, columns) turns an array of objects into a simple escaped <table> you can drop into HtmlBody. Pair it with sendHtmlEmail — it’s a table builder, not a full report engine.
What you'll need
- An array of row objects (
[{ name, email, status }, …]) columns: string keys or{ key, label }objects- Something that sends HTML (GmailApp /
sendHtmlEmail)
How to use this snippet
/**
* Escape text for HTML table cells.
* @param {*} value
* @return {string}
*/
function escapeHtml_(value) {
return String(value == null ? '')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* Build a simple HTML table from object rows.
* @param {Object[]} rows
* @param {Array<string|{key:string,label?:string}>} columns
* @return {string} HTML <table>…</table>
*/
function htmlTableFromObjects(rows, columns) {
rows = rows || [];
columns = (columns || []).map(function (c) {
if (typeof c === 'string') return { key: c, label: c };
return { key: c.key, label: c.label != null ? c.label : c.key };
});
var html = '<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;font-family:Arial,sans-serif;font-size:13px;">';
html += '<thead><tr>';
columns.forEach(function (col) {
html += '<th align="left">' + escapeHtml_(col.label) + '</th>';
});
html += '</tr></thead><tbody>';
rows.forEach(function (row) {
html += '<tr>';
columns.forEach(function (col) {
var cell = row && row[col.key];
html += '<td>' + escapeHtml_(cell) + '</td>';
});
html += '</tr>';
});
html += '</tbody></table>';
return html;
}
Example
function sendNightlyDigest_() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Exceptions');
var values = sheet.getDataRange().getValues();
var headers = values.shift();
var rows = values.map(function (r) {
var o = {};
headers.forEach(function (h, i) { o[h] = r[i]; });
return o;
}).filter(function (o) { return o.status === 'FAIL'; });
var table = htmlTableFromObjects(rows, [
{ key: 'orderId', label: 'Order' },
{ key: 'reason', label: 'Reason' },
'status'
]);
var body = '<p>' + rows.length + ' failures overnight.</p>' + table;
// pair with your sendHtmlEmail helper
GmailApp.sendEmail(Session.getActiveUser().getEmail(), 'Nightly exceptions', '', {
htmlBody: body
});
}
Tips:
- Always escape — user-entered sheet cells can contain
<and break (or worse, inject) HTML. - Keep columns explicit; don’t dump every object key into a digest.
- Empty
rowsstill returns a header-only table so the email layout stays stable. - For big reports, link to the sheet instead of stuffing hundreds of rows into Gmail.
Tip: NitroGAS Co-Pilot can draft the columns list and wire htmlTableFromObjects into your existing sendHtmlEmail digest — you still pick which fields leave the sheet.
Happy Coding!
