A recurring pitch deck or onboarding presentation often only needs a handful of names and numbers changed per audience, yet rebuilding it by hand every time invites copy-paste mistakes.
This script duplicates a Slides template into a destination folder with makeCopy, opens the new file with SlidesApp, and calls replaceAllText across the whole presentation to inject personalized values.
Because replaceAllText operates on the entire presentation rather than one slide at a time, a single call updates a placeholder wherever it appears, whether that is the title slide or a chart caption three slides in.
You end up with one finished, ready-to-present deck per recipient, generated in seconds instead of duplicated and edited by hand in the Slides editor.
Need this built? Hire a Google Apps Script developer →
Sheet / project setup
| Resource | Name / value | Purpose |
|---|---|---|
| Slides template | TEMPLATE_SLIDES_ID | makeCopy source |
| Dest folder | DEST_FOLDER_ID | Generated decks |
| Placeholders | {{Client}}, {{Quarter}} | replaceAllText tokens |
What It Does
The script copies a Slides template into a destination folder, renaming the copy to include the recipient's name, then opens the new presentation and calls replaceAllText once for each placeholder to fill it in.
Because SlidesApp.replaceAllText works across the whole file, placeholders on the title slide, in speaker notes, and inside text boxes on later slides are all updated from the same set of calls.
Prerequisites
Build your Slides template with clearly bracketed tokens such as {{RecipientName}} placed wherever personalization is needed, and avoid using the same bracket syntax for anything else in the deck.
Set up a destination Drive folder for finished decks and confirm your account has edit access to both the template and that folder.
Walkthrough
Set TEMPLATE_ID and DEST_FOLDER_ID at the top of the script, then call createSlidesFromTemplate with an object of placeholder names mapped to values for one recipient.
Run the function once with test data, open the resulting file in Slides, and click through every slide to confirm each placeholder was replaced and nothing was left showing curly braces.
Once the single-recipient case looks correct, loop the function over a list of recipients pulled from a spreadsheet to generate the full batch of decks.
Edge Cases
A placeholder that appears inside a table on a Slides page is still replaced correctly, since replaceAllText searches text across all shapes and tables on every slide, not just standalone text boxes.
If two placeholders share a common substring, such as {{Name}} and {{CompanyName}}, replacing the shorter token first can corrupt the longer one, so always replace the more specific, longer tokens before the shorter ones.
Testing
Generate two decks for two different recipients and open both side by side to confirm personalization values are correctly isolated per file with no leakage between them.
Deliberately leave one placeholder out of the data object, generate a deck, and confirm the unmatched token remains visible so it is easy to catch before sending the deck out.
Hardening
Sort placeholder replacement calls by token length, longest first, to avoid the substring collision problem described above without having to manually reorder the object every time a new token is added.
Add a final pass that searches the generated presentation's text for any remaining double-curly-brace pattern and logs a warning, mirroring the safety check used in the Doc placeholder tutorial.
Variations
Export each generated deck straight to PDF using the export-to-PDF pattern from this series so recipients receive a static file instead of an editable Slides link.
Read the mapping between recipients and their personalization values directly from a spreadsheet so the whole batch can be generated by a single script run tied to a menu item.
createSlidesFromTemplate.gs
createSlidesFromTemplate copies a Slides template, sorts placeholder keys longest first to avoid substring collisions, and calls replaceAllText across the whole presentation.
// Copy a Slides template and personalize it with replaceAllText
function createSlidesFromTemplate(data) {
var TEMPLATE_ID = 'REPLACE_WITH_TEMPLATE_ID';
var DEST_FOLDER_ID = 'REPLACE_WITH_DEST_FOLDER_ID';
var template = DriveApp.getFileById(TEMPLATE_ID);
var destFolder = DriveApp.getFolderById(DEST_FOLDER_ID);
var copy = template.makeCopy(data.RecipientName + ' - Deck', destFolder);
var presentation = SlidesApp.openById(copy.getId());
var keys = Object.keys(data).sort(function (a, b) { return b.length - a.length; });
for (var i = 0; i < keys.length; i++) {
var token = '{{' + keys[i] + '}}';
presentation.replaceAllText(token, data[keys[i]]);
}
presentation.saveAndClose();
Logger.log('Created deck: ' + copy.getUrl());
return copy.getId();
}- Line 8: makeCopy duplicates the whole template presentation in one call, landing the copy directly in the destination folder with a personalized name.
- Line 11: Sorting keys by length, longest first, prevents a shorter token like {{Name}} from partially matching inside a longer token like {{CompanyName}}.
- Line 15: replaceAllText updates every slide, table, and speaker note in the presentation in a single call rather than requiring a per-slide loop.
- Line 18: saveAndClose commits the replacements, similar to the Docs equivalent, before the function hands back control to the caller.
- Line 20: Returning the new file's ID lets a calling script immediately chain further steps, such as exporting the deck to PDF.
Deploy this example
- 01
Open Apps Script
In the bound spreadsheet: Extensions → Apps Script. For standalone projects, create one at script.google.com and link your Sheet by ID.
- 02
Paste and save
Add a .gs file, paste the code below, rename constants at the top (sheet names, column letters, API property keys), then save.
- 03
Authorize once
Run the main function from the editor. Accept OAuth scopes when prompted — triggers cannot run until authorization succeeds once.
- 04
Add the trigger
Triggers → Add trigger → choose the handler function and event (time-driven, on edit, or on form submit). Delete test triggers before production.
Before you run: create slides from template
- 1Slides template built with clearly bracketed placeholders
- 2Destination folder created for generated decks
- 3TEMPLATE_ID and DEST_FOLDER_ID filled in
- 4Function run with one recipient's test data
- 5Every slide checked for correctly replaced placeholders
- 6Longer tokens confirmed to replace before shorter overlapping ones
- 7Batch generation tested against a small recipient list
Frequently asked questions
Replacing the shorter one first can corrupt the longer one, so the script sorts tokens by length and replaces the longest first to avoid that collision.
Yes, it searches across the whole presentation including notes, not just the visible slide text.
Yes, chain the export-slides-to-pdf tutorial's UrlFetchApp pattern onto the presentation ID returned by this function.
Loop the function over rows pulled from a spreadsheet, calling it once per recipient with that row's data object.
It stays visible in the generated deck exactly as written, which makes it easy to spot before sending the deck out.
Yes, replaceAllText searches text in tables and other shapes across every slide, not just standalone text boxes.