Doc Variables
← Back to Resources

Google Docs Mail Merge: The Complete Guide to Personalizing Documents at Scale

Google Docs Mail Merge: The Complete Guide to Personalizing Documents at Scale

Google Docs Mail Merge: The Complete Guide to Personalizing Documents at Scale

What Mail Merge Actually Is (And Why Google Docs Makes It Complicated)

Mail merge is one of those features that sounds simple and turns out to have a dozen different ways to do it, each with its own tradeoffs. The core idea is straightforward: take one template document, plug in different data for each recipient, and generate a batch of personalized copies. A proposal template becomes 50 customized proposals. An invoice template becomes 100 unique invoices. A letter template becomes 500 personalized outreach emails.

Microsoft Word has had mail merge built in for decades. Google Docs, despite its dominance in workplaces and schools, never shipped a native equivalent. That gap has spawned an entire ecosystem of workarounds—add-ons, Apps Script automations, third-party integrations—each solving the problem differently.

This guide cuts through the options. You'll see exactly how each method works, what it's best for, and how to set it up from scratch. By the end, you'll know which approach fits your use case and have everything you need to run your first mail merge in Google Docs today.

When You Need Mail Merge

The trigger is always the same: you have structured data and you need that data to live inside formatted documents. Some scenarios where mail merge pays off immediately:

  • Sales proposals: 40 prospects, each needing a customized proposal with their company name, project scope, and pricing
  • HR documents: Offer letters that vary by candidate name, role, salary, and start date
  • Client reports: Monthly performance summaries personalized with each client's metrics
  • Event certificates: 300 attendees, each needing a certificate with their name and completion date
  • Invoices: Batch billing at month-end for recurring retainer clients
  • Legal agreements: Service contracts that share boilerplate but vary by client name, scope, and terms

If you're doing any of this manually—opening a template, copy-pasting names, changing dates, saving, repeat—mail merge eliminates that entirely.

The Core Concept: Templates + Data Sources

Every mail merge setup has two components:

A template — a Google Doc with placeholder variables where the personalized content goes. Variables look like {{First Name}} or <<Client Company>> depending on your tool of choice.

A data source — usually a Google Sheet where each row is one record (one person, one client, one invoice) and each column is a field that maps to a variable in your template.

When you run the merge, the tool reads your data source row by row, makes a copy of the template for each row, and replaces every variable with the corresponding value. The result: one personalized document per row, generated in seconds.

Setting Up Your Template

Before you pick a method, build a clean template. This step is the same regardless of which mail merge approach you use.

Design the Document First

Create a Google Doc with the final formatting you want. Add your logo, set your fonts and colors using Google Docs styles, structure the sections. Make it look exactly like it should look when filled in—except every piece of variable data is still a placeholder.

Insert Variables

For each piece of data that changes per record, insert a variable placeholder. The exact syntax depends on your tool:

  • Doc Variables syntax: {{First Name}}
  • Autocrat syntax: <<First Name>>
  • Apps Script (custom): Whatever you define—often {{First Name}}

A proposal template might look like this:

Dear {{Contact Name}},

Thank you for your interest in working with us. Based on our conversation
regarding {{Project Description}}, we're proposing the following:

Scope: {{Scope of Work}}
Timeline: {{Project Timeline}}
Investment: ${{Project Total}}

This proposal is valid through {{Expiration Date}}.

Best regards,
{{Account Manager Name}}

Variable names should match your spreadsheet column headers exactly—same spelling, same capitalization, same spacing. Case sensitivity matters in most tools.

Use Styles Instead of Manual Formatting

Don't format headings by manually changing fonts and sizes. Use Google Docs' built-in styles (Heading 1, Heading 2, Normal Text). This prevents formatting drift when data gets inserted and makes batch-generated documents consistent.

Setting Up Your Data Source

Your Google Sheet is where all the variable data lives. Each row is one document. Each column is one variable.

Column Headers = Variable Names

Row 1 should contain column headers that exactly match your template variable names. If your template has {{Contact Name}}, your sheet needs a column called Contact Name—not contact_name, not ContactName, not Name.

Clean Your Data

Garbage in, garbage out. Before running a merge:

  • Remove leading and trailing spaces (use =TRIM() in Sheets)
  • Standardize capitalization (=PROPER() for names)
  • Format dates consistently
  • Fill in every required field—blank cells produce blank variables in documents

Add Calculated Columns

Let Google Sheets do the math before it reaches your documents. Add formula columns for:

  • Totals and subtotals
  • Formatted dates: =TEXT(A2, "MMMM DD, YYYY")
  • Conditional flags: =IF(B2>1000,"Yes","No")

Calculated columns mean your template just displays the pre-computed value—no risk of arithmetic errors in the final document.

Method 1: Doc Variables Add-On

Doc Variables is a Google Workspace add-on built specifically for Google Docs template automation. It handles variable replacement, conditional logic, and batch generation from a sidebar inside Google Docs.

Setup

  1. In your template document: Extensions → Add-ons → Get add-ons
  2. Search "Doc Variables" and install
  3. Authorize the required permissions
  4. Open the sidebar: Extensions → Doc Variables → Open

Generating Documents

  1. In the sidebar, click "Connect to Sheet"
  2. Select your data spreadsheet and the correct tab
  3. Doc Variables reads column headers and maps them to template variables automatically
  4. Preview a record to verify variables populate correctly
  5. Click "Generate All" to batch-generate documents from every row
  6. Documents are saved to Google Drive, named automatically from your data

Conditional Logic

Doc Variables supports if/then logic in templates, which is where it significantly outpaces a basic mail merge approach:

{{#if Service Tier == "Premium"}}
As a Premium client, you have access to:
• Dedicated account manager
• 24/7 priority support
• Monthly strategy calls
{{/if}}

{{#if Service Tier == "Standard"}}
Your Standard plan includes email support
and quarterly account reviews.
{{/if}}

One template handles multiple tiers, industries, or scenarios—no manual editing after generation.

Loops for Repeating Content

For invoice line items, deliverable lists, or any variable-length content:

PROJECT DELIVERABLES

{{#each Deliverables}}
• {{this}}
{{/each}}

If your spreadsheet has a comma-separated list of deliverables in one cell, Doc Variables splits it and generates a bullet for each item.

Best for:

Teams that need conditional logic, recurring document generation, or non-technical users who want a UI instead of code.

Method 2: Google Apps Script (DIY Automation)

Apps Script is Google's built-in JavaScript environment. It runs inside Google Workspace and can read your Sheet, create Docs, replace text, move files, and send emails—all without leaving the Google ecosystem.

The Basic Mail Merge Script

In your Google Sheet: Extensions → Apps Script. Paste this:

function runMailMerge() {
  var TEMPLATE_ID = 'YOUR_TEMPLATE_DOC_ID';
  var OUTPUT_FOLDER_ID = 'YOUR_OUTPUT_FOLDER_ID';

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var data = sheet.getDataRange().getValues();
  var headers = data[0];

  var template = DriveApp.getFileById(TEMPLATE_ID);
  var outputFolder = DriveApp.getFolderById(OUTPUT_FOLDER_ID);

  for (var i = 1; i < data.length; i++) {
    var row = data[i];
    if (!row[0]) continue; // skip empty rows

    // Map column headers to values
    var vars = {};
    headers.forEach(function(header, idx) {
      var val = row[idx];
      if (val instanceof Date) {
        val = Utilities.formatDate(val, Session.getScriptTimeZone(), 'MMMM d, yyyy');
      }
      vars[header] = String(val !== null && val !== undefined ? val : '');
    });

    // Copy template
    var fileName = vars[headers[0]] + ' — Document — ' + new Date().toISOString().slice(0,10);
    var newFile = template.makeCopy(fileName, outputFolder);
    var doc = DocumentApp.openById(newFile.getId());
    var body = doc.getBody();

    // Replace variables
    Object.keys(vars).forEach(function(key) {
      body.replaceText('\\{\\{' + key + '\\}\\}', vars[key]);
    });

    doc.saveAndClose();
    Logger.log('Generated: ' + fileName);
  }
}

Get your Template ID from the document's URL: docs.google.com/document/d/THIS_IS_YOUR_ID/edit

Get your Output Folder ID from Drive: drive.google.com/drive/folders/THIS_IS_YOUR_ID

Adding PDF Export

Extend the script to automatically export each document as a PDF:

// After doc.saveAndClose():
var pdf = newFile.getAs('application/pdf');
outputFolder.createFile(pdf.setName(fileName + '.pdf'));
newFile.setTrashed(true); // Delete the Google Doc, keep only the PDF

Triggering on Form Submit

If your data comes from a Google Form, you can trigger document generation automatically when someone submits:

function onFormSubmit(e) {
  // e.values contains the form response values
  // e.range gives you the row in the response sheet
  runMailMergeForRow(e.range.getRow());
}

In Apps Script: Triggers → Add Trigger → Function: onFormSubmit, Event: On form submit.

Now every form submission generates a personalized document automatically—no manual action required.

Best for:

Technical users, custom workflows, integrations with other Google APIs (Gmail for automatic email delivery, Calendar for scheduling, Drive for file organization).

Method 3: Autocrat (Free Add-On)

Autocrat is one of the original Google Docs mail merge tools, widely used in education and nonprofits. It's free and runs from Google Sheets.

Template Syntax

Autocrat uses angle bracket syntax: <<Column Name>>

Dear <<First Name>>,

Your certificate of completion for <<Course Name>>
is attached to this email.

Date completed: <<Completion Date>>

Setup

  1. Install Autocrat from Google Workspace Marketplace (add it to Google Sheets)
  2. Create your template with <<Column Name>> variables
  3. In your Sheets: Add-ons → Autocrat → Launch
  4. Create a new job: specify template, output type (Doc or PDF), output folder
  5. Map columns to variables
  6. Run the job—one document per row

Limitations

Autocrat doesn't support conditional logic natively. Every document gets the same structure—only the variable values change. For simple mail merges (certificates, form letters, basic invoices), that's fine. For complex documents with tier-based content or optional sections, you'll need Doc Variables or Apps Script.

Best for:

Simple, high-volume mail merges. Great for schools generating 500 certificates or nonprofits sending personalized thank-you letters.

Method 4: Zapier or Make (Integrating External Data)

When your data lives outside Google Sheets—in a CRM, payment processor, form builder, or database—Zapier and Make can bridge the gap.

Example Workflow: HubSpot Deal to Proposal

Trigger: Deal moves to "Proposal Stage" in HubSpot

Actions:

  1. Zapier pulls deal data (company name, contact, value, service tier)
  2. Copies your Google Docs template
  3. Fills in variables from HubSpot data
  4. Exports to PDF
  5. Attaches PDF to the HubSpot deal record
  6. Notifies the sales rep via Slack

The rep closes the deal and finds a ready-to-send proposal waiting in HubSpot—generated automatically the moment the deal hit the right stage.

Example Workflow: Stripe Payment to Invoice

Trigger: Successful payment in Stripe

Actions:

  1. Make retrieves payment details
  2. Generates a Google Docs invoice from template
  3. Exports to PDF
  4. Emails PDF to customer via Gmail
  5. Logs invoice to a Google Sheet for accounting

Best for:

Multi-tool workflows where the data trigger comes from outside Google Workspace.

Advanced Techniques

Multi-Template Mail Merge

Sometimes different records need completely different templates. A sales team might have separate templates for new clients vs. returning clients, or for different industries.

In Apps Script, select the template based on row data:

function getTemplateId(clientType) {
  var templates = {
    'New Client': 'TEMPLATE_ID_NEW',
    'Returning Client': 'TEMPLATE_ID_RETURNING',
    'Enterprise': 'TEMPLATE_ID_ENTERPRISE'
  };
  return templates[clientType] || 'TEMPLATE_ID_DEFAULT';
}

// In your loop:
var templateId = getTemplateId(vars['Client Type']);
var template = DriveApp.getFileById(templateId);

One run, multiple templates, each record gets the right one.

Smart Document Naming

Automatically generated filenames save hours of manual renaming:

var fileName = vars['Client Company'] + 
               ' — ' + vars['Document Type'] + 
               ' — ' + new Date().toISOString().slice(0,10);
// Result: "Acme Corp — Service Agreement — 2026-04-13"

Consistent naming makes documents findable weeks or months later.

Auto-Organize Output by Category

function getOrCreateFolder(parentId, name) {
  var parent = DriveApp.getFolderById(parentId);
  var iter = parent.getFoldersByName(name);
  return iter.hasNext() ? iter.next() : parent.createFolder(name);
}

// Usage: sort proposals by industry
var industryFolder = getOrCreateFolder(OUTPUT_FOLDER_ID, vars['Industry']);
var newFile = template.makeCopy(fileName, industryFolder);

Documents self-organize into subfolders. No manual filing.

Batch Emails After Generation

After generating a batch of documents, email each one automatically:

// After doc.saveAndClose():
var pdf = newFile.getAs('application/pdf');
MailApp.sendEmail({
  to: vars['Client Email'],
  subject: 'Your ' + vars['Document Type'] + ' from Our Team',
  body: 'Hi ' + vars['Contact Name'] + ',\n\nPlease find your document attached.',
  attachments: [pdf]
});

Mail merge becomes full document delivery—generate and send in one run.

Tracking Which Records Were Processed

Add a "Generated" column to your sheet and mark rows when documents are produced:

// After successful generation:
sheet.getRange(i + 1, headers.length + 1).setValue(new Date());

Re-run the script anytime—it only processes rows where the Generated column is empty. No duplicate documents.

Comparing the Four Methods

MethodTechnical SkillConditional LogicExternal DataCost
Doc VariablesNoneYesGoogle Sheets onlyPaid (free trial)
Apps ScriptJavaScriptFull flexibilityYes (via API)Free
AutocratNoneNoGoogle Sheets onlyFree
Zapier/MakeNone (visual)LimitedYes (50+ integrations)Paid per task

Quick decision guide:

  • Simple mail merge, no code → Autocrat
  • Conditional content, team use, no code → Doc Variables
  • Custom logic, CRM integration, developer available → Apps Script
  • Non-Google data source, no-code required → Zapier or Make

Common Pitfalls and How to Avoid Them

Pitfall 1: Variable Names Don't Match

The most common reason mail merges fail silently. If your template has {{Client Name}} but your sheet column is ClientName, the variable won't replace. It just stays as-is in the document.

Fix: Copy column headers directly from your sheet and paste them into template variables. Never retype them.

Pitfall 2: Date Fields Show Raw Numbers

Google Sheets stores dates as serial numbers internally. A date formatted as "April 13, 2026" in the sheet might appear as "46,390" in the document.

Fix in Apps Script:

if (val instanceof Date) {
  val = Utilities.formatDate(val, 'America/Chicago', 'MMMM d, yyyy');
}

Fix in Sheets: Add a helper column: =TEXT(A2,"MMMM d, yyyy") and reference that column in your template.

Pitfall 3: Currency Loses Formatting

A cell formatted as "$12,500.00" may arrive in your document as "12500"—the sheet's visual formatting doesn't transfer.

Fix: Use a TEXT formula: =TEXT(G2,"$#,##0.00") in a helper column and reference that in your template instead of the raw number column.

Pitfall 4: Template Drift

Someone edits the master template instead of making a copy. Now every future mail merge uses the modified version.

Fix: Store templates in a folder where most users have view-only access. Only template admins have edit access.

Pitfall 5: No Error Handling in Scripts

Apps Script fails silently by default—one bad row can stop the entire batch without telling you which row failed or why.

Fix: Wrap your loop body in try/catch:

try {
  // generation code
  Logger.log('Generated: ' + fileName);
} catch(e) {
  Logger.log('ERROR on row ' + i + ': ' + e.message);
  // Continue to next row
}

Performance at Scale

For most teams, generating 50-200 documents per run is straightforward. When volume climbs into the thousands, a few things matter:

Apps Script quotas: Google Workspace accounts get 6 hours of script execution per day. Free accounts get 6 minutes per execution. For very large batches, process in chunks of 100 and use script properties to track progress between runs.

Drive API limits: Creating thousands of files quickly can hit rate limits. Add a small pause between creations: Utilities.sleep(500); (500ms between documents).

PDF export: Exporting a Google Doc to PDF makes an API call to Google's conversion service. This is slower than creating a Doc copy. Budget ~2-3 seconds per PDF for large batches.

Building a Repeatable Mail Merge Workflow

A one-time mail merge is useful. A repeatable workflow is transformative. Here's how to build one:

1. Standardize your template library. Store templates in a shared Drive folder with version numbers in the filename. One owner per template. Quarterly review.

2. Standardize your data sheet. Same column headers every time. Data validation to prevent inconsistent values. Calculated columns pre-format what the template needs.

3. Document the process. A one-page README in your template folder: how to add data, how to run the merge, where output goes, who to contact if something breaks.

4. Train your team. 20 minutes with a new team member: here's the sheet, here's how to add a row, here's the "Generate" button (or script). That's it.

5. Track output. A "Generated" column in your data sheet acts as a log. You know exactly which records have been processed and when.

From Mail Merge to Full Automation

Standard mail merge is the starting point. The progression toward full automation looks like:

  1. Manual merge: Add data → run merge → review → send. ~5 minutes per batch.
  2. Scheduled merge: Script triggers automatically on a schedule (first of month, every Friday). Data goes in, documents come out without human action.
  3. Event-driven merge: Form submit or CRM stage change triggers document generation immediately. Document exists before anyone has to ask for it.
  4. Full pipeline: Data enters through any channel → documents generate → review step → automatic delivery. Human involvement only at the review stage.

Start at step 1. Move to step 2 when the manual step feels tedious. Step 3 and 4 when the use case justifies the setup investment.

Getting Your First Merge Running in 30 Minutes

Minutes 0–5: Pick one document you create repeatedly. Build a Google Sheet with column headers for every piece of data that changes. Add 5 sample rows.

Minutes 5–15: Create a Google Docs template. Replace every piece of variable data with a {{Column Name}} placeholder (using the exact same column name from your sheet).

Minutes 15–25: Install Doc Variables (or set up Apps Script with the basic script above). Connect to your sheet. Generate documents for your 5 sample rows.

Minutes 25–30: Open the generated documents. Verify every variable populated correctly. Check formatting. Make any adjustments to the template.

After 30 minutes, you have a working mail merge. Scale it to your real data and run your first production batch.

The hours you spent manually creating documents one by one? Those are behind you.


Doc Variables makes Google Docs mail merge simple—variables, conditional logic, and batch generation from a sidebar in your document. Try it free at docvars.com.

Ready to try Doc Variables?

Join 190,000+ users creating amazing Google Doc templates.

Install Now - It's Free