Dynamic Token Replacement in Google Docs Using Sheets Data

I need help creating a flexible system to populate Google Doc templates with spreadsheet information. Right now I’m manually coding each placeholder replacement like this:

// Current approach - hardcoded replacements
docContent.replaceText('{{Name}}', sheetRow[0]);
docContent.replaceText('{{Email}}', sheetRow[1]);
docContent.replaceText('{{Department}}', sheetRow[2]);

This works but it’s not flexible. I want to automatically replace any placeholder that matches a column header in my spreadsheet. I found a dynamic approach that works for email templates:

function populateTemplateWithData_(templateContent, rowData) {
  let contentString = JSON.stringify(templateContent);
  
  contentString = contentString.replace(/{{[^{}]+}}/g, placeholder => {
    return sanitizeText_(rowData[placeholder.replace(/[{}]+/g, "")]) || "";
  });
  return JSON.parse(contentString);
}

function sanitizeText_(text) {
  return text
    .replace(/[\\]/g, '\\\\')
    .replace(/[\"]]/g, '\\"')
    .replace(/[\n]/g, '\\n')
    .replace(/[\r]/g, '\\r')
    .replace(/[\t]/g, '\\t');
}

Can someone help me adapt this method to work with Google Docs instead of just email templates? I want any placeholder in my document to automatically get replaced if there’s a matching column in my sheet.

JSON.stringify won’t work here - the Docs API body isn’t serializable like that. First grab all your placeholders, then loop through the headers. Use body.findText('{{.*}}') to find each placeholder, pull out the text between brackets, and match it to your column headers. Much cleaner than hardcoding it all.

Your main problem will be edge cases - empty cells and special characters that break replacements. I’ve dealt with this before. Use toString() on sheet values and add null checks to avoid weird errors. Also try getText() first to see if the placeholder exists before replacing. Saves time on big documents.

These code solutions work, but you’re building a maintenance nightmare. Every time your sheet structure changes, you’ll be back debugging Apps Script.

I spent months on similar document automation and learned the hard way that coding every edge case isn’t sustainable. What happens when you need conditional formatting? Or different templates for different rows? The complexity explodes.

Skip the custom code entirely. I switched to Latenode after too many late nights fixing broken scripts.

Just connect your Google Sheet and Doc, map columns to placeholders visually, and it handles dynamic replacement automatically. No regex patterns, no findText loops, no header mapping objects.

Works instantly with any sheet structure changes. Plus it handles bulk processing when you’re generating hundreds of documents.

Saved me 40 hours of coding and debugging on our last project. Way more reliable than Apps Script.

Had the exact same issue building our invoice generator. The trick is using findText() with the search continuation parameter so you don’t miss replacements. Here’s what worked for me:

function replaceDocumentTokens(docId, headers, rowData) {
  const doc = DocumentApp.openById(docId);
  const body = doc.getBody();
  
  // Create header-to-value mapping
  const dataMapping = {};
  headers.forEach((header, idx) => {
    dataMapping[header] = rowData[idx] ? String(rowData[idx]) : '';
  });
  
  // Find and replace each token
  let searchResult = body.findText('{{.+?}}');
  while (searchResult) {
    const element = searchResult.getElement();
    const foundText = searchResult.getElement().asText();
    const fullText = foundText.getText();
    
    // Extract token name
    const tokenMatch = fullText.substring(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive() + 1);
    const tokenName = tokenMatch.replace(/[{}]/g, '');
    
    if (dataMapping.hasOwnProperty(tokenName)) {
      foundText.replaceText(tokenMatch, dataMapping[tokenName]);
    }
    
    searchResult = body.findText('{{.+?}}', searchResult);
  }
}

Keeps document formatting intact and handles any number of dynamic columns without breaking.

You need to work with the Document API’s text elements directly - don’t stringify the content. I hit the same problem automating contract generation from our client database. Here’s what actually works: build a mapping object from your sheet headers first, then do one pass through the document. Grab your column headers from the first row, create key-value pairs with your data row, then use replaceText() with regex. javascript function replaceAllTokens(docId, headerRow, dataRow) { const doc = DocumentApp.openById(docId); const body = doc.getBody(); // Create mapping object const dataMap = {}; headerRow.forEach((header, index) => { dataMap[header] = dataRow[index] || ''; }); // Replace all tokens in one pass const tokenPattern = '{{(.+?)}}'; let foundElement = body.findText(tokenPattern); while (foundElement) { const element = foundElement.getElement(); const text = element.asText(); const match = foundElement.getElement().getText().match(new RegExp(tokenPattern)); if (match && dataMap.hasOwnProperty(match[1])) { text.replaceText('{{' + match[1] + '}}', dataMap[match[1]]); } foundElement = body.findText(tokenPattern, foundElement); } } This handles any number of columns without hardcoding and keeps your document formatting intact better than running multiple replace operations.

I’ve dealt with this document automation mess for years. Google Docs won’t let you stringify and parse content like emails - that’s the main problem.

Here’s what actually works for dynamic replacement:

function dynamicDocReplacement(docId, sheetData, headers) {
  const doc = DocumentApp.openById(docId);
  const body = doc.getBody();
  
  // Loop through each header and replace matching placeholders
  headers.forEach((header, index) => {
    const placeholder = '{{' + header + '}}';
    body.replaceText(placeholder, sheetData[index] || '');
  });
}

This approach still sucks though. Different data types break it, formatting gets weird, and it becomes a nightmare when you scale up.

I ditched all that and switched to Latenode. It connects Google Sheets and Docs directly and handles dynamic replacement automatically. You just drag and drop to map sheet columns to document placeholders.

No more regex hell or coding every single field. Handles bulk processing too when you need multiple documents from your sheet.

Takes 10 minutes to set up instead of hours debugging Apps Script. Way more reliable.