How to Apply a PowerPoint Master/Theme from One Presentation to Another?

Hi team,

I’m working on a PowerPoint Add-in using Office.js (taskpane) and a NestJS backend. The client requirement is to allow users to apply a branded PowerPoint template (master slide + theme) to their existing presentation without replacing the slide content.

Since Office.js doesn’t support applying themes directly (only inserting slides), we are trying a backend workaround using Aspose.Slides Cloud:

:white_check_mark: Current Approach:

  1. User uploads their original .pptx file from Office.js Add-in taskpane.
  2. They select a saved template (stored in DB & on S3).
  3. Backend (NestJS) does the following:
  • Downloads both the original and template .pptx.
  • Uploads them to Aspose Cloud storage (slides/ folder).
  • Calls copyMasterSlide() to copy the template’s master slide/theme into the user’s presentation.
  • Downloads the updated .pptx and returns back to taskpane.

:white_check_mark: Code Snippet (NestJS):

    const folderName = 'slides';
    const originalName = `${folderName}/original-${sessionId}.pptx`;
    const templateName = `${folderName}/template-${sessionId}.pptx`;

    try {
      this.logger.log('Downloading template from S3...');
      const templateRes = await fetch(template.pptxUrl);
      if (!templateRes.ok) throw new BadRequestException('Failed to download PPTX from S3');

      const templateBuffer = Buffer.from(await templateRes.arrayBuffer());

      /** ✅ Step 1: Upload both files to Aspose Storage */
      this.logger.log('Uploading presentation & template to Aspose Cloud...');
      await this.slidesApi.uploadFile(originalName, this.bufferToStream(originalFile.buffer));
      await this.slidesApi.uploadFile(templateName, this.bufferToStream(templateBuffer));

      /** ✅ Step 2: Copy the Master Slide (Theme) from Template → Presentation */
      this.logger.log('Copying master slide from template...');
    

      this.logger.log('templateName, originalName, folderName', templateName, originalName, folderName);

      await this.slidesApi.copyMasterSlide(
        templateName,         // Source file (template)
        originalName,         // Destination (user's presentation)
        1,                    // Clone master slide index = 1
        undefined,            // cloneFromPassword
        undefined,            // cloneFromStorage
        true,                 // applyToAll: apply to all slides
        undefined,            // password (destination)
        folderName,            // folder
       undefined           // storage
      );

:warning: Issues / Questions:

  1. Is copyMasterSlide() the correct method to apply full theme + master layout to all existing slides?
  2. Will this preserve existing slide content while updating design?
  3. Do we first need to apply master slide, then copy layout slides separately per slide?
  4. Is there a dedicated method for “Apply template/theme only” instead of inserting new slides?
  5. Are there any sample Node.js examples for this use case?
  6. Do template and original need to be in the same folder/storage?
  7. Any best practices to perform this faster or more reliably?

Thank you! This feature is crucial to match client requirements, so any guidance or sample code would be very helpful.

@umairm1alik

To apply a PowerPoint master/theme from one presentation to another using Aspose.Slides Cloud in Node.js, your approach with the copyMasterSlide() method is correct. Here are the answers to your questions:

  1. Is copyMasterSlide() the correct method?
    Yes, copyMasterSlide() is the appropriate method to copy a master slide from a template to an existing presentation.

  2. Will this preserve existing slide content?
    Yes, using copyMasterSlide() will preserve the existing slide content while updating the design with the new master slide.

  3. Do we need to apply the master slide and then copy layout slides separately?
    No, you do not need to copy layout slides separately. The copyMasterSlide() method applies the master slide to all existing slides in the presentation.

  4. Is there a dedicated method for “Apply template/theme only”?
    There is no specific method to apply a theme without copying slides. The copyMasterSlide() method is the best option for your use case.

  5. Are there any sample Node.js examples for this use case?
    While specific Node.js examples may not be readily available, you can refer to the Aspose.Slides Cloud documentation for general examples and adapt the provided code snippet as needed.

  6. Do template and original need to be in the same folder/storage?
    No, the template and original presentations do not need to be in the same folder. You can specify their paths when calling the copyMasterSlide() method.

  7. Any best practices for performance?

    • Ensure that both presentations are optimized and not excessively large.
    • Use asynchronous calls to handle uploads and downloads efficiently.
    • Consider caching frequently used templates to reduce retrieval time.

Here’s a refined code snippet for your reference:

await this.slidesApi.copyMasterSlide(
  `slides/${originalFileName}`,     // User’s presentation
  `slides/${templateFileName}`,     // Template with master slide
  1,                                // Copy first master slide
  undefined, undefined,
  true,                             // Apply to all slides
  undefined,
  'slides'
);

This should help you implement the feature as per your client’s requirements. If you have further questions, feel free to ask!

I am trying this way but getting error

[Nest] 41496  - 10/21/2025, 7:46:29 PM   ERROR [SlidesService] Error applying template theme:
[Nest] 41496  - 10/21/2025, 7:46:29 PM   ERROR [SlidesService] Object(2) {
  message: "slideIndex: The value 'template-0b4bbe33-7897-4c3c-8dfa-3db2399d6178.pptx' is not valid.\r\ninvalidFormat: Format masterSlides is not supported for this operation.",
  code: 400
}

@umairm1alik,
Thank you for contacting free support. I need some time to answer your questions. I will get back to you as soon as possible.

@umairm1alik,
Thank you for your patience. I agree with the answers above.

I was unable to reproduce the error you encountered.

Please note that for the copyMasterSlide method, the first parameter must be the file name of the destination presentation, and the second parameter must be the file name of the presentation that contains the master slide to copy. I hope this will help you resolve the issue.

I am also doing the same way but getting index related issue can you plz elaborate all steps one by one like creating application and storage in cloud may be one missing something out there.?

    const folderName = 'slides';
    const originalName = `${folderName}/original-${sessionId}.pptx`;
    const templateName = `${folderName}/template-${sessionId}.pptx`;

    try {
      this.logger.log('Downloading template from S3...');
      const templateRes = await fetch(template.pptxUrl);
      if (!templateRes.ok) throw new BadRequestException('Failed to download PPTX from S3');

      const templateBuffer = Buffer.from(await templateRes.arrayBuffer());

      /** ✅ Step 1: Upload both files to Aspose Storage */
      this.logger.log('Uploading presentation & template to Aspose Cloud...');
      await this.slidesApi.uploadFile(originalName, this.bufferToStream(originalFile.buffer));
      await this.slidesApi.uploadFile(templateName, this.bufferToStream(templateBuffer));

      /** ✅ Step 2: Copy the Master Slide (Theme) from Template → Presentation */
      this.logger.log('Copying master slide from template...');
       // or specify folder if needed

      this.logger.log('templateName, originalName, folderName', templateName, originalName, folderName);

      await this.slidesApi.copyMasterSlide(
        originalName,         // Destination (user's presentation)
        templateName,         // Source file (template)
        1,                    // Clone master slide index = 1
        undefined,            // cloneFromPassword
        undefined,            // cloneFromStorage
        true,                 // applyToAll: apply to all slides
        undefined,            // password (destination)
        folderName,            // folder
       undefined           // storage
      );
[Nest] 19180  - 10/22/2025, 3:40:55 PM     LOG [SlidesService] Downloading template from S3...
[Nest] 19180  - 10/22/2025, 3:41:03 PM     LOG [SlidesService] Uploading presentation & template to Aspose Cloud...
[Nest] 19180  - 10/22/2025, 3:41:15 PM     LOG [SlidesService] Copying master slide from template...
[Nest] 19180  - 10/22/2025, 3:41:15 PM     LOG [SlidesService] templateName, originalName, folderName
[Nest] 19180  - 10/22/2025, 3:41:15 PM     LOG [SlidesService] slides/template-da84bd46-745d-4663-9647-ddecaea75775.pptx
[Nest] 19180  - 10/22/2025, 3:41:15 PM     LOG [SlidesService] slides/original-da84bd46-745d-4663-9647-ddecaea75775.pptx
[Nest] 19180  - 10/22/2025, 3:41:15 PM     LOG [SlidesService] slides
[Nest] 19180  - 10/22/2025, 3:41:20 PM   ERROR [SlidesService] Error applying template theme:
[Nest] 19180  - 10/22/2025, 3:41:20 PM   ERROR [SlidesService] Object(2) {
  message: "slideIndex: The value 'original-da84bd46-745d-4663-9647-ddecaea75775.pptx' is not valid.\r\ninvalidFormat: Format masterSlides is not supported for this operation.",
  code: 400
}

@umairm1alik,
Here is the simplest project to use Aspose.Slides Cloud SDK for Node.js:
TestApp.zip (1.5 KB)

Regarding the error you described, please share the following additional files and information:

  • The sample presentation files.
  • The version of Aspose.Slides Cloud SDK for Node.js you are using.

original presentation on which we want to implement theme changes

template file from which we want to apply template

these are just the sample files we are using aspose cloud sdk version 25.6.0

“asposeslidescloud”: “25.6.0”,

@umairm1alik,
Thank you for the details. I need some time to investigate the issue. I will get back to you as soon as possible.

@umairm1alik,
I have reproduced the error you encountered. The error is caused by an incorrect path to the original file. Do not specify the full path in the originalName variable, because folderName already contains the path to the folder with the file and is passed as the eighth parameter. A full path should be specified only for the template file. Please try the following:

const folderName = 'slides';
const originalName = `original-${sessionId}.pptx`;
const templateName = `${folderName}/template-${sessionId}.pptx`;

The documentation:
Copy a Master Slide|Documentation

Thanks for you support but with this implementations i am getting error related to file path,

 const sessionId = uuidv4();
    const folderName = 'slides';
    const originalName = `original-${sessionId}.pptx`;
    const templateName = `${folderName}/template-${sessionId}.pptx`;

    try {
      this.logger.log('Downloading template from S3...');
      const templateRes = await fetch(template.pptxUrl);
      if (!templateRes.ok) throw new BadRequestException('Failed to download PPTX from S3');

      const templateBuffer = Buffer.from(await templateRes.arrayBuffer());

      /** ✅ Step 1: Upload both files to Aspose Storage */
      this.logger.log('Uploading presentation & template to Aspose Cloud...');
      await this.slidesApi.uploadFile(originalName, this.bufferToStream(originalFile.buffer));
      await this.slidesApi.uploadFile(templateName, this.bufferToStream(templateBuffer));

      /** ✅ Step 2: Copy the Master Slide (Theme) from Template → Presentation */
      this.logger.log('Copying master slide from template...');
       // or specify folder if needed

      this.logger.log('templateName, originalName, folderName', templateName, originalName, folderName);


      try {
        
        await this.slidesApi.copyMasterSlide(
          originalName,         // Destination (user's presentation)
          templateName,         // Source file (template)
          1,                    // Clone master slide index = 1
          undefined,            // cloneFromPassword
          undefined,            // cloneFromStorage
          true,                 // applyToAll: apply to all slides
          undefined,            // password (destination)
          folderName,            // folder
         undefined           // storage
        );

Error

[Nest] 95786  - 10/23/2025, 3:24:34 PM     LOG [SlidesService] Downloading template from S3...
[Nest] 95786  - 10/23/2025, 3:24:39 PM     LOG [SlidesService] Uploading presentation & template to Aspose Cloud...
[Nest] 95786  - 10/23/2025, 3:24:50 PM     LOG [SlidesService] Copying master slide from template...
[Nest] 95786  - 10/23/2025, 3:24:50 PM     LOG [SlidesService] templateName, originalName, folderName
[Nest] 95786  - 10/23/2025, 3:24:50 PM     LOG [SlidesService] slides/template-d6c9c065-c4f4-4eeb-9f66-93c8c2478dba.pptx
[Nest] 95786  - 10/23/2025, 3:24:50 PM     LOG [SlidesService] original-d6c9c065-c4f4-4eeb-9f66-93c8c2478dba.pptx
[Nest] 95786  - 10/23/2025, 3:24:50 PM     LOG [SlidesService] slides
[Nest] 95786  - 10/23/2025, 3:24:50 PM   ERROR [SlidesService] Error during copyMasterSlide:
[Nest] 95786  - 10/23/2025, 3:24:50 PM   ERROR [SlidesService] Object(2) {
  message: "AmazonS3 Storage exception: The specified key does not exist. Bucket 'aspose.cloud-filestorage-prod', FilePath '1045607/a0e7efbf-1c27-4516-9898-a34b6c584591/slides/original-d6c9c065-c4f4-4eeb-9f66-93c8c2478dba.pptx'",
  code: 404
}

I have also tried this way but getting same error with this approach as well giving folder name while upload but just giving original name on copy masterslide

await this.slidesApi.uploadFile(
  `slides/${originalName}`,            // ✅ full path including folder
  this.bufferToStream(originalFile.buffer),
  undefined                            // storageName (optional)
);

@umairm1alik,
The error occurs because a file path is specified incorrectly. The following code example works fine:

const sessionId = '12345'; // for example

const localOriginalFilePath = 'original-file.pptx';
const localTemplateFilePath = 'template-file.pptx';

const cloudFolderPath = 'slides';

const cloudOriginalFileName = `original-${sessionId}.pptx`;                    // used for copyMasterSlide
const cloudOriginalFilePath = `${cloudFolderPath}/${cloudOriginalFileName}`;   // used for uploading

const cloudTemplateFilePath = `${cloudFolderPath}/template-${sessionId}.pptx`; // used for uploading and copyMasterSlide

const originalFileStream = fs.createReadStream(localOriginalFilePath);
await slidesApi.uploadFile(cloudOriginalFilePath, originalFileStream);

const templateFileStream = fs.createReadStream(localTemplateFilePath);
await slidesApi.uploadFile(cloudTemplateFilePath, templateFileStream);

await slidesApi.copyMasterSlide(
    cloudOriginalFileName,
    cloudTemplateFilePath,
    1,
    undefined,
    undefined,
    true,
    undefined,    
    cloudFolderPath,   
    undefined     
);

Still getting same error is there any bucket related thing am I missing

[Nest] 60847  - 10/23/2025, 4:41:27 PM     LOG [SlidesService] Uploading original & template to Aspose Cloud...
[Nest] 60847  - 10/23/2025, 4:41:34 PM     LOG [SlidesService] Copying master slide from template to presentation...
[Nest] 60847  - 10/23/2025, 4:41:35 PM   ERROR [SlidesService] Error applying template theme:
[Nest] 60847  - 10/23/2025, 4:41:35 PM   ERROR [SlidesService] Object(2) {
  message: "AmazonS3 Storage exception: The specified key does not exist. Bucket 'aspose.cloud-filestorage-prod', FilePath '1045771/2963f655-b6a6-49e3-8d8f-e22a51e4458a/slides/original-fee12082-db9c-44c0-95ff-e0a60924e2cb.pptx'",
  code: 404
}

@umairm1alik,
Did you run the code example above?

yes i did


    const sessionId = uuidv4();
    const folderName = 'slides'; // Aspose Cloud folder name
    const cloudOriginalFileName = `original-${sessionId}.pptx`; // Only name, used in copyMasterSlide
    const cloudOriginalFilePath = `${folderName}/${cloudOriginalFileName}`; // Full path for uploading

    const cloudTemplateFileName = `template-${sessionId}.pptx`;
    const cloudTemplateFilePath = `${folderName}/${cloudTemplateFileName}`; // Full path for uploading & copyMasterSlide

    try {
      // 1️⃣ Download Template from S3
      const templateRes = await fetch(template.pptxUrl);
      if (!templateRes.ok)
        throw new BadRequestException('Failed to download template PPTX');
      const templateBuffer = Buffer.from(await templateRes.arrayBuffer());

      // 2️⃣ Upload both files to Aspose Cloud storage
      this.logger.log('Uploading original & template to Aspose Cloud...');

      await this.slidesApi.uploadFile(
        cloudOriginalFilePath,
        this.bufferToStream(originalFile.buffer),
      );
      await this.slidesApi.uploadFile(
        cloudTemplateFilePath,
        this.bufferToStream(templateBuffer),
      );

      // 3️⃣ Copy Master Slide from template to original
      this.logger.log('Copying master slide from template to presentation...');

      await this.slidesApi.copyMasterSlide(
        cloudOriginalFileName, // ✅ must be only the file name (not full path!)
        cloudTemplateFilePath, // ✅ full path for template
        1, // master slide index
        undefined, // cloneFromPassword
        undefined, // cloneFromStorage
        true, // apply to all slides
        undefined, // password (target)
        folderName, // ✅ folder name
        undefined, // storage name
      );

      // 4️⃣ Download updated PPTX
      const downloadResult = await this.slidesApi.downloadPresentation(
        cloudOriginalFileName,
        slidescloud.ExportFormat.Pptx,
        undefined,
        folderName, // ✅ ensure correct folder
      );

i am getting these logs on dashboard

http://api.aspose.cloud/v3.0/slides/storage/file/slides/original-fee12082-db9c-44c0-95ff-e0a60924e2cb.pptx status =200

Success: UploadFile.

http://api.aspose.cloud/v3.0/slides/storage/file/slides/template-fee12082-db9c-44c0-95ff-e0a60924e2cb.pptx 200

Success: UploadFile.

Failing here
http://api.aspose.cloud/v3.0/slides/original-fee12082-db9c-44c0-95ff-e0a60924e2cb.pptx/masterSlides 400

Error: CopyMasterSlide. Exception: AmazonS3 Storage exception: The specified key does not exist. Buc

@umairm1alik,
Could you please test the accessibility of the original file in storage immediately after uploading it by calling a simple method?

For example:

const originalInfo = await slidesApi.getPresentation(
    cloudOriginalFileName,
    undefined,
    cloudFolderPath);
console.log(originalInfo.body.slides.href);

I am calling it over here just after original upload but as soon as it reaches on get presetation immediately throughing error

     const templateBuffer = Buffer.from(await templateRes.arrayBuffer());

      // 2️⃣ Upload both files to Aspose Cloud storage
      this.logger.log('Uploading original & template to Aspose Cloud...');

      await this.slidesApi.uploadFile(
        cloudOriginalFilePath,
        this.bufferToStream(originalFile.buffer),
      );
      const originalInfo = await this.slidesApi.getPresentation(
        cloudOriginalFileName,
        undefined,
        folderName,
      );
      this.logger.log(
        'originalInfo?.body?.slides?.href: ' + originalInfo?.body?.slides?.href,
      );

[Nest] 42861 - 10/23/2025, 6:54:26 PM LOG [SlidesService] Downloading template PPTX from S3…
[Nest] 42861 - 10/23/2025, 6:54:30 PM LOG [SlidesService] Uploading original & template to Aspose Cloud…
[Nest] 42861 - 10/23/2025, 6:54:34 PM ERROR [SlidesService] Error applying template theme:
[Nest] 42861 - 10/23/2025, 6:54:34 PM ERROR [SlidesService] Object(2) {
message: “AmazonS3 Storage exception: The specified key does not exist. Bucket ‘aspose.cloud-filestorage-prod’, FilePath ‘1045771/2963f655-b6a6-49e3-8d8f-e22a51e4458a/slides/original-c05d492d-eb43-46fc-907b-0cd9e90ce47d.pptx’”,
code: 404
}

I have added the logger after upload for uploadresponse getting status 200 message ok and url of uploaded file

try {
        const originalUploaded = await this.slidesApi.uploadFile(
          cloudOriginalFilePath,
          this.bufferToStream(originalFile.buffer),
        );

        this.logger.log({
          message: 'Original file uploaded',
          response: originalUploaded.response,
        });
      } catch (error) {
        this.logger.error('Error uploading original file:', error);
        throw error;
      }

      const originalInfo = await this.slidesApi.getPresentation(
        cloudOriginalFileName,
        undefined,
        folderName,
      );
      this.logger.log(
        'originalInfo?.body?.slides?.href: ' + originalInfo?.body?.slides?.href,
      );
 message: 'Original file uploaded',
  response: {
    status: 200,
    statusText: 'OK',
    headers: Object [AxiosHeaders] {
      server: 'nginx/1.19.9',
      date: 'Thu, 23 Oct 2025 14:03:04 GMT',
      'content-type': 'application/json; charset=utf-8',
      'content-length': '27',
      'x-lb': 'TLR'

 method: 'put',
      url: 'https://api.aspose.cloud/v3.0/slides/storage/file/slides/original-4f4c5cc2-06b8-4eb5-a5f1-2acc68346daa.pptx',
      params: {},
      data: FormData {
        _overheadLength: 152,

@umairm1alik,
Please note that for Amazon S3 storage, the folder path (the eighth parameter of copyMasterSlide and the third parameter of getPresentation) must begin with the S3 bucket name.

I am not using any third party storage i have configure inetrnal storage, from where i can find the bucket name, did’nt find any thing from dashboard.