By Raj

Estimated reading time: 12 minutes

Generate Google Slides from Sheets with Apps Script

Building the same deck repeatedly from spreadsheet data, e.g. one slide per client or per month, wastes time. Generating Google Slides from Sheets with Apps Script automates it: read rows, copy a template presentation or create slides programmatically, replace placeholders or fill shapes with data, and save to Drive or share. You get consistent decks without manual copy-paste. The limits are execution time and Slides/Drive API quotas, so you batch work (e.g. 5–10 decks per run) and use triggers to process a queue. This guide covers architecture, implementation with SlidesApp, and scaling.

Why automate Slides generation

Consider a typical monthly workflow: you have a spreadsheet with 50 clients, and each month you need to create a personalized presentation for each one. Manually, this means copying a template, editing 5-10 fields per deck, and saving, roughly 15-20 minutes per deck. That's 12-16 hours of repetitive work every month.

Automation changes this equation. A well-designed Sheets-to-Slides pipeline can:

• Generate 10 decks in under 4 minutes (vs. 2-3 hours manually)
• Eliminate human error from copy-paste mistakes
• Ensure brand consistency with a single template source
• Scale to hundreds of decks without additional time investment
• Update all decks instantly when template changes

The real value isn't just time savings, it's consistency. Every client gets the same professional presentation structure, every report follows the same format, and your team focuses on the content quality rather than formatting.

Common mistakes

  • Creating one full deck per row in a long loop: This hits the 6-minute execution time limit quickly. Process in batches instead.
  • No template; building every slide from scratch in code: This is slow and hard to maintain. Always use a template presentation.
  • Not batching; processing hundreds of rows in one run: Break large datasets into smaller batches triggered on a schedule.
  • Assuming slide layout placeholders have fixed IDs: Layout IDs can vary by template. Use shape names or indices instead.
  • No status column or cursor: Without tracking processed rows, the same data gets regenerated each run.
  • Not saving and closing presentations: Always call pres.saveAndClose() or files remain locked.
  • Hardcoding template IDs in scripts: Use Script Properties for configuration that changes between environments.

Architecture

Flow: Sheet (data source) → script reads rows → for each row (or batch), copy template deck or create presentation via SlidesApp, replace text in placeholders or shapes, save. Use a template Slide deck with named placeholders or known shape indices so the script only fills data. For "one slide per row" reports, either append slides to one deck or create one deck per row; the latter scales better with batch size limits.

High-level architecture:

1. Data Layer: Google Sheets containing your source data (client info, metrics, etc.)
2. Template Layer: A master Google Slides presentation with placeholders
3. Automation Layer: Apps Script that reads data, copies template, and populates content
4. Delivery Layer: Save to Drive folder, share via email, or notify via webhook

The key design decision is whether to create one deck per row (individual files) or append all slides into a single deck. Individual files scale better for client-specific reports; single decks work better for consolidated monthly reports.

Implementation

Step 1: Prepare your data sheet

Create a Google Sheet with your data. Include a status column to track which rows have been processed:

• Column A: Client Name
• Column B: Contact Email
• Column C: Monthly Revenue
• Column D: Growth %
• Column E: Key Highlights
• Column F: Status (Pending / Generated / Error)

Step 2: Create your template presentation

Design a Google Slides template with placeholders. Use text boxes for dynamic content. Name your shapes descriptively (select a shape, look at the title bar in Slides) so you can reference them in code. Alternatively, use the slide layout's built-in placeholders.

Step 3: Basic generation script

Copy the template with DriveApp.getFileById(templateId).makeCopy(), open with SlidesApp.openById(copyId), get slides and shapes, then shape.getText().setText(value) or replace placeholder text. Use a consistent template layout so you can index slides and shapes. Process up to N rows per run (e.g. 5–10) so the run stays under 5 minutes.

function generateSlidesFromSheet() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("Data");
  const lastRow = sheet.getLastRow();
  if (lastRow < 2) return;
  const numRows = lastRow - 1;
  const data = sheet.getRange(2, 1, 2 + numRows - 1, 5).getValues();
  const templateId = PropertiesService.getScriptProperties().getProperty("SLIDES_TEMPLATE_ID");
  const BATCH = 5;
  for (var i = 0; i < Math.min(BATCH, data.length); i++) {
    var copy = DriveApp.getFileById(templateId).makeCopy();
    var pres = SlidesApp.openById(copy.getId());
    var slide = pres.getSlides()[0];
    var shapes = slide.getShapes();
    if (shapes.length > 0) shapes[0].getText().setText(data[i][0]);
    pres.saveAndClose();
  }
}

Step 4: Enhanced generation with error handling

Here's a more robust version that handles errors, updates status, and logs progress:

function generateSlidesBatch() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("Data");
  const templateId = PropertiesService.getScriptProperties().getProperty("SLIDES_TEMPLATE_ID");
  const outputFolderId = PropertiesService.getScriptProperties().getProperty("OUTPUT_FOLDER_ID");
  const BATCH_SIZE = 5;
  
  // Get pending rows
  const data = sheet.getDataRange().getValues();
  const pendingRows = [];
  for (let i = 1; i < data.length; i++) {
    if (data[i][5] === "Pending") { // Column F is Status
      pendingRows.push({ row: i + 1, data: data[i] });
    }
    if (pendingRows.length >= BATCH_SIZE) break;
  }
  
  if (pendingRows.length === 0) {
    Logger.log("No pending rows to process");
    return;
  }
  
  const outputFolder = DriveApp.getFolderById(outputFolderId);
  
  pendingRows.forEach(function(item) {
    try {
      const row = item.row;
      const rowData = item.data;
      
      // Copy template
      const templateFile = DriveApp.getFileById(templateId);
      const newFile = templateFile.makeCopy(rowData[0] + " Report", outputFolder);
      const pres = SlidesApp.openById(newFile.getId());
      
      // Replace placeholders on each slide
      const slides = pres.getSlides();
      slides.forEach(function(slide) {
        const shapes = slide.getShapes();
        shapes.forEach(function(shape) {
          const text = shape.getText().asString();
          // Replace {{placeholder}} with actual data
          if (text.includes("{{ClientName}}")) {
            shape.getText().setText(text.replace("{{ClientName}}", rowData[0]));
          }
          if (text.includes("{{Revenue}}")) {
            shape.getText().setText(text.replace("{{Revenue}}", "$" + rowData[2]));
          }
          if (text.includes("{{Growth}}")) {
            shape.getText().setText(text.replace("{{Growth}}", rowData[3] + "%"));
          }
        });
      });
      
      pres.saveAndClose();
      
      // Update status
      sheet.getRange(row, 6).setValue("Generated");
      sheet.getRange(row, 7).setValue(newFile.getUrl());
      
      Logger.log("Generated: " + rowData[0]);
    } catch (error) {
      sheet.getRange(item.row, 6).setValue("Error: " + error.message);
      Logger.log("Error for row " + item.row + ": " + error.message);
    }
  });
}

Triggers

Time-driven trigger for batch generation; or on Form Submit if each submission should create one deck. Store template ID in Script Properties. For custom UIs that trigger generation, Apps Script web apps can expose a button or form.

Setting up a daily trigger:

1. In Apps Script editor, click the clock icon (Triggers)
2. Click "+ Add Trigger"
3. Choose function: generateSlidesBatch
4. Select: Time-driven, Daily, Around 6am
5. Save and authorize

Example: A team generated monthly client reports as Slides (one deck per client, same layout). We automated: read "Pending" rows from Sheet, copy template, fill 3 slides per deck from row data, save to a folder; batch of 5 decks per run, daily trigger. Manual deck creation dropped from ~2 hours/month to zero.

Batching deck generation (5–10 per run) typically keeps runtime under 4 minutes and avoids Slides/Drive quota spikes.

Template design best practices

Your template design significantly impacts how easily the automation works. Follow these best practices:

1. Use consistent placeholder syntax
Use a pattern like ClientName, Revenue in your template. This makes replacement straightforward and easy to debug.

2. Name your shapes
In Google Slides, select a shape and look at the title in the sidebar. Rename shapes to meaningful names like "ClientNamePlaceholder" for easy identification in code.

3. Use master slides
Design your content on master slides so new slides added to the template inherit consistent styling.

4. Keep layouts simple
Avoid complex layouts with many overlapping elements. Simple, well-organized layouts are easier to populate programmatically.

5. Test with sample data
Before automating, manually create a few test decks to verify your template works as expected.

Advanced features

Once the basic generation works, consider these advanced features:

1. Multi-slide templates
Create templates with multiple slides (title, metrics, recommendations). The script can populate all slides from a single row of data.

2. Dynamic charts
Generate charts in Sheets, take snapshots, and insert them into slides using slide.insertImage().

3. Conditional content
Use Apps Script logic to include or exclude slides based on data values. For example, only add a "Pricing" slide if the deal size exceeds a threshold.

4. Email delivery
After generating, automatically email the presentation to the client using GmailApp.sendEmail().

5. Version control
Keep template versions in a separate folder. Update the template ID in Script Properties when making changes.

Scalability and quotas

Execution time and Slides/Drive create limits apply. Batch (5–10 decks per run), use a status column, and schedule triggers. For very high volume, use a cursor and continuation trigger. API integrations can push links to generated decks to another system.

Key quotas to understand:

Execution time: 6 minutes per run (consumer accounts)
Slides API: 50 requests per 100 seconds
Drive create: 500 files per day (varies by plan)
URL Fetch: 20,000 requests per day

For very high volumes, implement a queue system: store pending items in a sheet, process a batch per trigger run, and track progress with a cursor.

Enterprise scaling

For enterprise deployments, consider:

One trigger per job: Don't overlap trigger executions
Logging: Log run count, errors, and generated file URLs to a tracking sheet
Template ownership: Document who owns each template and the change process
Quota isolation: Use separate workbooks or scripts per team if needed
Access controls: Manage who can run generation and access output folders

FAQ

How much does it cost to generate Slides from Sheets?

Sheets and Apps Script are included in Google Workspace. You only pay for script development or template design time. There's no per-presentation fee. The Slides created count against your Google Drive storage quota, which starts at 15GB for free accounts.

What are the limits for generating Slides from Sheets?

Google Apps Script has a 6-minute execution time limit per run. Additionally, there are Slides API and Drive create limits. For generating many decks, process in batches (e.g., 5-10 per run) and use time-driven triggers or a queue system to handle larger volumes.

Is presentation data secure?

Yes. All data stays within your Google Workspace. Slides are created in your Google Drive with the same security controls. Use Script Properties for storing credentials and manage sharing settings through your Google Workspace admin console. No external services are involved unless you explicitly add them.

When to use a dedicated presentation tool instead?

Use dedicated tools like PowerPoint or Keynote for real-time collaboration on a single deck or when you need complex animations. Use Sheets to Slides automation when you have tabular data (like client reports, monthly summaries, or product catalogs) and want automated, consistent deck generation.

Can I generate multiple slides from one row of data?

Yes. You can create multiple slides per data row by copying a template slide multiple times within the same presentation. For example, a client report might have: an overview slide, a metrics slide, and a recommendations slide, all generated from a single row of client data.

How do I handle images in generated slides?

You can add images using SlidesApp's insertImage() method. Store image URLs or Drive file IDs in your Sheet, then reference them when generating slides. For dynamic images like charts, consider generating the chart in Sheets and then inserting a snapshot of it into the slide.

How do I update an existing presentation with new data?

Open the existing presentation by ID, find and replace the old values with new ones using the same placeholder replacement logic. You don't need to create a new file each time, update the existing one and save.

Can I generate different templates based on data values?

Yes. Add logic in your script to check a category field and select different template IDs. For example, if client type is 'Enterprise', use template A; if 'SMB', use template B.

About the author

Raj is an Apps Script and Google Workspace automation specialist. He builds Slides-from-Sheets and report automation for teams and SMEs.

More about Raj