How to Generate a PDF or Google Doc for Each Row in Google Sheets (2026)

You have a sheet with one row per person, client or student, and you need one document per row. Google Sheets has no built-in "make a document per row" button, but there are three practical routes: a short Apps Script you own, the built-in mail merge in Google Docs (limited), or an add-on. Here is each one, free option first.

Watch the 2-minute walkthrough on YouTube. Short on time? Watch the 50-second version.

Option 1: a free Apps Script (works today)

  1. Create a Google Doc template. Wherever a value should go, type the column header in double braces, such as {{Name}} or {{Course}}. The text must match the header in row 1 exactly.
  2. Create a Drive folder for the output and copy its ID from the URL (the part after /folders/). Copy the template's ID from its URL too (between /d/ and /edit).
  3. In your sheet, open Extensions › Apps Script, delete the sample code, paste the script below, and fill in the two IDs.
  4. Save, reload the sheet, then use the new Merge › Create docs for new rows menu. Approve the permission prompt the first time.
/**
 * One Google Doc + PDF per row.
 * Sheet: row 1 = headers (e.g. Name, Course, Date). Template Doc uses {{Name}}, {{Course}}, {{Date}}.
 * Adds "Doc link" and "PDF link" columns; rows that already have a PDF link are skipped.
 */
const TEMPLATE_ID = 'PASTE_TEMPLATE_DOC_ID';
const FOLDER_ID   = 'PASTE_OUTPUT_FOLDER_ID';

function mergeRows() {
  const sh = SpreadsheetApp.getActiveSheet();
  const data = sh.getDataRange().getDisplayValues();   // display values keep your date/currency formatting
  const head = data[0];
  let docCol = head.indexOf('Doc link'), pdfCol = head.indexOf('PDF link');
  if (docCol < 0) { docCol = head.length; sh.getRange(1, docCol + 1).setValue('Doc link'); head.push('Doc link'); }
  if (pdfCol < 0) { pdfCol = head.length; sh.getRange(1, pdfCol + 1).setValue('PDF link'); head.push('PDF link'); }
  const folder = DriveApp.getFolderById(FOLDER_ID);
  const template = DriveApp.getFileById(TEMPLATE_ID);
  const start = Date.now();

  for (let r = 1; r < data.length; r++) {
    const row = data[r];
    if (row[pdfCol]) continue;                          // already done
    if (Date.now() - start > 5 * 60 * 1000) break;      // stay under the 6-minute run limit; run again to continue
    const name = (row[0] || 'Row ' + (r + 1)) + ' - ' + Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd');
    const copy = template.makeCopy(name, folder);
    const doc = DocumentApp.openById(copy.getId());
    const body = doc.getBody();
    head.forEach((h, i) => {
      if (!h) return;
      body.replaceText('\\{\\{' + h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\}\\}', row[i] || '');
    });
    doc.saveAndClose();
    const pdf = folder.createFile(copy.getAs(MimeType.PDF)).setName(name + '.pdf');
    sh.getRange(r + 1, docCol + 1).setValue(copy.getUrl());
    sh.getRange(r + 1, pdfCol + 1).setValue(pdf.getUrl());
    SpreadsheetApp.flush();
  }
}

function onOpen() {
  SpreadsheetApp.getUi().createMenu('Merge').addItem('Create docs for new rows', 'mergeRows').addToUi();
}

What it does: copies the template per row, replaces every {{Header}} with that row's displayed value, exports a PDF next to the Doc, and writes both links back so a second run only processes new rows. Delete the Doc copy afterwards if you only want PDFs.

Limits to know

Permissions caveat: because this script calls DriveApp and DocumentApp.openById, Google will ask you to allow it to "see, edit, create and delete all of your Google Drive files" and all your Docs. That is fine for a script you wrote and own, but it is the same broad access most mail-merge add-ons request. If that matters for your organisation, look for a tool that uses the drive.file scope, which only touches files you pick or the tool creates.

Option 2: mail merge built into Google Docs

Some Google Workspace editions include a mail merge feature in Google Docs that pulls fields from a Google Sheet, aimed mainly at sending personalised emails from a Doc. Availability depends on your Workspace edition, and it is not designed to save one separate Doc or PDF per row into a Drive folder. If your account has it and you just need personalised emails, try it first. If you need files, use Option 1 or an add-on.

Option 3: an add-on

Add-ons wrap the same idea in a sidebar, with triggers, file naming and error handling. Established options include Autocrat (free), Document Studio, Portant and Form Publisher; see our Autocrat alternatives guide for prices and permissions checked November 5, 2026. Most of them request access to all of your Drive files.

Tips that save a re-run

Want this without maintaining a script? SheetWeave (disclosure: we make it) is a Google Sheets add-on that fills a Google Docs {{placeholder}} template from each row and saves Docs, PDFs or both into a Drive folder you choose, writing the output links back to the sheet. It uses narrow, non-restricted scopes only (spreadsheets.currentonly, drive.file, script.container.ui, and script.external_request for the license check), so it cannot browse your whole Drive or read Gmail, and your sheet and document content never leaves your Google account. 14-day free trial with no credit card, then $8/month or $59/year. It is live on the Google Workspace Marketplace.
See SheetWeave

Related Google Sheets guides

Third-party details come from each vendor's own site and Google Workspace Marketplace listing, checked November 5, 2026. Not affiliated with Google, New Visions, Digital Inspiration, Tofu Labs or any other vendor named here. Google Sheets, Google Docs and Google Drive are trademarks of Google LLC. Features and prices change, so verify before you buy. Questions or corrections: hello@loomwise.app.