Combining several presentations into one is a common step when organizing report materials: a main report and a topic appendix may be prepared by different team members, and finally need to sit in one file in a consistent order; sometimes only a few conclusion pages from the appendix are needed. Copying and pasting by hand takes time and may break the layout, charts, or text styles of the original slides. Handling it on a server also requires uploading the files to a third-party environment first.
This article uses Spire.Presentation for JavaScript to merge PowerPoint slides in the browser. The whole process runs locally on the user's device through WebAssembly, requires no Office installation and no back-end service, and keeps the presentation from leaving the browser.
This article covers two core features:
For installation and project configuration, see Integrating Spire.Presentation for JavaScript in a React Project. The examples below assume that Spire.Presentation is installed and the WebAssembly module has been initialized.
Merge multiple PowerPoint presentations
A complete report often consists of materials from several sources, for example a main report and a topic appendix prepared by different members that finally need to be combined into one file. To merge multiple presentations, load each source file in turn, then append all of its slides to one new presentation to obtain a merged result that contains every page.
| Code | Result |
|---|---|
new pptModule.Presentation() |
Creates a presentation instance with one blank slide by default |
mergedPpt.Slides.RemoveAt(0) |
Removes the blank slide created with the new presentation |
sourcePpt.Slides.Count |
Total number of slides in the current source presentation |
sourcePpt.Slides.get_Item(i) |
The (i+1)-th slide (index starts at 0) |
mergedPpt.Slides.Append({ slide }) |
Appends the source slide to the end of the merged result |
mergedPpt.SaveToFile({ file, fileFormat }) |
Writes the merged PPTX file in the specified format |
The steps are:
- Load
Presentation.pptxandSlidesToMerge.pptxinto the VFS withwindow.spire.FetchFileToVFS. - Create a new presentation with
new pptModule.Presentation()and remove the default blank page withSlides.RemoveAt(0). - For each source file, create a
Presentationinstance and load it withLoadFromFile, loop overSlides.Count, take each slide withSlides.get_Item(i), and append it to the merged result withSlides.Append({ slide }). - Release each source instance with
Dispose()when it has been processed; after all slides are appended, save the result asMergedPresentation.pptxwithSaveToFile. - Read the merged result through
window.dotnetRuntime.Module.FS.readFile, build aBlob, and trigger the browser download.
function App() {
const mergePresentations = async () => {
// Get the Spire.Presentation WASM module
const pptModule = window.wasmModule?.spirepresentation;
// Check if the module is ready
if (!pptModule) {
alert('Spire.Presentation is not ready yet');
return;
}
// Load the font and the two sample presentations into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const sourceFileNames = ['Presentation.pptx', 'SlidesToMerge.pptx'];
for (const fileName of sourceFileNames) {
await window.spire.FetchFileToVFS(fileName, '', `${process.env.PUBLIC_URL}data/`);
}
// Create a new presentation and remove its built-in blank slide
const mergedPpt = new pptModule.Presentation();
mergedPpt.Slides.RemoveAt(0);
// Append all slides of every source presentation to the merged result
for (const fileName of sourceFileNames) {
const sourcePpt = new pptModule.Presentation();
sourcePpt.LoadFromFile(fileName);
for (let i = 0; i < sourcePpt.Slides.Count; i++) {
mergedPpt.Slides.Append({ slide: sourcePpt.Slides.get_Item(i) });
}
sourcePpt.Dispose();
}
const outputFileName = 'MergedPresentation.pptx';
mergedPpt.SaveToFile({ file: outputFileName, fileFormat: pptModule.FileFormat.Pptx2013 });
mergedPpt.Dispose();
// Read the result file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Merge PowerPoint Presentations</h1>
<button onClick={mergePresentations}>
Merge Multiple Presentations
</button>
</div>
);
}
export default App;
Presentation after merging multiple presentations:

Merge selected PowerPoint slides
A merge does not always need every page of a source file. For example, the main report keeps its full structure, while only the two conclusion pages of the topic appendix are required. In this case, merge all slides of the main report first, then pick pages from the other presentation by index, so unrelated content is not carried over.
| Code | Result |
|---|---|
mergedPpt.Slides.Append({ slide }) |
Appends one slide to the end of the merged result |
mainPpt.Slides.Count |
Total number of slides in the main report |
mainPpt.Slides.get_Item(i) |
The (i+1)-th slide of the main report (index starts at 0) |
extraPpt.Slides.get_Item(index) |
The slide at the given index in the appendix |
selectedIndexes |
Indexes of the appendix slides that take part in the merge |
The steps are:
- Load the font,
Presentation.pptx, andSlidesToMerge.pptxwithwindow.spire.FetchFileToVFS. - Create two
Presentationinstances and load both source files withLoadFromFile; create the merged result withnew pptModule.Presentation()and remove the default blank page withSlides.RemoveAt(0). - Loop over
mainPpt.Slides.Countand append every slide of the main report withSlides.get_Item(i)andSlides.Append({ slide }). - Set
selectedIndexes(indexes start at 0) and append only those slides withextraPpt.Slides.get_Item(index); all other slides do not take part in the merge. - Save the result as
MergedSelectedSlides.pptxwithSaveToFile, then read the file throughwindow.dotnetRuntime.Module.FS.readFileand trigger the download.
function App() {
const mergeSelectedSlides = async () => {
// Get the Spire.Presentation WASM module
const pptModule = window.wasmModule?.spirepresentation;
// Check if the module is ready
if (!pptModule) {
alert('Spire.Presentation is not ready yet');
return;
}
// Load the font and the two sample presentations into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
for (const fileName of ['Presentation.pptx', 'SlidesToMerge.pptx']) {
await window.spire.FetchFileToVFS(fileName, '', `${process.env.PUBLIC_URL}data/`);
}
// Load the main report and the appendix
const mainPpt = new pptModule.Presentation();
mainPpt.LoadFromFile('Presentation.pptx');
const extraPpt = new pptModule.Presentation();
extraPpt.LoadFromFile('SlidesToMerge.pptx');
// Create a new presentation and remove its built-in blank slide
const mergedPpt = new pptModule.Presentation();
mergedPpt.Slides.RemoveAt(0);
// Append all slides of the main report
for (let i = 0; i < mainPpt.Slides.Count; i++) {
mergedPpt.Slides.Append({ slide: mainPpt.Slides.get_Item(i) });
}
// Append only the selected slides of the appendix (indexes start from 0)
const selectedIndexes = [1, 2];
for (const index of selectedIndexes) {
mergedPpt.Slides.Append({ slide: extraPpt.Slides.get_Item(index) });
}
const outputFileName = 'MergedSelectedSlides.pptx';
mergedPpt.SaveToFile({ file: outputFileName, fileFormat: pptModule.FileFormat.Pptx2013 });
// Release resources
mergedPpt.Dispose();
mainPpt.Dispose();
extraPpt.Dispose();
// Read the result file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Merge Selected PowerPoint Slides</h1>
<button onClick={mergeSelectedSlides}>
Merge Selected Slides
</button>
</div>
);
}
export default App;
Presentation after merging selected slides:

FAQ
Why is the slide order in the merged result different from the expected order
Cause: The append order follows the loop order, so files processed first come first. If the file list or loop order differs from the expected order, the page order in the result changes as well.
Solution: Arrange sourceFileNames in the required order and keep the Append loop running from the first slide to the last; the merged result then follows that order.
Why does the merged result contain an extra blank slide
Cause: The presentation created by new pptModule.Presentation() contains one blank slide by default. If the source slides are appended directly, that blank page stays at the beginning of the merged result.
Solution: Call Slides.RemoveAt(0) before the first append to remove the default page, for example:
const mergedPpt = new pptModule.Presentation();
mergedPpt.Slides.RemoveAt(0);
Are images, tables, and hyperlinks lost after merging
Cause: Slides.Append({ slide }) copies the whole slide, and images, tables, and hyperlinks are content of the slide itself, so they enter the merged result together with the slide.
Solution: No extra handling is needed. After merging, check the images and tables directly in the result file; if a hyperlink points to an external address, make sure that address is still reachable.
Get a Free License
Spire.Presentation for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.