npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

google-slides-copy

v0.1.0

Published

Copy slides between Google Slides presentations via PPTX round-trip

Downloads

249

Readme

google-slides-copy

Copy slides between Google Slides presentations programmatically. Built for workflows that assemble a destination deck from multiple source presentations.

Google Slides has no native API for copying a slide from one presentation to another. This library works around that gap via a PPTX round-trip: it exports presentations as Office Open XML (.pptx), performs the copy as in-memory ZIP/XML manipulation, and re-imports the result — all without any Microsoft tooling.

How it works

A PPTX file is a ZIP archive. A slide is a self-contained XML file inside that archive. Copying a slide between decks is therefore a structural operation — moving XML nodes and their associated media — rather than a semantic reconstruction. This produces high-fidelity results for standard slide content (text, shapes, images, tables, animations).

Limitations: Google-specific content — Sheets-linked charts, embedded Google Drawings, linked Forms — does not survive the PPTX round-trip and will be converted to static equivalents or dropped.

Installation

npm install google-slides-copy

Prerequisites

Your Google Cloud project must have the following APIs enabled:

  • Google Slides API
  • Google Drive API

Your OAuth2 credentials (or service account) need the following scopes:

https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/presentations

API

The library exposes three primitives and one convenience wrapper.


exportPptx(auth, presentationId)Promise<Buffer>

Downloads a Google Slides presentation as an in-memory PPTX buffer. This makes one Google Drive API call.

| Parameter | Type | Description | |------------------|----------|--------------------------------------| | auth | object | Authenticated Google OAuth2 client | | presentationId | string | Google Drive file ID of the presentation |


copySlide(sourcePptx, sourceSlideIndex, destPptx, insertIndex)Promise<Buffer>

Copies one slide from a source PPTX buffer into a destination PPTX buffer. Makes no Google API calls — entirely in-memory. Returns the updated destination buffer, which should be passed into subsequent calls.

| Parameter | Type | Description | |--------------------|------------------|-----------------------------------------------------------------------------| | sourcePptx | Buffer | Source presentation buffer (from exportPptx) | | sourceSlideIndex | number | 0-based index of the slide to copy | | destPptx | Buffer | Destination presentation buffer | | insertIndex | number \| null | 0-based position to insert at. Pass null to append at the end. Values beyond the current slide count also append. |


importPptx(auth, presentationId, pptxBuffer)Promise<void>

Uploads a PPTX buffer back to Google Drive, replacing the given presentation in-place and converting it to Google Slides format. This makes one Google Drive API call.

| Parameter | Type | Description | |------------------|----------|--------------------------------------------------| | auth | object | Authenticated Google OAuth2 client | | presentationId | string | Google Drive file ID to replace | | pptxBuffer | Buffer | Final assembled PPTX buffer (from copySlide) |


copySlideOneShot(options)Promise<void>

Convenience wrapper that exports source and destination, copies one slide, and re-imports — all in a single call. Use this for simple one-off operations. For bulk workflows involving multiple sources or slides, use the three primitives above to avoid redundant API calls.

| Option | Type | Description | |------------------------------|------------------|------------------------------------| | auth | object | Authenticated Google OAuth2 client | | sourcePresentationId | string | Source presentation file ID | | sourceSlideIndex | number | 0-based slide index in source | | destinationPresentationId | string | Destination presentation file ID | | insertIndex | number \| null | Insert position (null = end) |


Usage

Basic: copy one slide

const slides = require('google-slides-copy');
const { google } = require('googleapis');

// Set up your OAuth2 client (shown here with a service account)
const auth = new google.auth.GoogleAuth({
  keyFile: 'service-account.json',
  scopes: [
    'https://www.googleapis.com/auth/drive',
    'https://www.googleapis.com/auth/presentations',
  ],
});

await slides.copySlideOneShot({
  auth,
  sourcePresentationId:      '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms',
  sourceSlideIndex:          0,       // first slide (0-based)
  destinationPresentationId: '1EiJ6n3a7T9X2kLvQpRsDcFgHmYo4wN8bZuAeXcPqVtI',
  insertIndex:               null,    // append at the end
});

Bulk: assemble a deck from multiple sources

This is the primary use case. Export the destination once, copy all slides in memory, then import once at the end. Only two Google API calls touch the destination regardless of how many slides you copy.

const slides = require('google-slides-copy');

const DESTINATION_ID = '1EiJ6n3a7T9X2kLvQpRsDcFgHmYo4wN8bZuAeXcPqVtI';

const sources = [
  { presentationId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms', slideIndexes: [0, 1] },
  { presentationId: '1CyJm4b8U0YB6GOeLwRteDgIjNZp5xO9cAvBfYdQrWuJ', slideIndexes: [0, 1] },
];

// Export destination once
let destPptx = await slides.exportPptx(auth, DESTINATION_ID);

// Start at position 1 to preserve an existing title slide at index 0.
// Use position 0 if the destination should be entirely replaced.
let position = 1;

for (const source of sources) {
  const sourcePptx = await slides.exportPptx(auth, source.presentationId);

  for (const slideIndex of source.slideIndexes) {
    destPptx = await slides.copySlide(sourcePptx, slideIndex, destPptx, position);
    position++;
  }
}

// Import the assembled result once
await slides.importPptx(auth, DESTINATION_ID, destPptx);

Append all slides from one presentation to another

const slides = require('google-slides-copy');

const SOURCE_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms';
const DEST_ID   = '1EiJ6n3a7T9X2kLvQpRsDcFgHmYo4wN8bZuAeXcPqVtI';

const [sourcePptx, destPptx_initial] = await Promise.all([
  slides.exportPptx(auth, SOURCE_ID),
  slides.exportPptx(auth, DEST_ID),
]);

// Copy slides 0, 1, and 2 from the source, appending each to the destination
let destPptx = destPptx_initial;
for (let i = 0; i < 3; i++) {
  destPptx = await slides.copySlide(sourcePptx, i, destPptx, null); // null = append
}

await slides.importPptx(auth, DEST_ID, destPptx);

Authentication

The library accepts any authenticated Google OAuth2 client. The two most common patterns:

Service account (server-to-server, recommended for automation):

const { google } = require('googleapis');

const auth = new google.auth.GoogleAuth({
  keyFile: 'path/to/service-account.json',
  scopes: [
    'https://www.googleapis.com/auth/drive',
    'https://www.googleapis.com/auth/presentations',
  ],
});

The service account must be granted editor access to both the source and destination presentations in Google Drive.

OAuth2 (user-delegated access):

const { google } = require('googleapis');

const oauth2Client = new google.auth.OAuth2(
  'YOUR_CLIENT_ID',
  'YOUR_CLIENT_SECRET',
  'YOUR_REDIRECT_URI'
);
oauth2Client.setCredentials({ refresh_token: 'USER_REFRESH_TOKEN' });

Layout matching

When a slide is copied, the library attempts to assign it the most appropriate layout in the destination deck:

  1. Match by name — reads the source slide's layout name (e.g. "Title Slide", "Blank") and searches the destination for a layout with the same name.
  2. Blank fallback — if no name match is found, the slide is assigned the destination's "Blank" layout.
  3. Last resort — falls back to slideLayout1.xml if no Blank layout exists.

For slides built with primitive shapes (no layout-inherited placeholders), the Blank fallback produces clean results regardless of template differences between source and destination.


License

MIT © RingCentral, Inc.