// Slides · Apps Script

Replace text placeholders across a deck in Google Slides.

Use Apps Script's replaceAllText() to swap {{token}} placeholders across every slide in a Google Slides deck — with matchCase set to true so partial matches never corrupt real copy.

I need to fill a Google Slides template with dynamic values (names, dates, company fields) without manually editing every text box on every slide.

The script

copy · paste · trigger
replacePlaceholders.gs
Apps Script
// Replace {{token}} placeholders across an entire Slides deck.
// matchCase = true prevents {{name}} from also clobbering {{full_name}}.
function fillDeckFromData() {
  var DECK_ID = '1aBcDeFgHiJkLmNoPqRsTuVwXyZ'; // replace with your deck ID
  var pres = SlidesApp.openById(DECK_ID);

  var replacements = {
    '{{first_name}}': 'Maria',
    '{{company}}':    'Acme Corp',
    '{{date}}':       'June 12, 2026',
    '{{tier}}':       'Professional'
  };

  for (var token in replacements) {
    pres.replaceAllText(token, replacements[token], true);
  }

  pres.saveAndClose();
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

The matchCase trap that ruins your template

Presentation.replaceAllText(searchText, replacement) accepts an optional third argument, matchCase. It defaults to false. That sounds harmless until you have both {{name}} and {{full_name}} in the same deck: a case-insensitive search for {{name}} will match inside {{full_name}} first, leaving you with a garbled token like {{fullMaria}} on slide 3.

Passing true as the third argument locks the search to an exact byte-for-byte match. Combined with a consistent delimiter convention — double curly braces, lowercase, underscores for spaces — your tokens never collide with real slide copy. The first time I hit the garbled-token bug it was in a 40-slide sales deck sent to a live prospect; the fix is one boolean.

Choose your delimiter once and enforce it in the template. {{first_name}} is unambiguous; name or FIRST_NAME without delimiters will eventually match something in a headline and you will not catch it until after the send.

How replaceAllText() reaches every shape

The method operates at the Presentation level, not the Slide level. One call walks every slide, every layout, and every master in the deck — including text inside grouped shapes, tables, and speaker notes. You do not need to loop over slides yourself.

The replacement is applied to the raw text run content. It does not touch font size, color, or paragraph style on the replaced span, which means your template's formatting survives the substitution intact. If you need to change formatting on replaced text (bold a name, colorize a tier label), you have to iterate shapes manually after the fact using Shape.getText().getRuns().

saveAndClose() at the end flushes the changes and releases the lock. If you omit it, changes are still written on script exit, but calling it explicitly is the safe pattern for any script that touches a shared file.

Driving it from a Sheet row

The common production pattern is one Sheet row per recipient: columns map to token names, and the script reads the active row, builds the replacements object, makes a copy of the master deck with DriveApp.getFileById(DECK_ID).makeCopy(recipientName), fills the copy, then exports or shares it.

DriveApp.makeCopy() returns a File object; call getId() on it to get the deck ID you pass into SlidesApp.openById(). Keep the master deck untouched by always working on the copy — replaceAllText() is destructive and there is no undo inside Apps Script.

If you are generating more than 20 or 30 decks in a single run, wrap the loop in a try/catch and log failures to a second sheet. The Slides API has a per-minute quota (currently 300 write requests per minute per project); a tight loop over a large list will hit it and the default error message does not tell you which row failed.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Does replaceAllText() work inside tables and speaker notes?
Yes. The method scans all text content in the presentation, including table cells and speaker notes. Shapes inside groups are also included. The one place it does not reach is alt text (accessibility descriptions) — those are not text runs and require a separate API call.
My placeholder is not being replaced even though it looks right in the slide.
Google Slides sometimes splits a single visible token across multiple text runs internally, especially if you typed it character-by-character or edited it later. Open the slide, delete the token, and retype it in one pass without clicking away. You can also diagnose this by calling Shape.getText().getRuns() in a helper script and logging each run's content — you will see the token split across two or three entries.
Can I replace a placeholder with formatted text (bold, a different color)?
No, not with replaceAllText(). The method only substitutes the string content; it cannot inject a formatted span. The workaround is to replace with a unique sentinel string, then iterate shapes to find that sentinel with getText().find(), and apply formatting to the returned TextRange.
How do I target only specific slides instead of the whole deck?
replaceAllText() has no slide-range filter. To target specific slides, skip the Presentation-level method and instead call getSlides(), filter the array to the slides you want, then loop over each slide's shapes and call Shape.getText().replaceAllText() on each shape individually. The shape-level variant takes the same (searchText, replacement, matchCase) signature.